Mostrando entradas con la etiqueta .gitignore. Mostrar todas las entradas
Mostrando entradas con la etiqueta .gitignore. Mostrar todas las entradas

domingo, 12 de febrero de 2023

10.- Django. Formulario de contacto y envío de email con datos. Variables de Entorno.

En este capitulo crearemos un ejemplo de formulario de contacto, veremos el método POST y aprovecharemos para mostrar como enviar un email que nos informe que hay un nuevo usuario y nos envíe la información introducida.

Empezamos.

Para crear el formulario de contacto lo primero que debemos hacer es irnos a las vistas, al archivo views.py y al final crear una nueva vista. De momento lo único que va a hacer es devolvernos un renderizado de un archivo, que aun no hemos creado, pero que lo haremos luego (contacto.html)

gestionPedidos/views.py

...
def contacto(request):
    '''Vista para definir un formulario de contacto.'''
    return render(request, "contacto.html")

El siguiente paso es irnos a las urls para registrar la vista.

tiendaVirtual/urls.py

...
from django.contrib import admin
from django.urls import path
# Siempre hay que importar las vistas de la aplicación
from gestionPedidos import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('busqueda_juegos/', views.busqueda_juegos),
    path('buscar/', views.buscar),
    path('contacto/', views.contacto),
]
y lo que nos falta es crear en la carpeta "templates", el archivo html del formulario que será el siguiente:

gestionPedidosl/templates/contacto.html

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Contáctanos</title>
</head>
<body>
    <h1>Contacta con nosotros.</h1>
    <form action="/contacto/" method="POST">
        {% csrf_token %}
        <!-- Cuadro de texto de entrada -->
        <p>Asunto <input type="text" name="asunto"> </p>
        <p>Email <input type="text" name="email"> </p>
        <p>Mensaje<p>
        <p></p><textarea name="mensaje" rows="15" cols="45"></textarea></p>
        <input type="submit" value="Enviar">
    </form>
</body>
</html>
Es importante dentro de la etiqueta del formulario añadir {% csrf_token %} para evitar un ataque malicioso llamado "Cross Site Request Forgery".  Permite validar que las peticiones son realizadas desde un sitio web autorizado y no desde otras fuentes. Si te interesa una mayor explicación puedes encontrar la información aquí.

Otro matiz importante es que cuando pulsemos el botón enviar usaremos como forma de envío el método "POST". La diferencia con el método "GET", que usamos en el capitulo anterior, es que mientras que este envía los datos usando la URL y por tanto los podemos ver su contenido en la barra de navegación, el método POST los envía de forma que no podamos verlos (en segundo plano y ocultos para el usuario). 

Luego definimos los elementos del formulario que tendrá la siguiente forma:


formulario de contacto


De momento el formulario no hace nada, pero podemos probarlo para ver que funciona correctamente.

Ahora, para comprobar que este formulario (que está utilizando el método "POST") funciona, vamos a hacer lo siguiente. Si al dar al botón enviar todo va bien, nos devolverá un renderizado indicándonos que la información se ha enviado correctamente. Volvemos al archivo de vistas views.py:

gestionPedidos/views.py

...
def contacto(request):
    '''Vista para definir un formulario de contacto.'''
    if request.method=="POST":
        return render(request, "gracias.html")
    else:
        return render(request, "contacto.html")

La explicación es la siguiente.

La primera vez que entramos en la página del formulario, no estamos utilizando el método "POST" sino el "GET" con lo que se renderizará el formulario de contacto. Ahora bien, cuando le damos al botón enviar, entonces la información se envía de nuevo a esta vista /contacto/ usando, ahora si, el método "POST", con lo que se renderizará la página html "gracias.html", que crearemos ahora y que nos servirá para confirmar el envío y que todo funciona correctamente.

gestionPedidos/templates/gracias.html

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Enviado</title>
</head>
<body>
    <h3>Gracias por enviar la información.</h3>
</body>
</html>

Si entramos en la url /contacto/ y enviamos el formulario nos debería salir el mensaje de Enviado. (siempre que tengamos el servidor conectado, claro)


