6 points par GN⁺ 2024-05-27 | 1 commentaires | Partager sur WhatsApp
  • Un PDF qui regroupe les opérations qui bloquent souvent avec Git, pour retrouver rapidement les commandes de création de commits, changement de branche, comparaison de modifications, synchronisation distante et annulation de changements
  • Le flux de travail local repose sur git status, git add, git commit et git diff pour vérifier l’état avant et après staging et créer des commits
  • Le travail sur les branches utilise git switch, git checkout, git branch, git merge, git rebase et git cherry-pick pour créer, changer, fusionner et rebaser
  • L’exploration de l’historique et la restauration incluent git log, git blame, git show, git reflog, git reset et git restore; les branches, tags, HEAD et branches distantes peuvent aussi servir de références de commit
  • Les opérations sur dépôt distant couvrent la récupération et l’envoi de changements avec git remote add, git fetch, git pull, git push, git push --force-with-lease et git push --tags

Démarrer un dépôt et vérifier l’état actuel

  • Démarrer un nouveau dépôt :
    • git init
  • Cloner un dépôt existant :
    • git clone $URL
  • Vérifier l’emplacement et l’état actuels :
    • git status

Préparer et créer des commits

  • Ajouter un fichier non suivi :
    • git add $FILE
  • Ajouter tous les fichiers non suivis et les modifications non indexées :
    • git add .
  • Indexer seulement certaines modifications d’un fichier :
    • git add -p
  • Supprimer ou déplacer un fichier :
    • git rm $FILE
    • git mv $OLD $NEW
  • Retirer un fichier du suivi Git sans le supprimer :
    • git rm --cached $FILE
  • Annuler tout le staging :
    • git reset HEAD
  • Créer un commit :
    • git commit
    • Ouvre un éditeur de texte pour rédiger le message
  • Créer un commit en indiquant directement le message :
    • git commit -m 'message'
  • Créer un commit avec un message en incluant aussi les modifications non indexées :
    • git commit -am 'message'

Changer de branche et gérer les branches

  • Changer de branche :
    • git checkout $NAME
    • git switch $NAME
  • Créer une branche :
    • git checkout -b $NAME
    • git switch -c $NAME
  • Afficher la liste des branches :
    • git branch
  • Trier les branches par commit le plus récent :
    • git branch --sort=-committerdate
  • Supprimer une branche :
    • git branch -d $NAME
  • Supprimer une branche de force :
    • git branch -D $NAME

Explorer l’historique et suivre le code

  • Voir l’historique d’une branche :
    • git log main
  • Voir sous forme de graphe la relation entre deux branches :
    • git log --graph a b
  • Afficher le log sur une ligne :
    • git log --oneline
  • Voir qui a modifié en dernier chaque ligne d’un fichier :
    • git blame $FILENAME
  • Voir tous les commits qui ont modifié un fichier :
    • git log $FILENAME
  • Trouver les commits qui ont ajouté ou supprimé un texte précis :
    • git log -S banana

Comparer les modifications

  • Voir la différence entre un commit et son parent :
    • git show $COMMIT_ID
  • Voir la différence entre un commit de merge et les parents fusionnés :
    • git show --remerge-diff $COMMIT_ID
  • Voir un résumé du diff :
    • git diff $COMMIT_ID --stat
    • git show $COMMIT_ID --stat
  • Comparer deux commits :
    • git diff $COMMIT_ID $COMMIT_ID
  • Comparer seulement un fichier précis avec un commit :
    • git diff $COMMIT_ID $FILENAME
  • Comparer l’ensemble des modifications indexées et non indexées :
    • git diff HEAD
  • Comparer seulement les modifications indexées :
    • git diff --staged
  • Comparer seulement les modifications non indexées :
    • git diff

Fichiers de configuration et options

  • Fichier de configuration Git local :
    • .git/config
  • Fichier de configuration Git global :
    • ~/.gitconfig
  • Liste des fichiers à ignorer :
    • .gitignore
  • Définir une option de configuration :
    • git config user.name 'Julia'
  • Définir une option globale :
    • git config --global ...
  • Ajouter un alias :
    • git config alias.st status
  • Consulter les options de configuration possibles :
    • man git-config

