Git & Tricks – Pillole di source code management | Parte 2: gestire i commit con empatia

0

Dopo la prima puntata dedicata all’introduzione di Git ed alla preparazione dell’ambiente operativo, ecco un’altra serie di pillole dedicate questa volta alla gestione dei commit, vera linfa vitale del sistema di gestione dei codici sorgente creato da Linus Torvalds.

In cosa consiste un commit

Anche se nello scorso articolo, durante l’inizializzazione del repository di test, erano già stati effettuati alcuni commit, l’aver dato per scontato quella nozione impone di approfondirla ora, in modo da fugare il campo da ogni dubbio.

Un commit in Git è una singola unità di lavoro che include le modifiche effettuate all’interno di un repository, accompagnate da un messaggio che descrive quali queste siano state.

La sequenza di operazioni che registrano un commit è la seguente:

/git # echo "La mia sesta modifica" > Sesto.txt

/git # git add Sesto.txt 

/git # git commit -m "Sesta modifica al repository" -m "Questa la descrizione estesa del sesto commit."
[main cbf639b5b4ba] Sesta modifica al repository
 1 file changed, 1 insertion(+)
 create mode 100644 Sesto.txt

L’operazione consente di registrare la variazione che è stata apportata nel repository all’interno della cartella nascosta presente nel repository stesso il cui nome è .git. Qui verranno tracciati tutti i commit effettuati. La sua struttura è tipicamente la seguente:

/git # tree .git
.git
├── COMMIT_EDITMSG
├── HEAD
├── branches
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-merge-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   ├── push-to-checkout.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── main
├── objects
│   ├── 08
│   │   └── 12762b5167e9156a69cd71f8a55d556b555918
│   ├── 09
│   │   └── c257cf5ad206ab9823fa270fd408af6e05eb34
│   ├── 21
│   │   └── 316085bb152c6fdf7ce6fcb3f81a020a2e0eef
│   ├── 4a
│   │   └── 5a4334d66c1055937d2ad854cca6079a0ab82f
│   ├── 51
│   │   └── 6671c1aaaa21f92c45c176389b5e92c6c821ac
│   ├── 52
│   │   └── 8700156d4f6503c1d760c5e26ba0f13599af32
│   ├── 8f
│   │   ├── bf52479a1fd9b0af9277b3422d09ab3b5ac58b
│   │   └── f2518efd016a572615e662852c58012d191867
│   ├── 93
│   │   └── 1867c72d0cd29739e8ea485b64b4e34e37c2e1
│   ├── ad
│   │   └── 67ccb1502139fc7d53832e87d9b142bbf34143
│   ├── b0
│   │   └── 634e7ad5a7531ff000da68b62d6810bad0fc7a
│   ├── bb
│   │   └── d8cdc095d0d7ecc037f14f54600d84fd9a93b0
│   ├── be
│   │   └── 4138b8e24937a9248b1008e45dd62693234cf6
│   ├── c8
│   │   └── 4a422b268b957ef15723557288f7573ccf172a
│   ├── cb
│   │   └── f639b5b4ba165265905ef97b801ed48ad1c403
│   ├── dd
│   │   └── dabb104b88b01f16315e9cd7c621764a7298f2
│   ├── e2
│   │   └── 6b193e9904d0d5585bcd6444b65e09cae32cec
│   ├── fa
│   │   └── 5aacf8427670722a59128da1c1cc3021462408
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── main
    └── tags

29 directories, 39 files

Ad un occhio attento non sfuggirà il fatto che i nomi delle cartelle al di sotto della cartella objects corrispondono, parzialmente, agli hash dei commit:

/git # git lg
* cbf639b5b4ba - (2023-11-20 14:52:41 +0000)  (HEAD -> main) Sesta modifica al repository <Raoul Scarazzini>
* be4138b8e249 - (2023-09-29 16:33:57 +0000)  Quinto commit <Raoul Scarazzini>
* 21316085bb15 - (2023-09-29 16:33:57 +0000)  Quarto commit <Raoul Scarazzini>
* 0812762b5167 - (2023-09-29 16:33:57 +0000)  Terzo commit <Raoul Scarazzini>
* c84a422b268b - (2023-09-29 16:33:57 +0000)  Secondo commit <Raoul Scarazzini>
* b0634e7ad5a7 - (2023-09-29 16:33:57 +0000)  Primo commit <Raoul Scarazzini>

