--- categories: git title: Howto Git ... * Documentation: * À lire : * À voir : [Git](https://git-scm.com/) est un logiciel de gestion de code source, créé en 2005 par Linus Torvalds. ## Installation ~~~ # apt install git ~~~ Configuration minimum via `~/.gitconfig` : ~~~ [user] name = Prenom Nom email = jdoe+git@example.com [pull] rebase = true [push] default = simple ~~~ ## Commandes de base ### Créer un dépôt ~~~{.bash} $ mkdir foo $ cd foo $ git init ~~~ ### Cloner un dépôt Plusieurs méthodes d'accès pour récupérer un dépôt : HTTP(S), Git, file. ~~~{.bash} $ git clone https://git.example.com/path/projet $ git clone git://git.example.com/path/projet.git $ git clone file:///path/projet ~~~ ### Travailler sur un dépôt local Ajouter un nouveau fichier : ~~~{.bash} $ touch main.c $ git add main.c $ git commit -v ~~~ Modification d'un fichier existant : ~~~{.bash} $ vi main.c $ git add main.c $ git commit -v ~~~ *Note* : on peut éviter de faire `git add` et faire uniquement `git commit -a` si l'on est sûr qu'aucun autre fichier n'a été modifié (mais c'est une mauvaise habitude que l'on déconseille). Voir l'état du dépôt local (fichiers non commités, etc.) : ~~~{.bash} $ git status ~~~ Voir les derniers commits du dépôt local : ~~~{.bash} $ git log ~~~ ### pull/pusher avec un dépôt distant Si l'on a cloné un dépôt existant, un lien est automatiquement créé entre le dépôt local créé et le dépôt distant (référencé comme *origin*). Pour récupérer en local les derniers changements du dépôt distant : ~~~{.bash} $ git pull --rebase origin ~~~ *Note* : l'option `--rebase` est désormais le défaut mais par pédagogie on conseille de la mettre explicitement. Pour envoyer ses modifications locales vers le dépôt référencé comme distant : ~~~{.bash} $ git push origin ~~~ ### Gestion des branches Par défaut, certains outils utilisent une branche nommée *master*. Cette branche existe donc souvent au sein d'un dépôt, mais il faut garder en tête que c'est une convention, rien n'oblige en avoir une. Lister les branches existantes et connaître sa branche courante : ~~~{.bash} $ git branch -a ~~~ Créer une branche *myfeature* à partir de la branche *master* : ~~~{.bash} $ git checkout -b myfeature master ~~~ Pour passer d'une branche à une autre dans son dépôt local : ~~~{.bash} $ git checkout myfeature $ git checkout master ~~~ Travailler sur la branche *myfeature* puis merger son travail dans *master* : ~~~{.bash} $ git checkout master $ git merge --no-ff myfeature ~~~ Pousser une branche locale vers le dépôt référencé comme distant : ~~~{.bash} $ git push origin myfeature ~~~ Supprimer une branche locale et distante : ~~~{.bash} $ git branch -d myfeature $ git push origin :myfeature ~~~ ## Commandes avancées ### Modifier l'historique /!\\ Les modifications de l'historique ne doivent avoir lieu que si il n'a pas déjà été pushé vers un dépôt partagé ! Modifier son dernier message de commit : ~~~{.bash} $ git commit --amend ~~~ Modifier son dernier commit : ~~~{.bash} $ vi main.c $ git add main.c $ git commit --amend ~~~ Modifier son avant-dernier commit : ~~~{.bash} $ git rebase -i HEAD^^ … remplacer "pick" par "edit" pour le commit à modifier $ vi main.c $ git add main.c $ git commit --amend $ git rebase --continue ~~~ *Note* : On peut remonter au Nième commit en utilisant *HEAD~N* ### Piquer un commit dans une autre branche ~~~{.bash} $ git cherry-pick ~~~ ### gitignore Créer un fichier `.gitignore` à la racine pour ignorer certains fichiers/chemins : ~~~ $ cat .gitignore test.php htdocs/foo/bar toto/titi* ~~~ *Note* : le fichier .gitignore se commit dans le dépôt ### Ranger temporairement son travail (git stash) Cela permet d'avoir un buffer pour mettre "en pause" des modifications non commitées : ~~~{.bash} …hack…hack… $ git stash save ~~~ On peut lister ce buffer : ~~~{.bash} $ git stash list stash@{0}: WIP on dev: fb1fa70… m stash@{1}: WIP on dev: fb1fa70… m ~~~ Et ré-appliquer les modifications stockées : ~~~{.bash} $ git stash apply stash@{0} ~~~ Pour purger le buffer sans l'appliquer : ~~~{.bash} $ git stash clear ~~~ ### Echanger des commits sous forme de patchs Git Pour transmettre ses 3 derniers commits, on peut générer 3 patches qui contiendront les diffs ainsi que les messages de commit : ~~~{.bash} $ git format-patch -3 ~~~ Si l'on a ces 3 patches, on peut les appliquer sur son dépôt local ainsi : ~~~{.bash} $ git am 000*.patch ~~~ ## Workflow de travail Il existe plusieurs workflows possibles en travaillant avec Git. ### Le modèle Git branching Voir L'idée est de ne jamais toucher à *master*, sauf hotfixes. Pour le reste on le fait dans *dev*. Et pour les grosses features on le fait dans une branche, nommée avec le nom de la feature. ## Astuces diverses ### Partager un dépôt avec plusieurs utilisateurs Avec un dépôt *foo* existant, on autorisera les utilisateurs que si ils appartiennent au groupe *git* : ~~~ # cd foo # git config core.sharedRepository group # addgroup git # chgrp -R git . # chmod g=rwXs,o= -R . # chmod g=rwX -R . # astuce pour garder le +s sur les répertoires ~~~ ### Importer un dépôt SVN dans GIT Ce script permet de récupérer la liste des auteurs SVN, modifiez la comme voulu. ~~~{.bash} #!/usr/bin/env bash authors=$(svn log -q | grep -e '^r' | awk 'BEGIN { FS = "|" } ; { print $2 }' | sort | uniq) for author in ${authors}; do echo "${author} = Prenom Nom "; done ~~~ On lance ensuite la commande suivante : ~~~{.bash} $ git svn --authors-file=path/to/authors_file clone svn+ssh://svn.example.com/path/to/repo ~~~ ### Importer un dépôt CVS dans GIT Lancer la commande suivante : ~~~{.bash} $ git cvsimport -C repo_git -r cvs -k -vA path/to/authors_file -d user@hostname:/path/to/cvsroot repo ~~~ ### Convertir un dépôt en utf8 Créer un fichier exécutable dans `/tmp/recode-all-files` : ~~~{.bash} #!/bin/sh find . -type f -print | while read f; do mv -i "$f" "$f.recode.$$" iconv -f iso-8859-1 -t utf-8 < "$f.recode.$$" > "$f" rm -f "$f.recode.$$" done ~~~ Puis exécuter la commande suivante dans votre dépôt : ~~~{.bash} $ git filter-branch --tree-filter /tmp/recode-all-files HEAD ~~~ Exécuter ensuite la commande suivante pour convertir les messages de commit : ~~~{.bash} $ git filter-branch --msg-filter 'iconv -f iso-8859-1 -t utf-8' -- --all ~~~ ### Push vers un non-bare repository Ceci est évidemment déconseillé, car cela mettra aussi à jour les fichiers, ce qui nécessite de faire un `git reset --hard`. Mais si l'on veut tout de même le forcer : ~~~ $ git push Counting objects: 7, done. Delta compression using up to 4 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 618 bytes, done. Total 4 (delta 3), reused 0 (delta 0) Unpacking objects: 100% (4/4), done. remote: error: refusing to update checked out branch: refs/heads/master remote: error: By default, updating the current branch in a non-bare repository remote: error: is denied, because it will make the index and work tree inconsistent remote: error: with what you pushed, and will require 'git reset --hard' to match remote: error: the work tree to HEAD. remote: error: remote: error: You can set 'receive.denyCurrentBranch' configuration variable to remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into remote: error: its current branch; however, this is not recommended unless you remote: error: arranged to update its work tree to match what you pushed in some remote: error: other way. remote: error: remote: error: To squelch this message and still keep the default behaviour, set remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'. ! [remote rejected] master -> master (branch is currently checked out) ~~~ Il faut ajouter dans la config du repository "distant" : ~~~ [receive] denyCurrentBranch = warn ~~~ ### Transformer un non-bare repository en bare repository Il suffit de supprimer tous les fichiers sauf le .git, par exemple : ~~~{.bash} $ rm -rf * ~~~ Puis d'indiquer dans la config du repository : ~~~ bare = true ~~~ ### Mettre en place des notifications de push Sous Debian, pour envoyer des notifications par email à git@example.com : ~~~ # chmod a+x /usr/share/doc/git-core/contrib/hooks/post-receive-email $ cd /path/path/to/your/repository.git $ ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive $ git hooks.mailinglist git@example.com ~~~ Pour recevoir les patches : ~~~{.bash} $ git config hooks.showrev "git show -C %s; echo ~~~ ### Ignorer les vérifications SSL ~~~{.basb} $ git config --global http.sslverify "false" ~~~