Modifier l’historique et restaurer des fichiers

  • Annuler le commit le plus récent en conservant le répertoire de travail tel quel :
    • git reset HEAD^
  • Annuler un rebase raté :
    • git reflog BRANCHNAME
    • git reset --hard $COMMIT_ID
  • Fusionner les 5 derniers commits en un seul :
    • git rebase -i HEAD^^^^^^
    • Pour les commits à fusionner, remplacer pick par fixup
  • Récupérer la version d’un fichier depuis une autre branche ou un autre commit :
    • git checkout $COMMIT_ID $FILE
    • git restore $FILE --source $COMMIT_ID
  • Modifier le message de commit ou ajouter un fichier oublié :
    • git commit --amend

Fusionner des branches

  • Fast-forward merge pour mettre à jour la branche actuelle avec une autre branche :
    • git switch main
    • git merge banana
  • Copier un commit vers une autre branche :
    • git cherry-pick $COMMIT_ID
  • Rassembler des branches divergentes avec rebase :
    • git switch banana
    • git rebase main
  • Rassembler des branches divergentes avec merge :
    • git switch main
    • git merge banana
    • git commit
  • Rassembler des branches divergentes avec squash merge :
    • git switch main
    • git merge --squash banana
    • git commit

Dépôts distants et synchronisation

  • Ajouter un dépôt distant :
    • git remote add $NAME $URL
  • Push de la branche main vers le distant origin :
    • git push origin main
  • Envoyer une branche pour la première fois sur origin et définir la branche de suivi :
    • git push -u origin $NAME
  • Push de la branche actuelle vers la branche distante suivie :
    • git push
  • Force push :
    • git push --force-with-lease
  • Push des tags :
    • git push --tags
  • Récupérer les modifications sans modifier les branches locales :
    • git fetch origin main
  • Fetch de toutes les branches :
    • git fetch --all
  • Rebase de la branche actuelle après fetch :
    • git pull --rebase
  • Merge dans la branche actuelle après fetch :
    • git pull origin main
    • git pull

Façons de désigner un commit

  • Plusieurs références peuvent être utilisées à la place de $COMMIT_ID :
    • Branche : main
    • Tag : v0.1
    • ID de commit : 3e887ab
    • Branche distante : origin/main
    • Commit actuel : HEAD
    • Commit 3 commits avant : HEAD^^^
    • Commit 3 commits avant : HEAD~3

Abandonner des modifications

  • Supprimer les modifications indexées et non indexées d’un fichier :
    • git checkout HEAD $FILE
  • Supprimer les modifications non indexées d’un fichier :
    • git checkout $FILE
  • Supprimer toutes les modifications indexées et non indexées :
    • git reset --hard
  • Mettre de côté les modifications indexées et non indexées avec stash :
    • git stash
  • Supprimer les fichiers non suivis :
    • git clean

