Fork e upstream di Git: istruzioni e un suggerimento interessante

Il fork dei progetti per apportare le tue modifiche ti consentono di integrare facilmente i tuoi contributi. Tuttavia, se non stai inviando di nuovo quelle modifiche a monte, il che significa rispedirle al repository principale, rischi di perderne traccia, il che può causare righe divergenti nel tuo repository. Per assicurarti che tutti i collaboratori attingano alla stessa posizione, devi conoscere alcuni principi su come il forking git interagisce con git upstream. In questo blog, ti presenterò le nozioni di base, alcuni trucchi e ti lascerò anche un suggerimento interessante per portarti un passo avanti a tutti.

Git upstream: tieniti aggiornato e contribuisci

Vorrei iniziare descrivendo in dettaglio una configurazione comune e il flusso di lavoro più semplice per interagire con i repository upstream.

In una configurazione standard, generalmente hai un branch remoto di origine e un branch remoto upstream; quest'ultimo è il gatekeeper o l'origine di riferimento a cui vuoi contribuire.

Innanzitutto, verifica di aver già configurato un remoto per il repository upstream e, si spera, anche un'origine:

git remote -v

origin  git@bitbucket.org:my-user/some-project.git (fetch)
origin  git@bitbucket.org:my-user/some-project.git (push)

Se non hai un upstream, puoi aggiungerlo facilmente con il comando remote:

git remote add upstream git@bitbucket.org:some-gatekeeper-maintainer/some-project.git

Verifica che il remoto sia aggiunto correttamente:

git remote -v

origin    git@bitbucket.org:my-user/some-project.git (fetch)
origin    git@bitbucket.org:my-user/some-project.git (push)
upstream  git@bitbucket.org:some-gatekeeper-maintainer/some-project.git (fetch)
upstream  git@bitbucket.org:some-gatekeeper-maintainer/some-project.git (push)

Ora puoi raccogliere le ultime modifiche del repository upstream con fetch. Ripeti l'operazione ogni volta che vuoi ricevere aggiornamenti:

Se il progetto ha tag che non sono stati uniti a main, dovresti utilizzare anche: git fetch upstream --tags.

git fetch upstream

In genere, vorrai mantenere il branch main locale come equivalente prossimo di quello mainupstream ed eseguire qualsiasi lavoro nei branch di funzionalità, poiché potrebbero in seguito diventare richieste pull.

A questo punto, non importa se usi merge o rebase, poiché il risultato sarà in genere lo stesso. Usiamo merge:

git checkout main
git merge upstream/main

Quando vuoi condividere un po' di lavoro con i manutentori upstream del tuo branch main, crea un branch di funzionalità. Quando sei soddisfatto, invialo al tuo repository remoto.

Puoi anche usare rebase e quindi merge per assicurarti che l'upstream abbia un set pulito di commit (idealmente uno) da valutare:

git checkout -b feature-x

#some work and some commits happen
#some time passes

git fetch upstream
git rebase upstream/main

Pubblicare con git fork

Dopo i passaggi precedenti, pubblica il tuo lavoro nel fork remoto con un semplice push:

git push origin feature-x
git push -f origin feature-x

Personalmente preferisco mantenere la cronologia il più pulita possibile e optare per l'opzione tre, ma team diversi hanno flussi di lavoro diversi. Nota: fallo solo quando lavori con il tuo fork. Riscrivere la cronologia dei repository e dei branch condivisi è qualcosa che non dovresti MAI fare.

Suggerimento del giorno: numeri di anticipi/ritardi nel prompt

Dopo un'operazione fetch, git status ti mostra quanti commit sono in anticipo o in ritardo rispetto al branch remoto sincronizzato. Non sarebbe bello se potessi vedere queste informazioni nel tuo fedele prompt dei comandi? Lo pensavo anch'io, così ho iniziato a usare bash per riuscirci.

Ecco come apparirà sul tuo prompt una volta configurato:

nick-macbook-air:~/dev/projects/stash[1|94]$

E questo è ciò che devi aggiungere al tuo .bashrc o equivalente, solo una singola funzione:

function ahead_behind {
    curr_branch=$(git rev-parse --abbrev-ref HEAD);
    curr_remote=$(git config branch.$curr_branch.remote);
    curr_merge_branch=$(git config branch.$curr_branch.merge | cut -d / -f 3);
    git rev-list --left-right --count $curr_branch...$curr_remote/$curr_merge_branch | tr -s '\t' '|';
}
export PS1="\h:\w[\$(ahead_behind)]$"

Funzionamento interno

Per chi ama i dettagli e le spiegazioni ecco il funzionamento effettivo:

Otteniamo il nome simbolico dell'attuale HEAD, ovvero il branch attuale:

curr_branch=$(git rev-parse --abbrev-ref HEAD);

Otteniamo il remoto a cui punta il branch attuale:

curr_remote=$(git config branch.$curr_branch.remote);

Otteniamo il branch in cui questo remoto dovrebbe essere sottoposto a merge (con un trucco Unix comodo per scartare tutto fino all'ultima barra [/] inclusa):

curr_merge_branch=$(git config branch.$curr_branch.merge | cut -d / -f 3);

Ora abbiamo ciò di cui abbiamo bisogno per raccogliere il numero di conteggi per i commit in anticipo o in ritardo:

git rev-list --left-right --count $curr_branch...$curr_remote/$curr_merge_branch | tr -s '\t' '|';

Usiamo il vecchio comando Unix tr per convertire il TAB in un separatore |.

Guida introduttiva a git upstream

Questa è una guida di base su git upstream: come configurare un git upstream, creare un nuovo branch, raccogliere le modifiche, pubblicare con git fork e un buon consiglio su quanti commit in anticipo/ritardo sono presenti per il tuo branch remoto.

Bitbucket Data Center include la sincronizzazione dei fork, che sostanzialmente solleva gli sviluppatori dall'onere di tenersi aggiornati sui propri fork; Bitbucket Cloud ha invece una semplice sincronizzazione in 1 passaggio. Dai un'occhiata!

Consigliata per te

Blog di Bitbucket

Percorso di apprendimento DevOps

Scopri di più su Git

Trova altre guide e risorse su Git in questo hub.