====== Git ====== Molto utile questo howto: **[[http://b.lesseverything.com/2008/3/25/got-git-howto-git-and-github|Got Git? HOWTO git and github]]**. Vedere anche **[[https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow|Comparing Workflows]]**. Installare i pacchetti Debian * **''git-core''** Creare una copia locale di un progetto: git clone http://rivendell.lovergine.com/pubgit/ecw.git ecw Le informazioni relative al repository vengono salvate in una sola directory **''.git''** nella directory radice del progetto. Per aggiornare il repository locale basta eseguire nella directory del progetto: git pull ===== Prompt personalizzato ===== Quando si lavora in una copia locale Git, è comodo sapere a quale repository siamo sincronizzati. Questo trucco (da aggiungere al proprio **''.bashrc''** o simili) mostra il nome del repository git nel prompt: parse_git_branch() { git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/' } PS1="${debian_chroot:+($debian_chroot)}\u@\h:\w\$(parse_git_branch)$ " ===== Commit come se fosse SVN ===== Anzitutto si deve registrare un account su **[[https://github.com/]]**, si prende nota della **API token** (menu //Account Settings//, //Account Admin//) e si carica la propria chiave pubblica SSH (menu //Account Settings//, //SSH Public Keys//). Il proprietario del progetto deve concedere i permessi di commit. Per testare il funzionamento della chiave RSA: ssh -T git@github.com Se si ha una copia locale di un repository a cui si era fatto accesso via HTTPS, per passare alla modalità SSH: git remote -v origin https://github.com/RigacciOrg/ProTherm (fetch) origin https://github.com/RigacciOrg/ProTherm (push) git remote set-url origin git@github.com:RigacciOrg/ProTherm.git ==== git clone ==== Fare un **''clone''** del master repository e verificare che sia aggiornato. In questo esempio l'URL è di tipo //read only// ad accesso anonimo, il che obbliga a fare uno sgamotto prima del commit (vedi avanti). git clone git://github.com/qgis/qgis-web-client.git cd qgis-web-client git pull ==== git add ==== Se nel repository locale di un progetto si aggiunge un file, questo risulta come //untracked file// (vedere con ''git status''). È necessario aggiungerlo al sistema di tracking di Git con il comando: git add filename Se si sono aggiunte diverse directory e file al progetto, è possibile aggiungerli tutti al sistema di tracking di Git con: git add -A ==== git restore ==== Se un file viene modificato nel repositori locale e **non è stato committed** né pushed nel repository origine, è possibile riportarlo alla versione precedente con: git restore nomefile ==== git commit ==== A differenza che con SVN, il **''commit''** si intende rispetto alla propria copia locale. Verificare che le informazioni personali siano corrette (vengono salvate in **''$HOME/.gitconfig''**). In questo esempio viene corretto l'autore del commit dopo averlo fatto: git status git commit -a git config --global user.name "Niccolo Rigacci" git config --global user.email niccolo@rigacci.org git commit --amend --author='Niccolo Rigacci ' ==== git push ==== Solo con il **''push''** le modifiche vengono propagate sul server remoto. Le credenziali di accesso a github sono salvate in ''$HOME/.gitconfig''. In questo esempio è necessario specificare dove fare il push, perché il ''clone'' è stato fatto da un repository in sola lettura: git config --global github.user RigacciOrg git config --global github.token 2045669db9df8ba1dd13a13f62f2f793 git push git@github.com:qgis/qgis-web-client.git In alternativa si definisce un nuovo repository remoto (questo in lettura/scrittura) e si fa il ''push'' su di esso: git remote git remote add origin_rw git@github.com:qgis/qgis-web-client.git git push origin_rw ==== git pull --rebase ==== Può capitare che un ''git push'' fallisca perché il repository remoto ha ricevuto qualche push da qualche altro sviluppatore che non è stato integrato localmente. In questo caso si può sperare di risolvere semplicemente il conflitto allineando il repository locale con quello remoto e poi ripetere il commit: git pull --rebase git push ===== Fork e Request Merge su Sourceforge ===== Se si vuole contribuire a un progetto su Sourceforge usando Git: - **Fork** Dalla pagina web del progetto si clicca sul pulsante //Fork//, il codice in questione viene duplicato sotto il proprio account, sotto una gerarchia del tipo **u/niccolo**. - **Dowload** Sul proprio PC si esegue il download (clone) del codice (nella pagina web viene indicato come fare accesso RW): git clone ssh://niccolo@git.code.sf.net/u/niccolo/phpmpreloaded u-niccolo-phpmpreloaded - **Modifica** Dopo aver modificato i sorgenti con l'editor preferito, ecc. si verifica lo stato del repository con **''git status''**, si aggiungono i file modificati al successivo commit con **''git add file''**. - **Upload** Impostare i dati del proprio profilo (vedi sopra i comandi **''%%git config --global%%''**), quindi eseguire git commit -m "Comment to this commit" git push - **Request Merge** Dalla pagina web del progetto si clicca sul pulsante //Request Merge//, viene chiesto un commento più ampio della motivazione. ===== Fork e Pull Request su GitHub ===== - Navigare sul sito web, ad esempio [[https://github.com/osmandapp/OsmAnd-resources/]] - Cliccare sul pulsante **Fork**. Viene richiesto un account GitHub. Il fork viene creato all'interno di questo account. - Scaricare i sorgenti sul PC, con git clone https://github.com/RigacciOrg/OsmAnd-resources OsmAnd-resources - Modificare i sorgenti secondo il bisogno. - Fare il commit e il push delle modifichegit commit -a git push - Navigare alla radice del proprio fork e cliccare sul pulsante **New pull request**. ===== Eliminare un fork ===== Dalla propria home page, cliccare sul fork. Controllare che il titolo della pagina sia **%%/%%**. Quindi cliccare sul tab **Settings** e quindi in fondo alla pagina **Delete this repository**. ===== Gestione dei tag ===== Uno dei motivi più comuni per cui creare un **tag** è dare un nome e un numero di versione di una particolare versione del progetto. Quello che viene maracto con un tag diventa **immutabile** e non potrà essere cambiato in futuro. Per contro i **branch** servono a identificare rami diversi dello sviluppo che subiranno eventuali cambiamenti. Ad esempio per marcare la versione corrente con il nome **print3d-1.0** si esegue: git tag -a print3d-1.0 Una pratica abbastanza diffusa è quella di usare il solo numero di versione come tag, es. **v0.41**. Viene aperto l'editor per poter inserire una **breve descrizione** del tag. Se invece della versione corrente si vuole etichettare un commit precedente, bisogna individuare l'hash ed eseguire il comando (notare che l'hash è indicato in forma abbreviata): git log --pretty=oneline ... d7dd3e5c44023bfefc6a6c649506e3e029929b4c Added a 3D printable OpenSCAD model. ... git tag -a print3d-1.0 d7dd3e5 Infine bisogna salvare sul repository remoto (origin) il nuovo tag: git push origin print3d-1.0 È possibile anche fare il push di tutti i nuovi tag con il comando: git push origin --tags Se si desidera rimuovere un tag dal repository remoto: git push --delete origin print3d-1.0 ===== Gestionde dei branch ===== Mostra i rami di sviluppo, locali e remoti: git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/master ''remotes/origin/HEAD'' is the default branch for the remote named origin. This lets you simply say ''origin'' instead of ''origin/master''. ==== Creazione di un nuovo branch ==== Per creare localmente un nuovo branch di nome **jessie**: git branch jessie Si può verificare che sia stato creato: git branch -a jessie * master remotes/origin/HEAD -> origin/master remotes/origin/master Si vede che localmente esistono i due branch **jessie** e **master**; il secondo è marcato con un asterisco ed è quindi quello utilizzato per effettuare i commit locali. Quindi aver creato un branch nuovo non significa automaticamente lavorarci sopra. ==== Selezionare il branch di lavoro locale ==== Per iniziare a lavorare localmente su un determinato branch si usa il comando **git switch**: git switch jessie Si verifica che il branch locale predefinito (marcato con un asterisco) sia effettivamente cambiato: git branch -a * jessie master remotes/origin/HEAD -> origin/master remotes/origin/jessie remotes/origin/master In ogni momento è possibile cambiare branch di lavoro locale, ad esempio tornando su master: git switch master **ATTENZIONE** Prima di Git 2.23 si utilizzava il comando **git checkout** per cambiare branch di lavoro locale. ==== Propagare un branch locale anche in remoto ==== Per propagare il nuovo branch anche sul repository remoto: git push --set-upstream origin jessie È possibile verificare che localmente si sta lavorando ancora sul branch **master** e che sul repository remoto esistono i branch **master** e **jessie**: git branch -a jessie * master remotes/origin/HEAD -> origin/master remotes/origin/jessie remotes/origin/master Avendo fatto lo switch (o il checkout) ad uno specifico branch, gli eventuali commit andranno su di esso. Lo possiamo verificare con ''git status'': git status On branch jessie Your branch is ahead of 'origin/jessie' by 1 commit. (use "git push" to publish your local commits) ==== Clonare uno specifico branch ==== Quando si clona il repository remoto è possibile indicare subito su quale branch si intende lavorare: git clone --branch jessie https://github.com/RigacciOrg/project.git Con **git branch** sarà possibile verificare quale è il branch locale predefinito, marcato con un asterisco. ===== Misc Commands ===== ==== Restore o revert di un singolo file ==== A partire da Git 2.23 esiste il comando **git restore** che consente di ripristinare un file modificato localmente alla sua versione di checkout: git restore path/to/file Con versioni precedenti di Git si può usare: git checkout -- path/to/file Se si desidera tornare ad uno specifico commit, si può visualizzare il log degli stessi e richiederlo esplicitamente: git log path/to/file git checkout path/to/file ==== Colors ==== Disable GUI colors: git config --global color.ui false ==== Locally Committed Differences ==== Per vedere le differenze tra il master remoto e quello locale (**ATTENZIONE** questo comando non controlla la versione ultima presente sul repository, lavora sui dati salvati localmente!): git diff --name-only remotes/origin/master..master L'opzione ''%%--name-only%%'' limita l'output al solo nome dei file cambiati. ==== Mostrare l'URL del repository ==== git remote show origin ===== Annullare le modifiche locali in conflitto con origin/main ===== Se un file è stato cambiato sia nella copia locale che nel repository originale, queste sono le istruzioni per annullare le modifiche locali e riallineare tutto al **origin/main** (nell'esempio il file cambiato è ''database-pgsql.sql''): git fetch origin git checkout origin/main -- database-pgsql.sql git pull git status In questo modo le modifiche locali vengono annullate e **non resta traccia** di esse nel git. ===== Annullare le modifiche locali ===== I file modificati localmente possono essere in uno dei seguenti **tre stati**: * **Type 1**. Staged Tracked files * **Type 2**. Unstaged Tracked files * **Type 3**. Unstaged UnTracked files a.k.a UnTracked files Dove i termini significano: * **Staged** - File che sono stati aggiunti all'area di lavoro (staging area) o all'indice. * **Tracked** - File modificati, di cui il sistema Git si è accorto. * **UnTracked** - File nuovi, sempre //unstaged//. Se sono //staged// allora sono //tracked//. Per annullare delle modifiche si possono usare i seguenti comandi: * **''git checkout .''** - Removes Unstaged Tracked files ONLY [Type 2] * **''git clean -f''** - Removes Unstaged UnTracked files ONLY [Type 3] * **''git reset --hard''** - Removes Staged Tracked and UnStaged Tracked files ONLY [Type 1, Type 2] * **''git stash -u''** - Removes all changes [Type 1, Type 2, Type 3] Effettuare lo **stash** significa //mettere da parte// provvisoriamente le modifiche locali. Ciò che è stato //stashed// può essere visto con **git stash list**. Questi passaggi riallineano il repository locale con quello remoto. L'operazione conclusiva di merge comunque viene registrata in Git e pertanto viene richiesta una riga di commento: # Get remote files. git fetch # Remove local tracked files. git reset --hard HEAD # Merge the upstream branch into local files. git merge '@{u}' ===== Ignorare le modifiche locali ===== Se in un clone locale è necessario modificare alcuni file (ad esempio un file di configurazione) ma non si vuole che tale modifica sia propagata al repository, è possibile marcare ciascun file in questo modo: git update-index --skip-worktree config/database.php Il file in questione verrà **ignorato** sia da **pull** che da **push**. Per rimuovere tale caratteristica: git update-index --no-skip-worktree config/database.php Purtroppo questa configurazione non può essere utilizzata per ignorare una intera directory. ===== .gitignore ===== Supponiamo che nella radice di un progetto Git si abbia il file **.gitignore** che contiene: # --- Gradle & build output --- .gradle/ build/ # --- Local, machine-specific config --- local.properties # --- Signing secrets: NEVER commit these --- *.jks *.keystore keystore.properties # --- IDE / editor (not used here) --- *.iml .idea/ .vscode/ # --- OS cruft --- .DS_Store Thumbs.db # --- Misc --- *.log captures/ ad esempio la riga **build/** significa che qualsiasi cartella con quel nome verrà ignorata, ad esmepio **/build/** e **/app/build/**. L'esempio sopra è tipico di un progetto gestito con Gradle (tool per compilazione e pacchettizzazione Android). Nel file si indica di ignorare altri file eventualmente creati durante la normale attività di sviluppo, che però non ha senso replicare sul repository pubblico. Il progetto può includere altri **.gitignore** in sottocartelle, questi verranno applicati ovviamente a partire da esse e ricorsivamente nelle sottocartelle. ===== Chiave SSH per Github.com ===== Dal **13 agosto 2021** [[https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/|Github non accetterà più accesso utilizzando login e password]]. Ad esempio l'accesso da riga di comando tramite il comando **git** cesserà di funzionare. Sarà possibile utilizzare una chiave SSH/RSA; volendo utilizzare una chiave appositamente dedicata, quella che segue è la procedura. Creare la chiave RSA apposita: ssh-keygen -f ~/.ssh/id_github_rsa Aggiungere una sezione nel file **$HOME/.ssh/config**: Host github github.com HostName github.com IdentityFile ~/.ssh/id_github_rsa User RigacciOrg Collegarsi all'interfaccia web di amministrazione del proprio account, nella sezione **Settings** => **SSH and GPG keys** è possibile aggiungere una chiave, copiando il contenuto del file **id_github_rsa.pub**. A questo punto si può verificare che la chiave RSA funzioni: ssh -T git@github.com Hi RigacciOrg! You've successfully authenticated, but GitHub does not provide shell access. Verifichiamo che un repository locale sia stato clonato tramite protocollo HTTPS: git remote -v origin https://github.com/RigacciOrg/photo-reframe-slideshow.git (fetch) origin https://github.com/RigacciOrg/photo-reframe-slideshow.git (push) Si modifica il protocollo per utilizzare SSH/RSA: git remote set-url origin git@github.com:RigacciOrg/photo-reframe-slideshow.git In queste condizioni il **git clone** assume questo formato: git clone git@github.com:RigacciOrg/matrix-personal-bot.git