archivo gracias.html


Envío de Emails en Django.


Enviar correos con Django es muy sencillo. Para enviar correos con Django es necesario tener un servidor local de protocolo simple de transferencia de correo (SMTP), o poder acceder a un servidor SMTP externo, como tu proveedor de servicios de correo electrónico (Gmail, Yahoo, Outlook etc)

Para ello, vamos a utilizar la librería core.mail. Para poder enviar mails lo primero que tenemos es ir al archivo settings.py y configurar una serie de parámetros. En este archivo, al final del todo pondremos las siguientes instrucciones.

tiendaVirtual/settings.py

...
# Configuración de servidor de correo de django
EMAIL_BACKEND="django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = 'smtp.outlook.com' o 'smtp.gmail.com' etc
EMAIL_PORT = 587
EMAIL_HOST_USER = 'usuario@outlook.com' o 'usuario@gmail.com'
EMAIL_HOST_PASSWORD = 'la contraseña del correo'
EMAIL_USE_TLS = True
El EMAIL_HOST es el servidor de correo que vas a usar para enviar los correos. Como gmail siempre me ha dado problemas, yo personalmente utilizo outlook. Utilizaremos el método smtp para enviar los correos. El resto es buscar la configuración asociada al correo que utilices y que puedes buscar en Google. El valor por defecto sino se especifica nada el "localhost"

EMAIL_PORT es el puerto por el que se comunica el servicio SMTP, que es el protocolo que se utiliza para enviar el correo. Por defecto el puerto es el 25.
 
EMAIL_HOST_USER es tu usuario o cuenta de correo.

EMAIL_HOST_PASSWORD es la contraseña de tu correo electrónico. Si estas usando Gmail como servidor SMTP desde que implemento la verificación en dos pasos y otras medidas de seguridad, no puedes usar tu contraseña del correo directamente. En vez de ello, Google te permite crear una contraseña especifica para la aplicación desde tu cuenta.  Para ello ve al navegador y abre la siguiente dirección, https://myaccount.google.com/. En el menú de la izquierda haz click en Seguridad, verás una pantalla como esta:

The Signing in to Google page for Google accounts

En "como inicias sesión en Google", selecciona "Verificación en dos pasos". En la parte inferior de la página selecciona "Contraseñas de aplicación". Ahí introduce un nombre que te ayude a recordar dónde vas a utilizar la contraseña de la aplicación. Después selecciona "Generar". Para introducir la contraseña de aplicación, sigue las instrucciones que aparecen en pantalla. La contraseña de aplicación es el código de 16 caracteres que se genera en tu dispositivo. 

Si no puedes usar un servidor SMTP (Gmail, Outlook, yahoo o cualquier otro servicio de correo), puedes decirle a Django que envíe estos emails a la consola del sistema remplazando la entrada EMAIL_BACKEND de archivo settings.py por esta otra:

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Al usar esta configuración, Django enviará los emails a la consola del sistema en lugar de enviarlos fuera de tu equipo. Este resulta bastante útil para probar la aplicación.

Para probar si la configuración funciona vamos a hacer una prueba desde consola. Nos vamos a la misma e introducimos el comando.

$ python manage.py shell

Lo primero es importar la librería core.mail y dentro de este la función sendmail(). Luego le pasamos los argumentos que nos pide. Puedes encontrar la documentación Django sobre como enviar email aqui. Quedaría algo como esto:

(miEntorno) chema@lenovo:~/Cursos/DJANGO/tiendaVirtual$ python manage.py shell
Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.core.mail import send_mail
>>> send_mail(
... 'Aqui el asunto del correo',
... 'Aqui el mensaje',
... 'tu_usuario@outlook.com',
... ['correo_destino@gmail.com'],
... fail_silently=False,
... )
1
Entramos en la consola interactiva de Django e importamos la librería que comentamos previamente.
Luego rellenamos los campo de asunto, mensaje etc. 'desde_correo@outlook.com' es el correo que registramos previamente en el archivo settings.py, es decir el correo que utilizamos para enviar los mensajes. 

