-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy path03-Git.Rmd
841 lines (592 loc) · 38.6 KB
/
03-Git.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
# Git et GitHub {#chap-git}
\toc{1}
Le contrôle de source consiste à enregistrer l'ensemble des modifications apportées sur les fichiers suivis.
Les avantages sont nombreux: traçabilité et sécurité du projet, possibilité de collaborer efficacement, de revenir en arrière, de tenter de nouveaux développements sans mettre en péril la version stable...
## Principes {#sec:principes-git}
### Contrôle de source {#sec:git-cds}
L'outil standard est aujourd'hui *git*.
Les commandes de git peuvent être exécutées dans le terminal de RStudio.
(ref:git-Status) Capture d'écran du terminal de RStudio. La commande `git status` supposée décrire l'état du dépôt renvoie une erreur si le projet R n'est pas sous contrôle de source.
```{r}
#| label: git-Status
#| echo: false
#| fig.cap: "(ref:git-Status)"
knitr::include_graphics('images/git-Status.png')
```
La commande `git status` (figure \@ref(fig:git-Status)) retourne l'état du dépôt (*repository*), c'est-à-dire l'ensemble des données gérées par git pour suivre le projet en cours.
RStudio intègre une interface graphique pour git suffisante pour se passer de la ligne de commande dans le cadre d'une utilisation standard, présentée ici.
### git et GitHub
*git* est le logiciel installé sur le poste de travail.
*GitHub* est une plateforme, accessible par le web[^314], qui permet de partager le contenu des dépôts git (pour travailler à plusieurs) et de partager de la documentation sous la forme d'un site web (*GitHub Pages*).
[^314]: https://github.com/
Comme GitHub permet au minimum la sauvegarde des dépôts git, les deux sont toujours utilisés ensemble.
GitHub n'est pas la seule plateforme utilisable mais la principale.
Les alternatives sont Bitbucket[^309] et GitLab[^310] par exemple.
[^309]: https://bitbucket.org/
[^310]: https://about.gitlab.com/
## Créer un nouveau dépôt {#sec:creerdepot}
### A partir d'un projet existant
Dans un projet R existant, activer le contrôle de source dans les options du projet (figure \@ref(fig:git-Project)).
La commande exécutée est `git init`.
Redémarrer RStudio à la demande.
(ref:git-Project) Activation du contrôle de source dans le menu "Tools > Project Options...".
```{r}
#| label: git-Project
#| echo: false
#| fig.cap: "(ref:git-Project)"
knitr::include_graphics('images/git-Project.png')
```
Une nouvelle fenêtre *Git* apparaît dans le panneau supérieur droit.
Elle contient la liste des fichiers du projet (figure \@ref(fig:git-Fichiers)).
(ref:git-Fichiers) Fichiers du projet, pas encore pris en compte par git.
```{r}
#| label: git-Fichiers
#| echo: false
#| fig.cap: "(ref:git-Fichiers)"
knitr::include_graphics('images/git-Fichiers.png')
```
A ce stade, les fichiers ne sont pas pris en compte par git: leur statut est un double point d'interrogation jaune.
Pour git, le répertoire de travail local est un *bac à sable* où toutes les modifications sont possibles sans conséquences.
Le fichier `.gitignore` contient la liste des fichiers qui n'ont jamais vocation à être pris en compte, qu'il est donc inutile d'afficher dans la liste: les fichiers intermédiaires produits automatiquement par exemple.
La syntaxe des fichiers `.gitignore` est détaillée dans la documentation de git[^305].
En règle générale, utiliser un fichier existant: les modèles de documents notamment incluent leur fichier `.gitignore`.
[^305]: https://git-scm.com/docs/gitignore
### Prendre en compte des fichiers
Dans la fenêtre git, cocher la case *Staged* permet de prendre en compte (*Stage*) chaque fichier.
La commande exécutée est `git add <NomDeFichier>`.
Les fichiers pris en compte une première fois ont le statut "A" pour "Added".
Les fichiers pris en compte font partie de l'*index* de git.
### Valider des modifications
(ref:git-Commit) Fenêtre de validation des modifications prises en compte.
```{r}
#| label: git-Commit
#| echo: false
#| fig.cap: "(ref:git-Commit)"
knitr::include_graphics('images/git-Commit.png')
```
Les fichiers pris en compte peuvent être validés (*Commit*) en cliquant sur le bouton "Commit" dans la fenêtre *Git*.
Une nouvelle fenêtre s'ouvre (figure \@ref(fig:git-Commit)), qui permet de visualiser toutes les modifications par fichier (ajouts en verts, suppressions en rouge).
Le grain de modification traité par git est la ligne de texte, terminée par un retour à la ligne.
Les fichiers binaires comme les images sont traités en bloc.
Chaque validation (*Commit*) est accompagnée d'un texte de description.
La première ligne est la description courte.
Une description détaillée peut être ajoutée après un saut de ligne.
Pour la lisibilité de l'historique du projet, chaque *commit* correspond donc à une action, correspondant à la description courte: tous les fichiers modifiés ne sont pas forcément pris en compte et validés en une fois.
La commande exécutée est `git commit -m "Message de validation"`.
(ref:git-id) Fenêtre de demande d'identification.
```{r}
#| label: git-id
#| echo: false
#| fig.cap: "(ref:git-id)"
knitr::include_graphics('images/git-id.png')
```
Les validations sont liées à leur auteur, qui doit être identifié par git.
En règle générale, git utilise les informations du système.
S'il n'y parvient pas, une fenêtre demande à l'utilisateur de s'identifier avant d'effectuer son premier *commit* (figure \@ref(fig:git-id)).
Les commandes indiquées sont à exécuter dans le terminal de RStudio.
Elles peuvent aussi être utilisées pour vérifier les valeurs connues par git:
```
git config user.name
git config user.email
```
Dès la première validation, la branche principale du dépôt, appelée "master", est créée.
Une branche est une version du dépôt, avec son propre historique et donc ses propres fichiers.
Les branches permettent:
* de développer de nouvelles fonctionnalités dans un projet, sans perturber la branche principale qui peut contenir une version stable.
Si le développement est retenu, sa branche pourra être fusionnée avec la branche *master* pour constituer une nouvelle version stable.
* de contenir des fichiers totalement différents de ceux de la branche principale, pour d'autres objectifs.
Sur GitHub, les pages web de présentation du projet peuvent être placés dans une branche appelée "gh-pages" qui ne sera jamais fusionnée.
Le dépôt git est complètement constitué.
Dans le vocabulaire de git, il comprend trois *arbres* (figure \@ref(fig:git-Trees)):
* le répertoire de travail, ou bac à sable, qui contient les fichiers non pris en compte: inconnus, modifiés, supprimés ou renommés (case *Staged* décochée);
* l'index, qui contient les fichiers pris en compte (case *Staged* cochée);
* la tête, qui contient les fichiers validés.
(ref:git-Trees) Les trois arbres de git. Source: https://rogerdudler.github.io/git-guide/index.fr.html
```{r}
#| label: git-Trees
#| echo: false
#| fig.cap: "(ref:git-Trees)"
knitr::include_graphics('images/git-Trees.png')
```
Le statut des fichiers est représenté par deux icônes dans la fenêtre *Git* de RStudio: deux points d'interrogation quand ils n'ont pas été pris en compte par git.
Ensuite, l'icône de droite décrit la différence entre le le répertoire de travail et l'index.
Celle de gauche décrit la différence entre l'index et la tête.
Un fichier modifié aura donc l'icône `M` affichée à droite avant d'être pris en compte, puis à gauche après prise en compte.
Il est possible, même s'il vaut mieux l'éviter, de modifier à nouveau un fichier pris en compte avant qu'il soit validé: alors, les deux icônes seront affichées.
### Créer un dépôt vide sur GitHub
(ref:CreateRepo) Création d'un dépôt sur GitHub.
```{r}
#| label: CreateRepo
#| echo: false
#| fig.cap: "(ref:CreateRepo)"
knitr::include_graphics('images/CreateRepo.png')
```
Un dépôt vide sur GitHub doit être créé (figure \@ref(fig:CreateRepo)):
* sur GitHub, cliquer sur le bouton vert "New repository";
* saisir le nom du dépôt, identique à celui du projet R local;
* ajouter une description, qui apparaîtra uniquement sur la page GitHub du dépôt;
* choisir le statut du dépôt:
* public: visible par tout le monde;
* privé: visible seulement par les collaborateurs du projet, ce qui exclut de compléter par des pages web de présentation.
* ne pas ajouter de `README`, `.gitignore` ou licence: le projet doit être vide;
* cliquer sur "create Repository";
* copier l'adresse du dépôt (https://github.com/... ou [email protected]:...).
Le choix de l'adresse est lié à la méthode d'authentification.
L'authentification SSH (voir section \@ref(sec:SSH)) est à privilégier.
### Lier git et GitHub
Dans RStudio, un premier *commit* doit au moins avoir eu lieu pour que la branche principale du projet, nommée "master", existe.
En haut à droite de la fenêtre *Git* (figure \@ref(fig:git-Fichiers)), il est affiché "(no branch)" avant cela.
Ensuite, il est affiché "master", le nom par défaut de la branche principale du projet.
Le projet peut alors être lié au dépôt GitHub.
#### Méthode graphique
Cliquer sur le bouton violet à côté de "master": une fenêtre apparaît (habituellement utilisée pour la création d'une nouvelle branche, voir section \@ref(sec:branches)).
Saisir le nom de la branche "master", cliquer sur "Add Remotes" et compléter:
* Remote Name: `origin`;
* Remote URL: coller l'adresse du dépôt GitHub;
* Cliquer sur "Add".
Cocher la case "Sync with Remote".
Au message indiquant qu'une branche *master* existe déjà, cliquer sur "Overwrite".
#### En ligne de commande
Plutôt que la manipulation précédente, le lien entre Git et GitHub peut être mis en place par quelques commandes de git exécutées dans le terminal de RStudio.
Elles sont affichées sur la page d'accueil de tout dépôt vide nouvellement créé sur GitHub et peuvent donc être copiées et collées directement vers le terminal.
```
git remote add origin [email protected]:GitHubID/NomDuDepot.git
git branch -M master
git push -u origin master
```
La première commande déclare le dépôt GitHub comme dépôt distant.
Le nom *origin* est une convention de git.
Il peut être modifié mais l'organisation du projet sera plus lisible en respectant la convention.
L'adresse du dépôt est `https://github.com/GitHubID/NomDuDepot.git` si l'authentification HTTPS est choisie.
Les commandes suivantes activent la branche principale du projet et poussent son contenu vers GitHub.
Attention au nom de la branche principale (voir section \@ref(sec:branches)): par défaut, elle s'appelle "master" dans un projet créé dans RStudio mais "main" sur GitHub.
Les lignes de commande ci-dessus fournies par GitHub remplacent donc `master` par `main` et doivent être corrigées pour correspondre au nom de la branche créée par RStudio.
#### Authentification
Si l'authentification HTTPS est choisie, à la première connexion de RStudio à GitHub, une fenêtre permet de saisir ses identifiants GitHub (figure \@ref(fig:git-PAT)).
(ref:git-PAT) Identification HTTPS sur GitHub.
```{r}
#| label: git-PAT
#| echo: false
#| fig.cap: "(ref:git-PAT)"
knitr::include_graphics('images/git-PAT.png')
```
Depuis août 2021, GitHub n'accepte plus le mot de passe du compte de l'utilisateur pour cette authentification: le jeton personnel (PAT) créé en section \@ref(sec:pat) doit être saisi à sa place.
Si l'authentification SSH est choisie et a été configurée à l'installation de git (section \@ref(sec:SSH)), aucune action n'est nécessaire.
### Pousser les premières modifications
La manipulation précédente a automatiquement poussé (*Push*) les modifications validées sur GitHub.
Par la suite, il faudra cliquer sur le bouton "Push" de la fenêtre *Git* pour le faire.
Sur GitHub, les fichiers résultant des modifications enregistrées par git sont maintenant visibles.
Chaque *commit* réalisé localement est compté par git et un message "Your branch is ahead of 'origin/master' by *n* commits" affiché dans en haut de la fenêtre *Git* indique qu'il est temps de mettre à jour GitHub en poussant l'ensemble de ces *commits*.
Cliquer sur le bouton "Push" pour le faire.
A ce stade, le projet doit disposer d'un fichier `README.md` qui présente son contenu sur GitHub.
Son contenu minimal est un titre et quelques lignes de description:
```
# Nom du Projet
Description en quelques lignes.
```
Il est conseillé d'utiliser des badges[^311], à placer juste après le titre, pour déclarer l'état de maturité du projet, par exemple:
```
![stability-wip](https://img.shields.io/badge/|>
stability-work_in_progress-lightgrey.svg)
```
[^311]: https://github.com/orangemug/stability-badges
### Cloner un dépôt de GitHub
(ref:git-Clone) Clonage d'un dépôt à partir de *GitHub.*
```{r}
#| label: git-Clone
#| echo: false
#| fig.cap: "(ref:git-Clone)"
knitr::include_graphics('images/git-Clone.png')
```
Tout dépôt sur GitHub peut être installé (on dit *cloné*) sur le poste de travail en copiant son adresse qui apparaît en cliquant sur le bouton vert (figure \@ref(fig:git-Clone)).
Dans RStudio, créer un nouveau projet et, dans l'assistant, choisir "Version Control", "Git" et coller l'adresse dans le champ "Repository URL".
Le nom répertoire à créer pour le projet est déduit automatiquement de l'adresse.
Choisir le répertoire dans lequel celui du projet va être créé et cliquer sur "Create Project".
Le projet créé est lié au dépôt distant sur GitHub.
Pour travailler à plusieurs sur le même projet, le propriétaire du projet doit donner l'accès au projet à des collaborateurs (figure \@ref(fig:git-Access)), c'est-à-dire d'autres utilisateurs GitHub dans les réglages du dépôt (*Settings*).
(ref:git-Access) Attribution des droits d'accès sur GitHub.
```{r}
#| label: git-Access
#| echo: false
#| fig.cap: "(ref:git-Access)"
knitr::include_graphics('images/git-Access.png')
```
Les collaborateurs sont invités par un message envoyé par *GitHub*.
## Usage courant
### Tirer, modifier, valider, pousser
Toute séance de travail sur un projet commence en tirant (Bouton "Pull") de la fenêtre *Git* pour intégrer au dépôt local les mises à jour effectuées sur GitHub par d'autres collaborateurs.
Les modifications apportées aux fichiers du projet sont ensuite prises en compte (cocher les cases *Staged*) et validées (*Commit*) avec un message explicatif.
Une bonne pratique consiste à valider les modifications à chaque fois qu'une tâche élémentaire, qui peut être décrite dans le message explicatif, est terminée plutôt que d'effectuer des *commits* regroupant de nombreux changements avec une description vague.
Dès que possible, pousser (*Push*) les mises à jour pour qu'elles soient visibles par les collaborateurs.
### Régler les conflits
Il n'est pas possible de pousser les modifications validées si un collaborateur a modifié le dépôt distant sur GitHub.
Il faut alors les tirer pour les intégrer au dépôt local avant de pousser les modifications fusionnées.
Un conflit a lieu si un *Pull* importe dans le fichier local une modification qui ne peut pas être fusionnée automatiquement parce qu'une modification contradictoire a eu lieu localement.
Git considère chaque ligne comme un élément indivisible: la modification de la même ligne sur le dépôt distant et le dépôt local génère donc un conflit.
Git insère dans le fichier contenant un conflit les deux versions avec une présentation particulière:
```
<<<<<<<<< HEAD # Version importée du conflit
Lignes en conflit, version importée
========= # limite entre les deux versions
Lignes en conflit, version locale
>>>>>>>>> # Fin du conflit
```
Les lignes de formatage contenant les `<<<<`, les `====` et les `>>>>` doivent être supprimés et une seule version des lignes problématiques conservée, qui peut être différente des deux versions originales.
La résolution du conflit doit être prise en compte et validée.
Pour limiter les conflits dans un document contenant du texte (typiquement, un document R Markdown), une bonne pratique consiste à traiter chaque phrase comme une ligne, terminée par un retour à la ligne qui ne sera pas visible dans le document mis en forme: un saut de ligne est nécessaire pour séparer les paragraphes.
### Voir les différences
Dans la fenêtre *Git* de RStudio, le menu contextuel (affiché par un clic droit) "Diff" peut être utilisé pour afficher les modifications apportées à chaque fichier (figure \@ref(fig:git-diff)).
(ref:git-diff) Différences entre le répertoire de travail et la tête.
```{r}
#| label: git-diff
#| echo: false
#| fig.cap: "(ref:git-diff)"
knitr::include_graphics('images/git-diff.png')
```
### Revenir en arrière
Le menu contextuel "Revert" permet d'annuler toutes les modifications apportées à un fichier (affichées par *Diff*) et de rétablir son contenu validé la dernière fois (son état dans la tête).
Il n'est pas simple de revenir en arrière au-delà de la dernière validation parce que les modifications ont pu être prises en compte par des collaborateurs: leur suppression rendrait le projet incohérent.
### Voir l'historique
Le bouton en forme d'horloge de la fenêtre *Git* de RStudio affiche l'historique du projet (figure \@ref(fig:git-historique)).
(ref:git-historique) Historique des validations dans le dépôt.
```{r}
#| label: git-history
#| echo: false
#| fig.cap: "(ref:git-history)"
knitr::include_graphics('images/git-historique.png')
```
En haut se trouve la tête, puis toutes les validations (*commits*) qui l'ont constituée.
Pour chaque validation, les différences de chaque fichier peuvent être affichées en cliquant sur le nom du fichier dans la partie basse de la fenêtre.
## Branches {#sec:branches}
Les branches d'un projet sont des versions différentes mais simultanées.
Un usage typique est le développement d'une nouvelle fonctionnalité.
Si son écriture prend du temps, le projet est perturbé par le chantier en cours: le code peut ne plus fonctionner.
Si le développement s'avère impossible ou inutile, il faut pouvoir l'abandonner sans dommage.
Pour l'isoler pendant sa réalisation et se permettre de le valider ou de l'abandonner à la fin, il faut le placer dans une branche.
La branche principale du projet s'appelle "master" ou "main" à partir de novembre 2020[^315].
Elle doit toujours être dans un état stable: c'est elle qui est clonée à partir de GitHub par d'autres utilisateurs éventuels.
[^315]: https://github.com/github/renaming
Le changement de convention pour le nom de la branche "master" fait qu'à partir de novembre 2020, les projets créés sur GitHub clonés dans RStudio ont pour branche principale "main" alors que les projets créés sur RStudio puis liés à GitHub conservent le nom "master".
### Créer une nouvelle branche
Cliquer sur le bouton violet "New Branch" dans la fenêtre *git* de RStudio.
Saisir son nom et cliquer sur "Create".
La nouvelle branche est maintenant active.
Les commandes git peuvent aussi être exécutées dans le terminal (pour créer la branche et l'activer):
```
git branch new_branch
git checkout new_branch
```
### Changer de branche
Sélectionner la branche à activer dans la liste des branches locales de la la fenêtre *git*.
Les *commits* s'appliquent à la branche active.
Chaque branche se comporte comme une version différente du projet.
Attention: pour éviter la confusion, sauvegarder les modifications, prendre en compte et valider les changements avant de changer de branche.
### Pousser la nouvelle branche
Les premières modifications de la nouvelle branche doivent être poussées en ligne de commande parce que les boutons "Push" et "Pull" de la fenêtre *Git* ne fonctionnent pas tant que la branche n'existe pas sur le dépôt distant.
Exécuter, dans le terminal:
```
git push -u origin new_branch
```
### Comportement du système de fichier
A chaque changement de branche, git réécrit les fichiers du projet pour qu'ils reflètent l'état de la branche.
Les changements peuvent être observés hors de RStudio, dans l'explorateur de fichier par exemple.
Les fichiers ignorés par `.gitignore` ne sont pas modifiés.
Il est donc indispensable que les fichiers `.gitignore` des différentes branches soit identiques, sinon des fichiers ignorés dans une branche apparaîtront comme ajoutés dans la branche affichée après un changement.
Les branches de développement ont un contenu proche de celui de la branche principale.
Ce n'est pas le cas de branches spécialisées vues plus loin, comme `gh-pages` (voir section \@ref(sec:github-pages)) qui contient le site web de présentation du dépôt.
Il est préférable de ne pas tenter d'afficher ces branches dans RStudio: leur contenu est produit automatiquement et ne doit pas être modifié manuellement.
Si c'est indispensable, il faudra y copier le fichier `.gitignore` de la branche principale et garder à l'esprit que les fichiers ignorés appartiennent en réalité à une autre branche que celle affichée.
### Fusionner avec `merge`
La fusion d'une branche de développement avec la branche principale marque l'atteinte de son objectif: son code va être intégré au projet.
L'interface graphique de RStudio ne prévoit pas les fusions, il faut donc utiliser le terminal: tout d'abord, se placer dans la branche cible (possible avec l'interface graphique):
```
git checkout master
```
Ensuite, fusionner:
```
git merge new_branch
```
Dans la majorité des situations, la fusion sera automatique ("Fast Forward").
Il est possible que des conflits apparaissent: utiliser la commande `git status` pour afficher la liste des fichiers concernés, les ouvrir, régler le confit et effectuer un *commit*.
La branche fusionnée n'est pas supprimée: elles peut être utilisée à nouveau pour d'autres développements ou supprimée manuellement avec la commande suivante:
```
git branch -d new_branch
```
### Fusionner avec une requête de tirage
L'autre façon de fusionner est plus formelle mais aussi plus générale: elle permet de fusionner une branche dans un dépôt d'un autre utilisateur pour y contribuer, ou de faire valider sa branche par un autre membre de l'équipe dans un projet collaboratif.
Pour contribuer au projet d'un autre utilisateur de GitHub[^312], il faut commencer par en créer un *fork*, c'est-à-dire une copie sous la forme d'un dépôt lié à l'original.
Il sera possible de tirer les modifications de l'original pour rester à jour[^313] (par opposition à une simple copie instantanée possible en téléchargeant un Zip du projet) et, à la fin du développement, de fusionner le *fork* au dépôt original (par opposition à un clone qui ne permettrait pas de contribuer par la suite).
Ensuite, il faut créer une branche de développement comme précédemment, la modifier et finalement demander au propriétaire du dépôt de la fusionner.
Ce processus est décrit en détail dans la documentation de git .
[^312]: https://git-scm.com/book/fr/v2/GitHub-Contribution-%C3%A0-un-projet
[^313]: https://ardalis.com/syncing-a-fork-of-a-github-repository-with-upstream/
Dans le cadre plus simple d'une branche de son propre projet comme dans le cas d'un *fork*, la branche de développement est prête à être fusionnée.
Elle doit avoir être poussée sur GitHub.
Sur la page GitHub du projet, un bouton "Create Pull Request" permet de demander la fusion.
Un message décrivant les modifications proposées avec leur argumentaire doit être ajouté.
Le propriétaire du projet (les membres de l'équipe dans le cadre d'un projet collaboratif, ou soi-même si l'équipe se réduit à une personne) est averti de la requête de tirage.
Sur la page du projet original, il est possible de voir le message, la liste des modifications (chronologie des *commits* ou comparaison des fichiers), d'engager un discussion avec l'auteur de la requête...
Si la requête n'est pas retenue, elle peut être fermée.
Si elle est validée, le bouton "Merge Pull Request" permet de fusionner la branche de développement avec la branche "master" (ou une autre) du projet source.
Les requêtes de tirage sont le seul moyen de contribuer à un dépôt sur lequel on ne dispose pas de droits d'écriture.
C'est aussi le moyen de fusionner une branche de développement dans sont propre projet en en gardant une trace explicite (dans la rubrique *Pull requests* de la page GitHub du projet).
Dans le cadre d'un projet collaboratif, les propositions d'un membre (auteur de la requête) peuvent être validées par un autre (qui accepte la fusion).
## Usage avancé
### Commandes de git
Au-delà de l'usage courant permis par l'interface graphique de RStudio, des manipulations avancées des projets sont permises en utilisant git en ligne de commande.
Quelques exemples utiles sont présentés ici.
Un petit guide des commandes est proposé par Roger Dudler[^301].
Il résume les commandes essentielles, donc intégrées à l'interface graphique de RStudio.
Des liens vers des références plus complètes sont donnés en bas de la page.
[^301]: https://rogerdudler.github.io/git-guide/index.fr.html
### Taille d'un dépôt
Pour connaître l'espace disque occupé par un dépôt, utiliser la commande `git count-objects -vH`[^302].
[^302]: https://git-scm.com/docs/git-count-objects
Les données pour ce document au stade de la rédaction sont présentées à titre d'exemple.
```
$ git count-objects -v
count: 200
size: 2.66 MiB
in-pack: 0
packs: 0
size-pack: 0
prune-packable: 0
garbage: 0
size-garbage: 0
```
La taille totale est sur la ligne *size*.
Les packs sont une méthode utilisée par git pour réduire la taille du dépôt: des fichiers similaires sont stockés sous la forme d'une partie commune et de différences.
La ligne *prune-packable* donne la taille d'objets stockés à la fois sous forme individuelle et dans des packs.
Si leur taille est importante, exécuter `git prune-packed` pour la ramener à zéro.
La ligne *size-garbage* donne la taille des objets qui peuvent être supprimés.
`git gc` les supprime, mais pas seulement: il optimise le stockage.
```
$ git gc
Enumerating objects: 194, done.
Counting objects: 100% (194/194), done.
Delta compression using up to 8 threads
Compressing objects: 100% (188/188), done.
Writing objects: 100% (194/194), done.
Total 194 (delta 83), reused 0 (delta 0)
$ git count-objects -vH
count: 1
size: 5.72 KiB
in-pack: 194
packs: 1
size-pack: 4.00 MiB
prune-packable: 0
garbage: 0
size-garbage: 0 bytes
```
Ici, la majorité des objets du dépôt a été placée dans un pack (mais sa taille est supérieure à celle des objets individuels).
Il est généralement inutile d'effectuer la collecte des déchets manuellement: git gère bien l'organisation de ses dépôts.
GitHub limite la taille des dépôts.
En mai 2020, la limite est de 100 Go.
La taille de tous les dépôts d'un utilisateur authentifié peut être affichée dans les réglages de son compte ("Personal Settings", "Repositories")[^303].
[^303]: https://github.com/settings/repositories
### Supprimer un dossier
Toutes les modifications apportées à un dépôt sont stockées dans son historique.
Il peut être utile d'en supprimer dans quelques cas particuliers:
- si un fichier contenant des informations confidentielles a été validé par mégarde.
La validation de sa suppression ne le retire pas de l'historique, et les informations confidentielles restent visibles en consultant l'historique.
- si des fichiers volumineux ne sont plus nécessaires, par exemple des fichiers PDF produits par R Markdown (chapitre \@ref(chap-rediger)), binaires (donc inadaptés à git) et reproductibles à partir du code.
Typiquement, le dossier `docs` est utilisé pour stocker les documents produits à partir de code R Markdown.
Les fichiers HTML et PDF doivent s'y trouver pour constituer les pages GitHub du projet.
Chaque modification du dépôt génère une nouvelle version de ces fichiers dont le volume de l'historique devient rapidement considérable.
Une solution efficace consiste à déléguer la création de ces fichiers à un système d'intégration continue (chapitre \@ref(chap-ci)) et à retirer le dossier `docs` de la branche principale (*master*) du dépôt.
Il faut alors supprimer tout son historique pour récupérer la place qu'il occupe, qui peut être l'essentiel de la taille du dépôt.
Les commandes de suppression complète d'un dossier d'un dépôt son présentées ici[^304].
Le dépôt doit être propre, c'est-à-dire sans modifications non validées, et les versions distantes et locales synchronisées.
[^304]: https://stackoverflow.com/questions/10067848/remove-folder-and-its-contents-from-git-githubs-history
Les trois commandes suivantes suppriment complètement le dossier `docs` de l'historique du dépôt git:
```
git filter-branch --tree-filter "rm -rf docs" |>
--prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ |>
| xargs -n 1 git update-ref -d
```
Le dossier n'est pas supprimé du répertoire de travail.
Il doit donc être ajouté au fichier `.gitignore` pour ne plus être suivi.
La modification de `.gitignore` doit être validée.
Ces opérations peuvent être réalisées avec l'interface de RStudio ou en ligne de commande:
```
echo docs/ >> .gitignore
git add .gitignore
git commit -m 'Removing docs folder from git history'
```
Le nettoyage du dépôt est nécessaire pour supprimer physiquement les données retirées:
```
git gc
```
Enfin, le dépôt doit être poussé.
L'option `--force` implique le remplacement du contenu du dépôt distant par celui du dépôt local: toutes les modifications faites par des collaborateurs sont effacées, c'est pourquoi cette opération de nettoyage implique l'arrêt complet du projet pendant qu'elle a lieu.
```
git push origin master --force
```
Ce code peut être utilisé pour supprimer totalement n'importe quel fichier ou dossier d'un dépôt en remplaçant simplement `docs` dans la commande `git filter-branch` initiale.
La réduction de la taille du dépôt peut être suivie en utilisant `git count-objects -vH` avant l'opération, avant `git gc` (la taille du dépôt reste stable mais a été déplacée vers *garbage*) et à la fin (la taille du dépôt est sensiblement réduite).
### Revenir en arrière
Il est possible de restaurer un dépôt dans un état précédent en plaçant sa tête (figure \@ref(fig:git-Trees)) au niveau d'un ancien *commit*.
Toutes les modifications ultérieures sont alors détruites.
Cette opération ne doit pas être réalisée sur un dépôt partagé: les autres utilisateurs ne pourraient plus pousser leurs modifications.
Afficher l'historique du dépôt et rechercher l'identifiant (SHA) du dernier *commit* à conserver.
Dans le terminal de RStudio, exécuter:
```
git reset --hard <SHA>
git push -f
```
Tout l'historique du dépôt après le point de restauration choisi est perdu.
Une méthode moins radicale et utilisable sur un dépôt partagé consiste à exécuter un *commit* qui annule les modifications d'un autre mais ne détruit aucune donnée de l'historique.
Cette opération n'annule qu'un seul *commit* à la fois et doit donc être répétée pour en annuler plusieurs, en commençant par le plus récent.
Dans le terminal de RStudio, exécuter:
```
git revert <SHA>
```
Pour annuler le dernier *commit*, exécuter:
```
git revert HEAD
```
Utiliser `HEAD` évite simplement de rechercher l'identifiant correspondant.
## Données confidentielles dans un dépôt public {#sec:confidentiel}
Un dépôt public sur GitHub pose problème quand des données utilisées dans le projet ne le sont pas.
Une solution peu satisfaisante consiste à ne pas inclure les données au projet, ce qui le rend non reproductible.
Une meilleure solution est de les crypter, en permettant à certains utilisateurs de les décrypter.
C'est l'objet du package **secret**.
Un coffre-fort (dossier `vault`) est créé dans le projet.
Il contient une liste d'utilisateurs autorisés: chacun d'entre eux doit disposer d'une paire de clés de cryptage, une clé publique incluse dans le coffre-fort et une clé privée, gardée secrète.
Les données sont cryptées avec toutes les clés publiques disponibles (et donc dupliquées).
Les utilisateurs utilisent ensuite chacun sa clé privée pour le décryptage.
Pour ne pas multiplier les copies des données, le propriétaire du dépôt a intérêt à créer un utilisateur générique pour le projet, dont il communiquera la clé privée hors de GitHub.
Le coffre contiendra les clés du propriétaire du projet et de l'utilisateur générique seulement.
En cas de compromission de la clé privée de l'utilisateur générique, il suffira de le retirer du coffre-fort et d'en créer un nouveau.
### Génération d'une paire de clés pour le propriétaire du projet
Les clés sont générées par le logiciel _ssh_, installé avec _git_ ou par défaut sous Linux.
La procédure est identique à celle de la section \@ref(sec:SSH), mais la clé utilisée doit être au format RSA (pris en charge par le package **secret**, contrairement au format ed25519, plus sûr, utilisé pour l'authenfication sur GitHub).
Exécuter la commande suivante dans le terminal de RStudio pour créer une clé RSA:
```
ssh-keygen -t rsa -b 4096 -C "user.email"
```
Stocker la clé publique sur GitHub dans "Settings > SSH and GPG Keys".
Repérer la position de la clé: si une clé d'authentification a déjà été enregistrée pour deux postes de travail par exemple, la clé RSA sera la troisième.
### Génération d'une paire de clés pour le projet
Générer une clé au format RSA dans le terminal de RStudio:
```
ssh-keygen -t rsa -b 4096"
```
- Entrer le nom de la clé: `<RepoID>.rsa`.
- Ne pas saisir de phrase de validation (mot de passe) pour permettre l'utilisation de la clé sans interaction.
La clé privée `<RepoID>.rsa` ne doit être diffusée qu'aux ayant-droits du projet.
Il faut donc ajouter la ligne `*.rsa` au fichier `.gitignore` du projet pour ne pas pousser la clé sur GitHub.
Pour permettre l'intégration continue du projet (chapitre \@ref(chap-ci)), la clé privée doit être stockée comme un secret du dépôt GitHub contenant le projet.
Appliquer la procédure de la section \@ref(sec:secrets-ci) pour créer un secret nommé "RSA" et coller le contenu du fichier `<RepoID>.rsa` dans le champ "Value" du formulaire.
L'utilisation du secret est décrite dans la section \@ref(sec:confidentielCI).
### Création d'un coffre-fort
```{r}
#| label: unlink_vault
#| include: false
# Suppression du coffre-fort préexistant
unlink("vault", recursive = TRUE, force = TRUE)
```
Exécuter:
```{r}
#| label: create_vault
library("secret")
vault <- "vault"
create_vault(vault)
```
### Ajout des utilisateurs
Le propriétaire du projet est ajouté à partir de sa clé publique stockée sur GitHub, qui est la troisième dans notre exemple.
```{r}
#| label: add_github_user
# Identifiant GitHub du propriétaire du projet
github_user <- "EricMarcon"
# Lecture et stockage de la clé, i est le numéro de la clé
add_github_user(github_user, vault = vault, i = 3)
```
La clé de l'utilisateur générique du projet est ajoutée par:
```{r}
#| label: add_user
library("openssl")
project_id <- "NomDuProjet"
# Lecture de la clé
rsa_project <- read_pubkey(paste0(project_id, ".rsa.pub"))
# Ajout au coffre-fort
add_user(project_id, public_key = rsa_project, vault = vault)
```
### Stockage des données
Les données, stockées dans des variables de R, sont stockées une à une par la fonction `add_secret()`.
Dans l'exemple suivant, la variable s'appelle `X` et vaut 1.
```{r add_secret, tidy=FALSE}
#| label: add_secret
X <- 1
add_secret(
# Nom de la donnée
"X",
# Valeur
value = X,
# Utilisateurs autorisés: propriétaire et générique
users = c(paste0("github-", github_user), project_id),
# Coffre-fort
vault = vault
)
```
Le contenu du coffre-fort peut être vérifié:
```{r}
#| label: list_secrets
# Liste des données du coffre
list_secrets(vault = vault)
# Liste des propriétaire de la donnée "X"
list_owners("X", vault = vault)
```
Les données seront lues dans le code du projet par la commande `get_secret()`.
La clé privée de l'utilisateur générique du projet, communiquée par un moyen sécurisé aux ayant-droits, doit se trouver dans le dossier du projet.
```{r}
#| label: get_secret
# Sélection de la clé privée
Sys.setenv(USER_KEY = usethis::proj_path(paste0(project_id, ".rsa")))
# Lecture de la donnée "X"
get_secret("X", vault = vault)
```
La clé peut être vérifiée:
```{r}
#| label: local_key
local_key()
```
## Pages GitHub {#sec:github-pages}
Tout projet sur GitHub doit avoir contenir un fichier `README.md` pour le présenter.
Ce fichier est écrit au format Markdown.
Le fichier peut être placé dans le dossier `docs` pour fournir à fois la page d'accueil du dépôt et de son site web.
Le package **memoiR** fournit des commandes permettant d'automatiser ces tâches dans les projets de documents.
Un dépôt contenant un article écrit en R Markdown (voir section \@ref(sec:memo)) est utilisé comme exemple[^306].
[^306]: https://github.com/EricMarcon/Krigeage
Son fichier `README.md` existe aux deux emplacements: il est écrit par le développeur à la racine du projet et dupliqué dans `docs`.
### Activation
Pour activer les pages GitHub, il faut ouvrir les propriétés du dépôt (*Settings*) et modifier la rubrique "GitHub Pages" (dans "Options").
Sélectionner la branche du projet et le dossier contenant les pages web, ici: `master` et `/docs`.
En option, le choix d'un thème personnalise l'apparence des pages.
Le site web est accessible à une adresse[^307] du domaine *github.io*.
[^307]: https://EricMarcon.github.io/Krigeage/
Le fichier `README.md` affiché en page d'accueil a un aspect très différent mais le même contenu que celui affiché avec le code sur la page du dépôt dans GitHub.
L'intérêt des pages GitHub est de permettre un accès simple aux documents formatés quand le dépôt contient une production écrite et ou à la documentation des packages R.
Ces contenus seront présentés dans le chapitre suivant.
Un site web principal est proposé avec chaque compte GitHub, à l'adresse https://GitHubID.github.io[^308].
Il sera utilisé pour héberger un site web personnel produit par **blogdown**.
[^308]: Exemple: https://EricMarcon.github.io/Krigeage/
### Badges
Les badges sont de petites images, éventuellement mises à jour dynamiquement, qui renseignent rapidement sur le statut d'un projet.
Ils doivent être placés immédiatement après le titre du fichier `README.md`.
Une bonne pratique consiste à indiquer l'avancement dans le cycle de vie du projet.
Les badges correspondants sont listés sur le site du Tidyverse[^320].
[^320]: https://www.tidyverse.org/lifecycle/
Leur code Markdown est le suivant:
```
![stability-wip]
(https://img.shields.io/badge/lifecycle-maturing-blue.svg)
```
Le package **usethis** simplifie leur création en plaçant le code nécessaire dans le presse-papier.
Il suffit ensuite de le coller dans le fichier.
```{r}
#| label: use_lifecycle_badge
#| eval: false
usethis::use_lifecycle_badge("maturing")
```