Il contenuto effettivo di questi file è visionabile utilizzando il comando git cat-file che si aspetta un parametro -p contenente appunto l’hash del commit. Grazie a questo sarà possibile verificare come i file al di sotto della cartella objects contengano tutte le informazioni del repository.

Facendo un esempio pratico delle corrispondenze, si prenda il secondo commit del repository il cui hash è c84a422b268b. Il file corrispondente sarà quindi quello presente nel path objects/c8/4a422b268b957ef15723557288f7573ccf172a.

In questo file è presente la descrizione del commit, oltre che il commit precedente (parent b0634e7ad5a7531ff000da68b62d6810bad0fc7a) ed il ramo che rappresenta lo stato della cartella del repository quando questo commit è stato fatto (tree bbd8cdc095d0d7ecc037f14f54600d84fd9a93b0):

/git # git cat-file -p c84a422b268b957ef15723557288f7573ccf172a
tree bbd8cdc095d0d7ecc037f14f54600d84fd9a93b0
parent b0634e7ad5a7531ff000da68b62d6810bad0fc7a
author Raoul Scarazzini <rasca@mmul.it> 1696005237 +0000
committer Raoul Scarazzini <rasca@mmul.it> 1696005237 +0000

Secondo commit

Questa la descrizione estesa del Secondo commit.

L’hash relativo a tree consentirà di osservare la struttura del repository:

/git # git cat-file -p  bbd8cdc095d0d7ecc037f14f54600d84fd9a93b0
100644 blob 528700156d4f6503c1d760c5e26ba0f13599af32	Primo.txt
100644 blob 931867c72d0cd29739e8ea485b64b4e34e37c2e1	Secondo.txt

Insieme al contenuto dei file che ne fanno parte:

/git # git cat-file -p 528700156d4f6503c1d760c5e26ba0f13599af32
Contenuto del Primo file.

/git # git cat-file -p 931867c72d0cd29739e8ea485b64b4e34e37c2e1
Contenuto del Secondo file.

Buone regole per i propri commit

Creazione di un branch dedicato

Quando si prevede di lavorare all’interno di un repository condiviso per apportare una o più variazioni è buona norma creare dei branch. I branch in Git consentono ai membri di un team di lavorare su diverse funzionalità in modo separato, evitando conflitti diretti, migliorando la gestione delle versioni e permettendo il controllo di qualità prima di integrare le modifiche nella branch principale.

Ecco quindi come creare un branch:

/git # git branch mio-contributo

/git # git status
On branch main
nothing to commit, working tree clean

/git # git checkout mio-contributo
Switched to branch 'mio-contributo'

O, in forma più immediata:

/git # git checkout -b mio-contributo
Switched to a new branch 'mio-contributo'

A questo punto le modifiche potranno essere apportate in un ambiente isolato che verrà integrato solo mediante un ulteriore processo manuale.

Modifiche chiare, atomiche e non ripetitive

Git è nato per consentire a più persone di lavorare sullo stesso codice, pertanto vien da sé come ragionare in termini empatici nel momento in cui si sviluppa possa essere più che una buona idea.

Anche se sembra molto filosofico e poco tecnico l’aspetto dell’empatia, ossia mettersi nei panni di chi dovrà lavorare successivamente sui propri sviluppi, risulta essere cruciale nel lungo termine.

Piuttosto che generare commit immensi che modificano decine se non centinaia di righe in una sola volta, è sempre bene produrre commit sintetici che siano cioè il più possibile comprensibili in merito al loro scopo. Generare quindi commit che siano il più possibile atomici.

Allo stesso tempo, nel momento in cui ci si accorge di aver dimenticato delle parti nel proprio commit, oppure aver commesso dei typo, oppure di fronte all’esigenza di modificare solamente il titolo o la descrizione estesa, va sempre sottolineato come sia inutile, confusionario e dannoso pensare di generare tanti commit dal titolo identico o che si differenziano per un numero.

Si pensi a questa lista di commit mettendosi nei panni di chi dovrà leggerla, magari l’autore stesso, dopo 2 mesi:

  • Mia nuova modifica, l’ultima giuro
  • Mia nuova modifica #3
  • Mia nuova modifica #2
  • Mia nuova modifica #1
  • Mia nuova modifica
  • Mia nuova modifica