La opción fail_silently=False final sirve para que si algo falla nos muestre las trazas del error y poder tener una idea de lo que ocurre. 
El 1 final es que el correo se envió correctamente.

Una vez que vemos que los parámetros son correctos y que nos llegan los correos electrónicos ¿Cómo hacemos para que desde nuestro formulario de contacto, lo que el usuario haya tecleado cuando le de al botón enviar nos llegue a nuestro correo electrónico?

Pues tenemos que ir al archivo de vistas y concretamente a la vista contacto. Allí tenemos que adaptar lo que hemos visto para enviar un email con los contenidos de los campos del formulario.

Lo primero empezaremos importando del módulo django.core.mail el método send_mail y también settings para poder usar las propiedades que definimos antes. Luego es hacer lo mismo que realizamos por consola. El archivo views.py quedaría tal que así.

 gestionPedidos/views.py
from django.shortcuts import render
from django.http import HttpResponse
# Para poder usar el modelo Articulos de la base de datos
from gestionPedidos.models import Articulos
# Para poder enviar emails del formulario de contacto.
from django.core.mail import send_mail
from django.conf import settings

...
def contacto(request):
    '''Vista para definir un formulario de contacto.'''
    if request.method=="POST":

        asunto = request.POST['asunto']
        mensaje = request.POST['mensaje'] + " " + request.POST['email']
        email_from = settings.EMAIL_HOST_USER

        send_mail(
            asunto,
            mensaje,
            email_from,
            ['correo_destino@correo.com'],
            )

        return render(request, "gracias.html")
    else:
        return render(request, "contacto.html")
Las variable asunto coge su valor del formulario, a través del método POST. En el formulario también llamamos asunto a la casilla de texto que recogía la información. Lo mismo ocurre con mensaje que coge su valor del mensaje que el usuario tecleo en el formulario y después de un espacio en blanco le añado el email para que cuando lo recibamos podamos contestarle si queremos.

email_from coge su valor del archivo de configuración y recordad que este correo, es la cuenta de correo que hemos configurado para enviar los archivos. Luego en una lista pondremos el correo o correo de destino a donde queremos que llegue la información de ese formulario.

Por ultimo si se ha enviado correctamente se renderizará el archivo "gracias.html".

Usar Variables de entorno para preservar datos privados.


Para preservar información importante y confidencial en nuestros proyectos de Django es útil usar las variables de entorno. De forma esquemática el proceso es el siguiente:

1.- Instalamos el siguiente paquete.

pip install django-environ

2.- Cuando este instalado, creamos un archivo llamado .env en el mismo directorio en donde este el archivo settings.py. Su contenido esta formado por parejas de clave-valor y es muy importante que no haya espacios ni antes ni después del igual ya que sino no funcionará. Por ejemplo, para usar como variables de entorno el EMAIL_HOST_USER y EMAIL_HOST_PASSWORD pondríamos lo siguiente:

tiendaVirtual/.env

EMAIL_HOST_USER=usuario@outlook.com
EMAIL_HOST_PASSWORD=#ladificilcontraseña
En el directorio base o raíz, añadimos al archivo .gitignore (o lo creamos si no lo está) lo siguiente:

*.pyc
__pycache__
db.sqlite3
/env
*.env
.vscode
Esto es para que GIT el controlador de versiones que normalmente se usa en los proyectos no haga un seguimiento de estos archivos, ni de sus valores.

Por último en el archivo settings.py en la línea de importación añadimos

tiendaVirtual/settings.py

import environ
env = environ.Env()
environ.Env.read_env()
y ya podemos usar los valores de entorno en el archivo de configuración de la siguiente forma:

tiendaVirtual/settings.py

...

# Configuración de servidor de correo de django
EMAIL_BACKEND="django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = 'smtp.outlook.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = env('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD')
EMAIL_USE_TLS = True
Ahora bien, si en el archivo de vistas de la aplicación queremos usar para algo los valores de estas variables tendremos que importarlos desde el archivo settings.py, ya que se encuentran ahí. Lo podemos hacer usando:

from django.conf import settings
Por eso en el apartado anterior al definir las vistas usamos "email_from = settings.EMAIL_HOST_USER"





domingo, 25 de septiembre de 2022

GIT - 1 - Borrar y Renombrar archivos - Revertir Cambios.

 

Borrar un archivo.


Para borrar un archivo del árbol de trabajo podríamos usar el comando "rm" sobre el mismo. Sin embargo aún tendríamos que usar git add [archivo_borrado] para que está modificación quedara registrada y luego ya podríamos hacer el commit.


chema@lenovo:~/proyecto$ rm main.py
chema@lenovo:~/proyecto$ git status
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	deleted:    main.py

no changes added to commit (use "git add" and/or "git commit -a")
chema@lenovo:~/proyecto$ git add main.py
chema@lenovo:~/proyecto$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    main.py

chema@lenovo:~/proyecto$ git commit -m "Pasos para borrar un archivo."
[master 5a2eea1] Pasos para borrar un archivo.
 1 file changed, 2 deletions(-)
 delete mode 100644 main.py

Sin embargo con;

$ git rm [archivos]

No solo borramos los archivos del árbol de trabajo sino que también lo preparamos para hacer el commit.

chema@lenovo:~/proyecto$ git rm main.py
rm 'main.py'
chema@lenovo:~/proyecto$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    main.py

chema@lenovo:~/proyecto$ git commit -m "borrado de un archivo."
[master e86a317] borrado de un archivo.
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 main.py

Mover o Renombrar un Archivo.

Lo mismo ocurre si queremos mover un archivo de un directorio a otro dentro del árbol de trabajo. Para ahorrar tener que confirmar la modificación con git add, directamente tecleamos:

$ git mv [archivo] [nuevo_nombre]

y así podemos mover los archivo entre directorios o bien también lo podemos usar para cambiar el nombre de los archivos y tener estas modificaciones registradas y listas para hacer el commit.

mover un archivo con git

chema@lenovo:~/proyecto$ git status
On branch master
nothing to commit, working tree clean
chema@lenovo:~/proyecto$ git mv main.py ./MOVER/
chema@lenovo:~/proyecto$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    main.py -> MOVER/main.py

Ignorar Cambios.


Git ve cada archivo en el árbol de trabajo como una de estas tres cosas:

- Rastreado. Un archivo que se ha preparado o confirmado previamente.
- Sin seguimiento. Un archivo que no se ha preparado ni confirmado.
- Ignorado. Un archivo que se le ha dicho explícitamente a git que ignore cualquier cambio que se produzca.

Esos archivos o directorios son aquellos que no suelen formar parte del proyecto tales como archivos de compilación o archivos temporales generados por el ordenador. 

Los archivos a ignorar se buscan en un archivo especial llamado .gitignore, un archivo oculto que hay que crear en la raíz del repositorio. Este archivo debe crearse y editarse a mano cuando tengas nuevos archivos que quieras ignorar. Los archivos .gitignore contienen patrones que se comparan con los nombres de los archivos de los repositorios para determinar si deben ignorarse o no.

Se pueden usar patrones globales dentro del archivo para ampliar los casos en los que determinados archivos no deban ser incluidos. Puedes ver una buena explicación del uso de los mismos en esta página.

Ejemplo de archivo .gitignore

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

El propio archivo .gitignore necesita ser comprometido "commit" al igual que el resto.


¿Cómo evitar el rastreo de archivos a los que previamente les has hecho "commit" ?


$ git rm --cached nombre-del-archivo

Establece un archivo como "untracked file", sin seguimiento.

Saca los archivos de nuestro repositorio local y del área de staging, pero los mantiene en el disco duro, no los borra. Básicamente le dice a Git que deje de trackear el historial de cambios de estos archivos, por lo que pasaran a un estado untracked o Sin seguimiento. Si no queremos que git les vuelva a hacer seguimiento los añadiríamos al archivo .gitignore.


Revertir Cambios.

Tenemos que distinguir entre dos posibles escenarios.

A) Revertir cambios antes de que los archivos pasen al Stage.

B) Revertir cambios cuando los archivos ya están en el Stage.


- Revertir cambios antes del Stage.

$ git checkout [archivos]

Revierte cambios en archivos antes de que sean confirmados, antes de que se haga un commit nuevo y  siempre que no los hayamos añadido al Stage, usando un git add. 

Se vuelve a la versión del último commit o confirmación realizada. Es decir, descartamos las modificaciones que hayamos hecho en el archivo y lo devolvemos a como estaba anteriormente, en la confirmación o commit previo.

Veámoslo con un ejemplo.

Imaginemos que iniciamos un repositorio, creamos un archivo y dentro escribimos un comentario. Después lo pasamos al stage y realizamos un commit.

chema@lenovo:~/proyecto$ git init
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint: 	git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint: 	git branch -m <name>
Initialized empty Git repository in /home/chema/proyecto/.git/
chema@lenovo:~/proyecto$ echo "# Primera linea del archivo" > main.py
chema@lenovo:~/proyecto$ git add main.py 
chema@lenovo:~/proyecto$ git commit -m "primer commit"
[master (root-commit) ef8ab09] primer commit
 1 file changed, 1 insertion(+)
 create mode 100644 main.py

Ahora, imaginemos que añadimos más código al archivo. Como es un ejemplo para mostrar como funciona el comando, solamente voy a añadir dos líneas más, pero funciona igual si has tecleado miles de líneas de código.

chema@lenovo:~/proyecto$ echo "#Segunda linea añadida" >> main.py 
chema@lenovo:~/proyecto$ echo "#Tercera linea añadida" >> main.py 
chema@lenovo:~/proyecto$ cat main.py 
# Primera linea del archivo
#Segunda linea añadida
#Tercera linea añadida

Ahora me doy cuenta de que en el archivo hay un error y no recuerdo como estaba originalmente cuando funcionaba. Lo que necesito es deshacer lo que he hecho desde la última vez que guarde los cambios. 

Lo único que hay que hacer es usar, el comando $ git checkout main.py

chema@lenovo:~/proyecto$ git checkout main.py
Updated 1 path from the index
chema@lenovo:~/proyecto$ cat main.py
# Primera linea del archivo
Y volvemos al estado original.

Otro ejemplo. Supongamos que tenemos un programa con un montón de archivos y directorios. Como todo funciona bien hacemos un commit del repositorio y seguimos programando. Cuando llevamos algunas líneas de código lo volvemos a probar y algo no va. No sabemos donde puede estar el bug pero si sabemos que la ultima versión que teníamos funcionaba. Decidimos empezar otra vez y volvemos a la anterior versión que era estable. Para ello usamos:

$ git checkout .
(no se si se ve pero al final hay un punto.)

Al escribir este comando de esta forma le decimos que vuelva a la última versión, a la versión previa.
En git esto es volver al commit previo.

- Revertir los cambios cuando ya hemos añadido esos archivos al Stage.


$ git reset HEAD <archivos>

Así como el comando anterior nos permitía revertir cambios a archivos modificados antes de que pasaran al Stage, este comando nos permite deshacer cambios que ya estén en el Stage bien porque ya hayamos hecho, por ejemplo un git add . o un git add *.

Es decir lo que nos permite es pasar archivos, que ya estaban en el Stage preparados para hacer un commit, al árbol de trabajo de nuevo. En definitiva sacar esos archivos del Stage. 

Veámoslo con un ejemplo.

Partimos del ejemplo anterior y le añadimos un cuarto comentario al archivo. Después, aunque solamente tenemos un archivo, lo añadimos al stage. Entonces nos damos cuenta de que queremos cambiar algo antes de hacer el commit con lo que usamos este comando para devolver el archivo, al árbol de trabajo.

chema@lenovo:~/proyecto$ echo "Cuarta linea añadida" >> main.py
chema@lenovo:~/proyecto$ git add *
chema@lenovo:~/proyecto$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   main.py

chema@lenovo:~/proyecto$ git reset HEAD main.py
Unstaged changes after reset:
M	main.py
chema@lenovo:~/proyecto$ cat main.py
# Primera linea del archivo
Cuarta linea añadida
chema@lenovo:~/proyecto$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   main.py

no changes added to commit (use "git add" and/or "git commit -a")

Que quede claro que no se modifica el archivo, simplemente lo saca del Stage y lo devuelve al árbol de trabajo para que podamos trabajar con el. (Aunque si coges ese archivo y lo modificas antes de hacer un commit también lo sacarás del Stage porque habrás hecho una modificación que no has confirmado)

Comandos equivalentes son:

$ git restore --staged <archivos>

$ git reset -p      En este caso nos pedirá confirmación paso a paso de las acciones a realizar.


- Revertir los cambios si ya hemos hecho un commit. 


Si ya hemos realizado una instantánea del repositorio al realizar un commit y queremos anularlo, podemos hacerlo de diversas formas dependiendo de lo que queramos conseguir.

A) Cambiando el último commit:


$ git commit --amend 

Es la forma conveniente de modificar la confirmación más reciente. Sobrescribe el commit previo, es decir, se añade lo que ya tengamos en esa instantánea con lo que tengamos actualmente en el Stage. Si no hemos añadido o modificado nada, se puede utilizar simplemente para editar el mensaje de confirmación anterior sin cambiar su instantánea.

SOLAMENTE USALO EN COMMITS LOCALES porque borra el historial del último commit que en proyectos en grupo podría haber realizado otra persona. Las confirmaciones modificadas son en realidad confirmaciones completamente nuevas y la confirmación anterior ya no aparecerá.

Por ejemplo. Digamos que acabamos de realizar una confirmación (commit) y cometimos un error en el mensaje de confirmación. Ejecutar este comando cuando no hay nada todavía nuevo preparado, que esté en el Stage, nos permite modificar el anterior mensaje de confirmación anterior sin alterar su instantánea.

chema@lenovo:~/proyecto$ git log
commit 2f08687ba8e43dbd7d40d0405108a841c97c69a9 (HEAD -> master)
Author: usuario <usuario@correo.es>
Date:   Thu Sep 22 19:33:06 2022 +0200

    primer commit. Contiene un error.
chema@lenovo:~/proyecto$ git commit --amend -m "primer commit. Error Subsanado."
[master c5ecfed] primer commit. Error Subsanado.
 Date: Thu Sep 22 19:33:06 2022 +0200
 1 file changed, 1 insertion(+)
 create mode 100644 main.py
chema@lenovo:~/proyecto$ git log
commit c5ecfed30cff8625efd54e1ea136906ef2cca50d (HEAD -> master)
Author: usuario <usuario@correo.es>
Date:   Thu Sep 22 19:33:06 2022 +0200

    primer commit. Error Subsanado.

Si te fijas es cierto que hemos modificado el mensaje de la confirmación, pero si miras bien verás que el número del commit (en negrita) también es distinto.


Otro ejemplo habitual. Digamos que hemos editado algunos archivos que nos gustaría confirmar en una sola instantánea, pero luego nos damos cuenta de que se nos ha olvidado añadir uno de los archivos la primera vez. Bastará con prepara el nuevo archivo, añadirlo al Stage y usar este comando.

chema@lenovo:~/proyecto$ git log
commit 2ebf529db3565c2c9a26f9fa65ef5ffcc6fc3d5d (HEAD -> master)
Author: usuario <usuario@correo.es>
Date:   Fri Sep 23 17:33:07 2022 +0200

    primer commit
chema@lenovo:~/proyecto$ > archivo_añadir
chema@lenovo:~/proyecto$ git add archivo_añadir 
chema@lenovo:~/proyecto$ git commit --amend -m "Primer commit con archivo olvidado añadido"
chema@lenovo:~/proyecto$ git show
commit 1b9aa03962422e4cf73c5496ec66557901c81e8f (HEAD -> master)
Author: usuario <usuario@correo.es>
Date:   Fri Sep 23 17:33:07 2022 +0200

    primer commit. Con archivo olvidado añadido


B) Revertir un commit por completo. (ROLL BACK)


$ git revert HEAD

Crea un nuevo commit que es justo la inversa del último, con lo que volvemos al estado del anterior. Sin embargo esta acción no sobrescribe el commit previo ni lo elimina del historial

* Estado del archivo en la última instantánea.
chema@lenovo:~/proyecto$ cat main.py 
# Primera linea del archivo
#!/usr/bin/env python3
import math

* Deshacemos los cambios
chema@lenovo:~/proyecto$ git revert HEAD
[master 7d7e430] Revert "muevo commit"
 1 file changed, 2 deletions(-)

chema@lenovo:~/proyecto$ cat main.py 
# Primera linea del archivo

* El último commit es el inverso del anterior.
chema@lenovo:~/proyecto$ git log
commit 7d7e43071d1b48ea76ba6f0ab99e0e1732e688ea (HEAD -> master)
Author: usuario <usuario@correo.es>
Date:   Fri Sep 23 19:53:05 2022 +0200

    Revert "nuevo commit"
    
    This reverts commit 7b0418f9029abea87808f019f0683bc6b70ec709.

commit 7b0418f9029abea87808f019f0683bc6b70ec709
Author: usuario <usuario@correo.es>
Date:   Fri Sep 23 19:51:59 2022 +0200

    muevo commit

C) Revertir un commit por completo que no sea el último.


El Id es el código alfanumérico muy largo que aparece al lado de la palabra commit. Lo calcula el programa con el algoritmo SHA1. Sirve para garantizar que la integridad del commit y que ese commit es único.

En el ejemplo anterior el ID del primer commit es 7b0418f9029abea87808f019f0683bc6b70ec709

$ git revert <ID del commit>

Este comando desharía los cambios de esa instantánea en concreto, pero ojo que esto nos puede dar dolores de cabeza el revertir cambios anteriores que ya estaban consolidados.

Borrar todos los commits posteriores a uno previo.


$ git reset --hard <id o sha del commit>

Se BORRAN PARA SIEMPRE todos los commits más nuevos y se recupera el repositorio al estado del que hemos ido.

Movernos entre distintos commits.

En ocasiones nos interesa movernos atrás en el tiempo para ver como estaba un repositorio en un momento concreto. 

Considera este repositorio con 4 confirmaciones.

chema@lenovo:~/Prueba$ git log --oneline
0face9c (HEAD -> main) Añadido archivo de instrucciones
e624744 añadido nuevo comando a main.py
ad3b242 Segundo commit.
5011475 Primer commit del repositorio.

Si quisiera volver, a ver como estaba el proyecto en el segundo repositorio (ad3b242) se puede usar:

$ git checkout <id del commit>

git checkout ad3b242
Note: switching to 'ad3b242'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at ad3b242 Segundo commit.
chema@lenovo:~/Prueba$ ls
main.py

Pero OJOOOO, que si realizas alguna modificación y la confirmas no volverás a la rama main o master que es en la que estas trabajando, si no que crearemos una nueva rama. Aunque el concepto de ramas lo veremos en los siguientes capítulos voy a hacer una modificación al repositorio y un commit para ver lo que ocurre.

Luego, si le pregunto a git cuantas ramas tengo y donde estoy, veo lo siguiente:

chema@lenovo:~/Prueba$ git branch
* (HEAD detached from ad3b242)
  main
 
El asterisco indica en donde estoy. Para volver a donde estaba trabajando, tengo que desplazarme al último commit de mi rama principal que en mi caso se llama main (también puede aparecerte como master)

$ git checkout <rama a donde nos queremos mover>

chema@lenovo:~/Prueba$ git checkout main
Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  327aad6 prueba de checkout

If you want to keep it by creating a new branch, this may be a good time
to do so with:

 git branch <new-branch-name> 327aad6

Switched to branch 'main'
chema@lenovo:~/Prueba$ git log --oneline 
* 0face9c (HEAD -> main) Añadido archivo de instrucciones
* e624744 añadido nuevo comando a main.py
* ad3b242 Segundo commit.
* 5011475 Primer commit del repositorio.
 

y volvemos a donde estabamos.