1 commentaires

 
GN⁺ 2024-05-27
Avis de Hacker News
  • Il y a quelques commandes qui sont de vrais joyaux cachés : git diff --staged montre la différence entre le dernier commit et ce que vous avez actuellement indexé, ce qui est pratique pour vérifier que seul ce que vous vouliez est inclus quand vous ajoutez des fichiers ou des hunks de façon fine
    git log <file> n’affiche que les commits qui ont modifié ce fichier, ce qui évite de fouiller tout le git log, et c’est particulièrement utile pour les fichiers qui changent rarement
    Quand vous utilisez git add --patch, si vous voulez choisir plus finement que les hunks découpés par Git, essayez l’option e. Les indications pour inclure/exclure des lignes individuelles sont fournies, donc pas besoin de les mémoriser

    • Je viens d’apprendre que git diff --staged est un alias de git diff --cached. J’utilise cached depuis des années, mais staged est effectivement beaucoup plus intuitif. Reste à savoir si j’arriverai à effacer cette mémoire musculaire
    • Créer beaucoup d’alias pour ce genre de commandes fait gagner pas mal de temps et abaisse aussi la barrière pour mieux organiser son travail
      Si ça vous intéresse, vous pouvez jeter un œil ou proposer des alias : https://gitlab.com/Falimonda/gitrc
    • Si vous définissez git config --global commit.verbose true, Git ajoute automatiquement le diff indexé dans la zone de commentaires de l’éditeur de message de commit
  • Une de mes commandes préférées, qui n’est pas dans la liste, est git log -LstartLine,endLine:fileName
    Comme un git blame étendu, elle montre comment les lignes indiquées ont changé au fil du temps et qui les a modifiées. Elle les suit même si les numéros de ligne changent ou si le fichier est déplacé
    Elle répond à des questions comme « qui a fait ça, quand et pourquoi ? » ou « depuis quand est-ce comme ça ? »

  • Ça me rappelle la citation de Phil Karlton : « Il n’y a que deux choses difficiles en informatique : l’invalidation de cache et le nommage. »
    De ce point de vue, le fait qu’une commande Git s’appelle blame m’a toujours semblé assez étrange, et je ne suis pas le seul à le penser
    What does 'git blame' do? https://stackoverflow.com/questions/31203001/what-does-git-blame-do
    Blame someone else for your bad code https://news.ycombinator.com/item?id=27963868
    Git blame should be called git credit https://dev.to/damcosset/git-blame-should-be-called-git-credit-27h5
    Does Git Blame sound too negative? https://www.reddit.com/r/ProgrammerHumor/comments/r5lzyo/does_git_blame_sound_too_negative_just_look_up/

    • Quand on pense au nom de l’outil, git, blame semble être un nom cohérent avec la marque
    • D’accord. Dans ma configuration, j’ai remplacé blame par who. En fait, comme je suis un utilisateur pur et dur de Vim, j’utilise simplement w
  • J’ai bien aimé la façon dont j’ai appris Git. Au début, j’ai commencé avec Sourcetree, une GUI Git tierce, et c’était très facile à utiliser et à comprendre
    Ensuite je suis passé à git gui, mais on peut sauter cette étape. Puis lancer une interface graphique est devenu pénible, alors j’ai appris les commandes du terminal
    Je le recommande aux débutants Git, mais ce n’est que mon expérience n=1

    • Tout ce que je connais de Git se résume à clone, pull, commit, push, et jusqu’ici ça m’a suffi. Git peut être aussi simple que ça. Bien sûr, on peut apprendre des usages plus complexes si nécessaire
    • Je n’ai pas envie de revenir aux commandes du terminal. Les dépôts Git et les workflows se comprennent beaucoup mieux quand ils sont visualisés ; Git est donc pratiquement un outil qui demande à être utilisé avec une GUI
      C’est au moment où j’ai commencé à utiliser Magit que Git a commencé à avoir du sens pour moi
    • Je suis passé de Sourcetree à Fork. C’est très similaire, mais les performances sont bien meilleures et l’UI est fluide. À moins que Sourcetree ne se soit amélioré ces dernières années
    • J’ai suivi un parcours similaire, mais avec GitKraken et Sublime Merge, et j’ai gardé Sublime Merge parce qu’il rend même les opérations complexes faciles
      Je n’ai que des compliments à faire à Sublime Merge
  • Je me demande s’il y a des raisons historiques au fait que Git soit inutilement compliqué
    En voyant cette antisèche, cette complexité devient encore plus palpable

    • Oui. C’est parce que Git a été conçu par Linus Torvalds comme un outil destiné à rendre possible et à faire évoluer le développement du noyau Linux
      Le fait qu’il soit devenu aussi largement utilisé est peut-être un « heureux accident », ou un malheur. GitHub a largement contribué à populariser Git avec son « hébergement Git gratuit », mais je ne pense pas que Git soit le système de gestion de versions idéal pour beaucoup de projets
      Comme il a été créé pour Linus et Linux, sa terminologie et ses workflows peuvent parfois sembler inversés par rapport aux systèmes de gestion de versions ordinaires. Le nom Pull Request vient aussi du développement de Linux, où Linus ou les mainteneurs tirent les changements dans l’arbre du noyau. Dans d’autres systèmes, on parle généralement de Merge Request, ce qui est bien plus logique dans la mesure où il s’agit de fusionner dans l’arbre des changements quelle que soit leur provenance
      Git était quasiment le seul système de gestion de versions distribué open source capable de traiter rapidement d’énormes bases de code, et c’est pour cela que l’inertie est devenue forte aussi dans les grandes entreprises
      Pour les projets personnels, je préfère largement Mercurial et je l’utilise effectivement, mais si l’on essaie Hg sur une grosse base de code comme Android AOSP, il se met à ramer sévèrement. Mercurial a une meilleure interface utilisateur et une meilleure organisation des commandes, ainsi qu’une fonction permettant de lancer à la volée un serveur web pour visualiser l’arbre des changements, mais comme il est écrit en Python, ses performances et sa scalabilité ne sont pas idéales
    • Une partie de la complexité vient du fait que, dans les premières versions, le coût d’ajout d’une nouvelle sous-commande était assez élevé. Résultat, des fonctionnalités ont continué à être greffées sur des sous-commandes existantes comme git checkout, ce qui a rendu l’expérience utilisateur plus confuse
      Dans les versions récentes de Git, le coût d’ajout de nouvelles sous-commandes a diminué, et ils ont commencé à ajouter des commandes plus spécifiques comme git switch
    • Pour la plupart des workflows, je pense qu’il suffit de connaître quelques commandes Git comme log, add, commit, pull et push. Il existe aussi des outils GUI
      Honnêtement, pour les tâches courantes, je ne trouve pas Git si compliqué. Il a simplement beaucoup de fonctionnalités puissantes disponibles quand on en a besoin
    • Git n’est pas inutilement compliqué. C’est le workflow même qu’il a été conçu pour prendre en charge qui était exceptionnellement complexe, et ses utilisateurs cibles, les développeurs du noyau, étaient très techniques, donc la complexité n’était pas un gros problème
      Les développeurs du noyau devaient communiquer entre eux de manière décentralisée et retravailler par-dessus le travail des autres. Comment travailler sur un projet où 15 000 personnes écrivent du code ? Git a été créé pour cet objectif
      Il est aussi complexe que nécessaire pour prendre en charge le workflow pour lequel il a été conçu. Créer un outil plus simple est facile, mais il ne pourrait alors pas prendre en charge des workflows complexes comme le développement du noyau Linux. Donc il n’est pas inutilement compliqué, mais il est vrai qu’il est plus complexe que nécessaire pour des usages simples
      Si l’on a besoin d’un frontend moins complexe, il existe beaucoup d’outils qui facilitent l’utilisation de Git. Si vous n’êtes pas développeur logiciel à plein temps, je ne recommande pas d’utiliser Git directement. Mais si vous êtes développeur à plein temps, cela vaut la peine de prendre le temps de comprendre ses concepts internes ; une fois qu’on les comprend, Git est assez élégant au regard de ce qu’il fait
      Je ne dirais pas que connaître Git détermine si quelqu’un est qualifié comme développeur logiciel, mais si comprendre par soi-même le fonctionnement de systèmes fondamentaux comme celui-ci ne vous procure aucune dopamine, ce domaine n’est peut-être pas fait pour vous. Ce n’est pas grave : il y a des choses qui conviennent à chacun, et d’autres non
      Pour apprendre à se déplacer dans Git, je recommande https://learngitbranching.js.org. Bien sûr, https://xkcd.com/1597/ s’applique aussi
    • Parce qu’une bonne conception d’UI est une compétence à part et demande un effort supplémentaire, elle est généralement tout simplement ignorée
  • Je ne connaissais pas git add -p, mais c’est vraiment utile
    -p, --patch permet de choisir de manière interactive les hunks de patch entre l’index et l’arbre de travail, et de les ajouter à l’index. Cela donne l’occasion de revoir les différences avant d’ajouter les modifications à l’index

    • Quand c’est possible, je sélectionne moi-même les hunks du travail en cours pour les assembler en commits cohérents. Cela m’aide à réorganiser mentalement ce que j’ai fait, et si je dois l’expliquer à quelqu’un, je peux le faire en suivant le fil des commits
  • Ce que j’aimerais ajouter ici, c’est la combinaison git commit --fixup $COMMIT_ID et git rebase -i upstream/master --autosquash
    C’est devenu indispensable dans mon workflow récent. Dans l’exemple de Julia, elle utilise HEAD^^^^^ pour rebaser les 5 commits précédents, mais jusqu’à récemment j’utilisais HEAD~5. Or je viens de comprendre qu’on peut simplement rebaser tous les commits jusqu’au HEAD upstream

    • Il faut vraiment que j’adopte tout de suite git commit --fixup $COMMIT_ID. Je fais toujours de petits commits de correction du genre “oops GET users/me”, puis je les déplace manuellement lors du rebase interactif suivant pour les fixer, mais cette méthode est bien meilleure
    • Je ne l’ai appris que récemment, et c’est excellent quand on travaille avec des patches empilés dans Gerrit
  • « Fusionner les 5 derniers commits en un seul : git rebase -i HEAD^^^^^^ » devrait plutôt être git rebase -i HEAD~5, non ?
    En supposant qu’on veuille seulement rebaser un historique linéaire
    https://stackoverflow.com/questions/2221658/whats-the-difference-between-head-and-head-in-git

  • La plus grande prise de conscience, ça a été le moment où j’ai compris que tous les commits étaient encore là, en interne. Les tags et les branches ne font que pointer vers des commits, et les commits vers lesquels une branche pointait auparavant n’ont pas disparu
    Beaucoup de commandes, peut-être même presque toutes, sont non destructrices, ce qui permet de référencer facilement l’ancien état d’une branche
    J’avais déjà vu git reflog, mais je m’en servais surtout pour me rappeler « c’était quoi déjà, cette branche sur laquelle je travaillais avant ? »
    J’avais développé une crainte à moitié saine des opérations qui peuvent vous entraîner au milieu d’une résolution de conflits non désirée. En général, les conflits arrivaient parce que je référençais le mauvais commit ou la mauvaise branche
    Cette peur n’a pas complètement disparu, mais je suis au moins devenu assez à l’aise pour récupérer d’anciens travaux au milieu d’opérations comme un rebase

    • Jujutsu a complètement fait disparaître cette peur
      Si je ne sais pas si une opération va provoquer des conflits, je la fais simplement et je regarde. S’il y a un conflit, soit je le résous, soit j’annule l’opération qui l’a créé, soit je crée un nouveau commit ailleurs et je laisse le conflit pour plus tard
      Si je sais qu’il va y avoir un conflit, je clone les branches concernées puis j’essaie de les aligner à l’avance, ou bien j’exécute l’opération conflictuelle et je suis le processus de résolution. Si la résolution devient trop brouillonne, je peux annuler une partie seulement de la résolution et continuer à essayer, la mettre de côté un moment pour faire autre chose, ou jeter tout l’arbre
  • Pour le dire franchement, il faut lâcher Git et regarder JJ
    https://martinvonz.github.io/jj/v0.17.1/
    Même Linus trouverait probablement que l’UI de Git est atroce. JJ propose un excellent workflow

    • J’ai du mal à être d’accord sur l’absence d’index. Il y a sans doute des workflows où c’est plus simple, mais, pour le meilleur ou pour le pire, je suis tellement habitué à l’index de Git que ma façon de travailler changerait assez fortement
      Cela dit, à part l’avoir un peu manipulé, je ne l’ai pas vraiment utilisé en profondeur dans un vrai contexte de travail, donc difficile d’être catégorique