Ciascun commit magari avrà una solamente una virgola diversa dal precedente. Davvero una cattiva abitudine.

Evitare queste situazioni e guadagnare in empatia è semplice, poiché esiste un alleato fondamentale, che si chiama Amend.

Utilizzo di amend

A fronte di un errore o di una dimenticanza sarà possibile “ritornare sui propri passi” andando ad effettuare le correzioni del caso, aggiungendo i nuovi file modificati mediante git add ed infine sovrascrivendo il commit di testa con le variazioni, mediante il comando git commit --amend.

Ecco un esempio pratico, in cui un nuovo commit viene creato per poi essere corretto:

/git # git lg
* cbf639b5b4ba - (2023-11-20 14:52:41 +0000)  (HEAD -> main) Sesta modifica al repository <Raoul Scarazzini>
* be4138b8e249 - (2023-09-29 16:33:57 +0000)  Quinto commit <Raoul Scarazzini>
* 21316085bb15 - (2023-09-29 16:33:57 +0000)  Quarto commit <Raoul Scarazzini>
* 0812762b5167 - (2023-09-29 16:33:57 +0000)  Terzo commit <Raoul Scarazzini>
* c84a422b268b - (2023-09-29 16:33:57 +0000)  Secondo commit <Raoul Scarazzini>
* b0634e7ad5a7 - (2023-09-29 16:33:57 +0000)  Primo commit <Raoul Scarazzini>

/git # echo "Settimo commit con typo" > Settimo.txt

/git # git add Settimo.txt 

/git # git commit -m "Settimo commit con typo" -m "Descrizione estesa e con typo del Settimo commit."
[mio-contributo 44529906f938] Settimo commit con typo
 1 file changed, 1 insertion(+)
 create mode 100644 Settimo.txt

/git # git lg
* 44529906f938 - (2023-11-20 16:13:27 +0000)  (HEAD -> mio-contributo) Settimo commit con typo <Raoul Scarazzini>
* 09d1099992ef - (2023-11-20 16:10:46 +0000)  Sesta modifica al repository <Raoul Scarazzini>
* be4138b8e249 - (2023-09-29 16:33:57 +0000)  Quinto commit <Raoul Scarazzini>
* 21316085bb15 - (2023-09-29 16:33:57 +0000)  Quarto commit <Raoul Scarazzini>
* 0812762b5167 - (2023-09-29 16:33:57 +0000)  Terzo commit <Raoul Scarazzini>
* c84a422b268b - (2023-09-29 16:33:57 +0000)  Secondo commit <Raoul Scarazzini>
* b0634e7ad5a7 - (2023-09-29 16:33:57 +0000)  Primo commit <Raoul Scarazzini>

L’ultimo commit è il 44529906f938, che nel dettaglio apporta queste modifiche:

/git # git show 44529906f938
commit 44529906f938305eff953d05a6756458c02ab827 (HEAD -> mio-contributo)
Author: Raoul Scarazzini <rasca@mmul.it>
Date:   Mon Nov 20 16:13:27 2023 +0000

    Settimo commit con typo
    
    Descrizione estesa e con typo del Settimo commit.

diff --git a/Settimo.txt b/Settimo.txt
new file mode 100644
index 000000000000..139bc9e75a8d
--- /dev/null
+++ b/Settimo.txt
@@ -0,0 +1 @@
+Settimo commit con typo

L’idea è quella di cambiare il contenuto di Settimo.txt, facendo risultare la modifica in modo che sia l’ultima effettuata, e vada quindi a sostituire quella precedente, ossia il commit 44529906f938.

Questo è possibile effettuando la modifica ed utilizzando poi l’opzione --amend di git commit, in questo modo:

/git # echo "Settimo commit" > Settimo.txt

/git # git status
On branch mio-contributo
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:   Settimo.txt

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

/git # git add Settimo.txt

/git # git commit --amend

Il comando innescherà l’apertura dell’editor di testo (generalmente vim) presentando quanto segue:

Settimo commit con typo

Descrizione estesa e con typo del Settimo commit.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Mon Nov 20 16:13:27 2023 +0000
#
# On branch mio-contributo
# Changes to be committed:
#       new file:   Settimo.txt
#

E qui potranno essere apportate ulteriori variazioni tanto al titolo del commit, quanto alla descrizione estesa. La stessa cosa che nella linea di comando viene dichiarata mediante git commit -m 'Titolo' -m 'Descrizione estesa'.

Una volta modificate le descrizioni e salvato il contenuto (in vim si userà :wq) il nuovo stato dei commit sarà il seguente:

/git # git commit --amend
[mio-contributo 9d49e2580e5a] Settimo commit
 Date: Mon Nov 20 16:13:27 2023 +0000
 1 file changed, 1 insertion(+)
 create mode 100644 Settimo.txt

/git # git lg
* 9d49e2580e5a - (2023-11-20 16:17:35 +0000)  (HEAD -> mio-contributo) Settimo commit <Raoul Scarazzini>
* 09d1099992ef - (2023-11-20 16:10:46 +0000)  Sesta modifica al repository <Raoul Scarazzini>
* be4138b8e249 - (2023-09-29 16:33:57 +0000)  Quinto commit <Raoul Scarazzini>
* 21316085bb15 - (2023-09-29 16:33:57 +0000)  Quarto commit <Raoul Scarazzini>
* 0812762b5167 - (2023-09-29 16:33:57 +0000)  Terzo commit <Raoul Scarazzini>
* c84a422b268b - (2023-09-29 16:33:57 +0000)  Secondo commit <Raoul Scarazzini>
* b0634e7ad5a7 - (2023-09-29 16:33:57 +0000)  Primo commit <Raoul Scarazzini>

/git # git show 9d49e2580e5a
commit 9d49e2580e5a6f7b3a0a1c5dd7ef9fb81416425b (HEAD -> mio-contributo)
Author: Raoul Scarazzini <rasca@mmul.it>
Date:   Mon Nov 20 16:13:27 2023 +0000

    Settimo commit
    
    Descrizione estesa del Settimo commit.

diff --git a/Settimo.txt b/Settimo.txt
new file mode 100644
index 000000000000..c24683b810bb
--- /dev/null
+++ b/Settimo.txt
@@ -0,0 +1 @@
+Settimo commit

A balzare all’occhio è il nuovo hash dell’ultimo commit in ordine di tempo, ora 9d49e2580e5a, ma vale la pena far notare come il vecchio, 44529906f938, per quanto non appaia in elenco rimanga comunque accessibile e sia presente nella cartella .git:

/git # git show 44529906f938
commit 44529906f938305eff953d05a6756458c02ab827
Author: Raoul Scarazzini <rasca@mmul.it>
Date:   Mon Nov 20 16:13:27 2023 +0000

    Settimo commit con typo
    
    Descrizione estesa e con typo del Settimo commit.

diff --git a/Settimo.txt b/Settimo.txt
new file mode 100644
index 000000000000..139bc9e75a8d
--- /dev/null
+++ b/Settimo.txt
@@ -0,0 +1 @@
+Settimo commit con typo

Questo è un altro aspetto da tenere a mente per quanto riguarda Git: nulla, per fortuna, viene dimenticato.

Conclusioni

Nelle prossime puntate della serie vedremo come effettuare il merge del proprio commit nel branch main, come editare massivamente i commit utilizzando il rebase interattivo e come sfruttare il rebase classico per tenere aggiornato il proprio branch mentre lo si sta modificando.

Ma non finisce qui, perché la creazione di branch ordinate che permettano di formulare liste di modifiche chiare e comprensibili passa anche da altri utilizzi di Git, uno su tutti il cherry-pick, una funzionalità apparentemente oscura, ma indispensabile agli occhi di quanti la padroneggiano.

La serie sino ad ora

Git & Tricks – Pillole di source code management | Parte 1: un ambiente confortevole

Git & Tricks – Pillole di source code management | Parte 2: gestire i commit con empatia

Git & Tricks – Pillole di source code management | Parte 3: l’importanza del rebase per un mondo migliore

Da sempre appassionato del mondo open-source e di Linux nel 2009 ho fondato il portale Mia Mamma Usa Linux! per condividere articoli, notizie ed in generale tutto quello che riguarda il mondo del pinguino, con particolare attenzione alle tematiche di interoperabilità, HA e cloud.
E, sì, mia mamma usa Linux dal 2009.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *