geelweb
mercredi 29 février 2012
Technocrati
A fake post to validate the blog on Technocrati 98M73DYY5FUQ
samedi 11 février 2012
Filtrer les requêtes dans pgFouine
Si vous utilisez pgFouine, un analyseur de log PostgreSQL, il vous ai peut être déjà arrivé de ne vouloir analyser que certaines requêtes. J'ai récemment soumis un patch sur pgFoundry permettant de filtrer les requêtes analysées en se basant sur une regex (le patch peut également être téléchargé ici).
Il suffit par exemple d'ajouter un mot clé dans les requêtes que vous voulez analyser
Ensuite vous n'avez plus qu'à executer pgFouine en utilisant l'option -onlypattern
Bien entendu vous pouvez aussi filtrer sur les requêtes utilisant une table en particulier
Il suffit par exemple d'ajouter un mot clé dans les requêtes que vous voulez analyser
/* my keyword */ select * from foo;
Ensuite vous n'avez plus qu'à executer pgFouine en utilisant l'option -onlypattern
./pgfouine.php -file /var/log/syslog -onlypattern "/^\/\* my keyword/" > output.html
Bien entendu vous pouvez aussi filtrer sur les requêtes utilisant une table en particulier
./pgfouine.php -file /var/log/syslog -onlypattern "a_huge_table" > output.html
Libellés :
pgFouine,
pgFoundry,
PostgreSQL
| Réactions : |
jeudi 9 février 2012
PostregSQL partitioning : retour d'expérience
Après plus de 2 ans d'utilisation de la méthode de partitionnement dynamique PosgreSQL décrite sur ce blog, voici un petit retour d'expérience. Tout d'abord les performances ont bien été améliorées. Cependant quelques inconvénients nous ont incité à faire évoluer cette méthode.
1. Le problème de la réplication
Le fait de créer les partitions dynamiquement rend extrêmement difficile, pour ne pas dire impossible, la réplication des partitions via Slony. Des méthodes alternatives doivent donc être mises en place (comme des dump/re-import nocturne) mais avec un risque de perte de données, si la base master tombe, les données ajoutées depuis le dernier dump seront perdues.
2. Le coût du PostgreSQL Query Planner
Même en écrivant correctement les requêtes SQL de sorte qu'une seule partition ne soit effectivement utilisée, le nombre de partitions fait tout de même augmenter considérablement le temps d’exécution. Prenons par exemple 2 tables identiques partitionnées selon le même critère, la première aura 1000 partitions et la seconde 4000. Les tables ne contiennent aucune données, ceci afin d'obtenir un temps d’exécution assez représentatif du temps pris par le query planner. Sur une requête simple, les explain sur ces deux tables sont identiques :
Mais lorsque l'on execute ces 2 requêtes, l'on obtient des temps d’exécution complètement différents:
Et en y réfléchissant c'est parfaitement logique, avant de savoir qu'il faut utiliser la partition bar_3, parce qu'elle correspond à la contrainte other_id = 3, le PostgreSQL Query Planner doit tout de même vérifier toutes les tables filles pour trouver lesquels correspondent à cette contrainte.
Une solution
Ces deux problèmes ont été résolu en utilisant un nombre fixe de partitions (20 dans notre cas semble être un bon compromis). Les tables filles n'étant plus créer dynamiquement, on peut les répliquer comme des tables normales avec Slony. Le nombre de partitions n'étant que de 20, on conserve un coût d'analyse de la requête par le PostgreSQL Query Planner tout à fait correct.
Les partitions ont été créées de la façon suivante
la seule contrainte de ce nouveau système est que pour tirer plainnement parti du partitionnement il faut légèrement modifier les requêtes du type
pour plutôt faire
Cette modification a permis un gains significatif des performances avec une amélioration du temps d'éxécution allant jusqu'à 97% sur certaines requêtes.
1. Le problème de la réplication
Le fait de créer les partitions dynamiquement rend extrêmement difficile, pour ne pas dire impossible, la réplication des partitions via Slony. Des méthodes alternatives doivent donc être mises en place (comme des dump/re-import nocturne) mais avec un risque de perte de données, si la base master tombe, les données ajoutées depuis le dernier dump seront perdues.
2. Le coût du PostgreSQL Query Planner
Même en écrivant correctement les requêtes SQL de sorte qu'une seule partition ne soit effectivement utilisée, le nombre de partitions fait tout de même augmenter considérablement le temps d’exécution. Prenons par exemple 2 tables identiques partitionnées selon le même critère, la première aura 1000 partitions et la seconde 4000. Les tables ne contiennent aucune données, ceci afin d'obtenir un temps d’exécution assez représentatif du temps pris par le query planner. Sur une requête simple, les explain sur ces deux tables sont identiques :
test=> explain select * from foo where other_id=3; QUERY PLAN ----------------------------------------------------------------------- Result (cost=0.00..73.50 rows=22 width=8) -> Append (cost=0.00..73.50 rows=22 width=8) -> Seq Scan on foo (cost=0.00..36.75 rows=11 width=8) Filter: (other_id = 3) -> Seq Scan on foo_3 foo (cost=0.00..36.75 rows=11 width=8) Filter: (other_id = 3) (6 rows) test=> explain select * from bar where other_id=3; QUERY PLAN ----------------------------------------------------------------------- Result (cost=0.00..73.50 rows=22 width=8) -> Append (cost=0.00..73.50 rows=22 width=8) -> Seq Scan on bar (cost=0.00..36.75 rows=11 width=8) Filter: (other_id = 3) -> Seq Scan on bar_3 bar (cost=0.00..36.75 rows=11 width=8) Filter: (other_id = 3) (6 rows)
Mais lorsque l'on execute ces 2 requêtes, l'on obtient des temps d’exécution complètement différents:
test=> select * from foo where other_id=3; id_foo | other_id --------+---------- (0 rows) Time: 37,419 ms test=> select * from bar where other_id=3; id_bar | other_id --------+---------- (0 rows) Time: 136,268 ms
Et en y réfléchissant c'est parfaitement logique, avant de savoir qu'il faut utiliser la partition bar_3, parce qu'elle correspond à la contrainte other_id = 3, le PostgreSQL Query Planner doit tout de même vérifier toutes les tables filles pour trouver lesquels correspondent à cette contrainte.
Une solution
Ces deux problèmes ont été résolu en utilisant un nombre fixe de partitions (20 dans notre cas semble être un bon compromis). Les tables filles n'étant plus créer dynamiquement, on peut les répliquer comme des tables normales avec Slony. Le nombre de partitions n'étant que de 20, on conserve un coût d'analyse de la requête par le PostgreSQL Query Planner tout à fait correct.
Les partitions ont été créées de la façon suivante
CREATE TABLE children.part_0 ( CHECK (id_question % 20 = 0) ) INHERITS (answers); CREATE TABLE children.part_1 ( CHECK (id_question % 20 = 1) ) INHERITS (answers); ... CREATE TABLE children.part_19 ( CHECK (id_question % 20 = 19) ) INHERITS (answers);
la seule contrainte de ce nouveau système est que pour tirer plainnement parti du partitionnement il faut légèrement modifier les requêtes du type
select * from answers where id_question = 12;
pour plutôt faire
select * from answers where id_question = 12 and id_question % 20 = 12 % 20;
Cette modification a permis un gains significatif des performances avec une amélioration du temps d'éxécution allant jusqu'à 97% sur certaines requêtes.
Libellés :
PostgreSQL partitioning,
query planner,
SQL
| Réactions : |
mercredi 7 décembre 2011
Le "Dogpile effect"
L'utilisation du cache pour stocker des données, peu importe leur provenance et le système de cache, est une technique éprouvée. Tellement évidente et simple à mettre en place que l'on ne se pose pas forcément de questions sur la mise à jour du contenu stocké en cache.
La méthode la plus répandue consiste à essayer de récupérer les données en cache, si elles ont expirées ou ne sont pas en cache alors on relance le calcul et on met à jour le cache. Dans le cas d'une application web le client qui arrivera au moment où le cache aura expiré devra juste attendre un peu (parfois longtemps) qu'il soit rafraîchi. Dans un contexte de forte charge, et/ou avec des données longues à calculer, l'on va se retrouver avec plusieurs connexions arrivants au moment où le cache est expiré et pas encore rafraîchi, ce qui aura pour effet immédiat de lancer plusieurs fois la mise à jour du cache, de charger la base de données, de faire monter les cpus de serveurs, l'utilisation de la mémoire, etc. C'est cela que l'on nome le dogpile effect (terme, à priori, emprunter à l'argo du foot US pour désigner un tackle impliquant au moins 3 joueurs).
Différente technique "anti-dogpiling" existent pour sinon résoudre complètement ce problème, au moins limiter les risques qu'il ne se présente:
1. Mettre en place des tâches planifiées.
Cette méthode est très simple à mettre en place, une ou plusieurs tâches planifiées iront rafraîchir vos données en cache selon la fréquence que vous aurez définie, les clients n'ayant plus à mettre à jour les données, ont ne risque plus d'avoir de multiples demande de rafraîchissement du cache. L'inconvénient et que toutes les données en cache seront rafraîchies, qu'elles soient utiles ou non et qu’il est toujours possible qu’un ou plusieurs clients se connectent alors que les données ne sont pas présentent en cache (Memcache peut par exemple, de lui même, supprimer certaines données).
2. Verrouiller la procédure de mise à jour du cache.
L'on peut aussi choisir de laisser aux clients la responsabilité de mettre à jour le cache, dans ce cas pour éviter le dogpile effect l'on va poser des verrous avant de relancer le calcul des données en utilisant, en fonction de votre architecture, le système de fichier ou la base de données par exemple. Lorsque le verrou est posé, les autres instances du clients qui essaient d'accéder aux données vont attendre qu'elles soient mis à jour sans relancer le calcul. Cette méthode à l'avantage de ne mettre à jour le cache que pour les données utilisées, l'inconvénient et que même si la mise à jour du cache n'est lancée qu'une fois, tous les clients sont bloqués et doivent attendre qu'elle soit terminée.
3. Anticiper l'expiration du cache.
Cette dernière méthode se rapproche de la seconde, la différence est que en plus des données, l'on stockera également en cache le temps nécessaire au calcul de ces données. Lorsqu'une requête sera faite, si la durée de vie du cache restante est inférieure au temps nécessaire au rafraîchissement du cache alors on posera les verrous et l'on lancera la mise à jour du cache en asynchrone. L'avantage de cette technique est qu'aucun client n'aura plus à attendre que le cache soit mis à jour, la demande sera anticipé. L'inconvénient et qu'il faut tout de même garder une solution de secours avec une des autre méthode dans le cas, par exemple, où aucun appel ne serait fait dans le délai activant la mise à jour du cache.
La méthode la plus répandue consiste à essayer de récupérer les données en cache, si elles ont expirées ou ne sont pas en cache alors on relance le calcul et on met à jour le cache. Dans le cas d'une application web le client qui arrivera au moment où le cache aura expiré devra juste attendre un peu (parfois longtemps) qu'il soit rafraîchi. Dans un contexte de forte charge, et/ou avec des données longues à calculer, l'on va se retrouver avec plusieurs connexions arrivants au moment où le cache est expiré et pas encore rafraîchi, ce qui aura pour effet immédiat de lancer plusieurs fois la mise à jour du cache, de charger la base de données, de faire monter les cpus de serveurs, l'utilisation de la mémoire, etc. C'est cela que l'on nome le dogpile effect (terme, à priori, emprunter à l'argo du foot US pour désigner un tackle impliquant au moins 3 joueurs).
Différente technique "anti-dogpiling" existent pour sinon résoudre complètement ce problème, au moins limiter les risques qu'il ne se présente:
1. Mettre en place des tâches planifiées.
Cette méthode est très simple à mettre en place, une ou plusieurs tâches planifiées iront rafraîchir vos données en cache selon la fréquence que vous aurez définie, les clients n'ayant plus à mettre à jour les données, ont ne risque plus d'avoir de multiples demande de rafraîchissement du cache. L'inconvénient et que toutes les données en cache seront rafraîchies, qu'elles soient utiles ou non et qu’il est toujours possible qu’un ou plusieurs clients se connectent alors que les données ne sont pas présentent en cache (Memcache peut par exemple, de lui même, supprimer certaines données).
2. Verrouiller la procédure de mise à jour du cache.
L'on peut aussi choisir de laisser aux clients la responsabilité de mettre à jour le cache, dans ce cas pour éviter le dogpile effect l'on va poser des verrous avant de relancer le calcul des données en utilisant, en fonction de votre architecture, le système de fichier ou la base de données par exemple. Lorsque le verrou est posé, les autres instances du clients qui essaient d'accéder aux données vont attendre qu'elles soient mis à jour sans relancer le calcul. Cette méthode à l'avantage de ne mettre à jour le cache que pour les données utilisées, l'inconvénient et que même si la mise à jour du cache n'est lancée qu'une fois, tous les clients sont bloqués et doivent attendre qu'elle soit terminée.
3. Anticiper l'expiration du cache.
Cette dernière méthode se rapproche de la seconde, la différence est que en plus des données, l'on stockera également en cache le temps nécessaire au calcul de ces données. Lorsqu'une requête sera faite, si la durée de vie du cache restante est inférieure au temps nécessaire au rafraîchissement du cache alors on posera les verrous et l'on lancera la mise à jour du cache en asynchrone. L'avantage de cette technique est qu'aucun client n'aura plus à attendre que le cache soit mis à jour, la demande sera anticipé. L'inconvénient et qu'il faut tout de même garder une solution de secours avec une des autre méthode dans le cas, par exemple, où aucun appel ne serait fait dans le délai activant la mise à jour du cache.
Libellés :
dogpile cache performance optimisation
| Réactions : |
samedi 16 avril 2011
Lancer des traitements asynchrones en PHP
Il y a différentes raisons de vouloir lancer des traitements asynchrones en PHP, les insertions de logs par exemple, où l’on a pas besoin d’attendre un retour pour continuer. Ou encore pour récupérer les différents éléments de contenu d’une page web simultanément, et ainsi réduire le temps nécessaire à l’affichage de la page. On peut aussi vouloir mettre en place un service qui lancera d'autres scripts, indépendants les uns des autres. Voici quelques une des possibilités qui existent pour effectuer des traitements asynchrones en PHP.
- exec permet de faire des appels systèmes. Cette méthode prend en paramètre une ligne de commande comme vous la taperiez dans un terminal. Ajouter un & à la fin de cette ligne de commande permet de la lancer en tâche de fond et vous permet de ne pas attendre le résultat.
- curl_multi_init permet de lancer plusieurs traitements cURL en parallèle. La fonctionnalité est très intéressante, en particulier quand il s’agit de faire plusieurs appels à des web-services pour récupérer du contenu.
- Pcntl est une extension PHP implémentant un système de contrôle des processus permettant la gestion de tâches en parallèle. Comme le précise les premières lignes de la documentation, Pcntl n'est pas fait pour l'utilisation en mode web. Le contrôle de processus pouvant se révéler assez complexe, son utilisation est à réserver à des utilisateurs avertis.
- fsockopen permet d’initialiser une connexion par socket, il suffit ensuite de poster des données avec fwrite par exemple. A noter qu’il est possible d’ouvrir une connexion persistante avec pfsockopen.
- pg_send_query permet d’envoyer une requête SQL à une base de données PostgreSQL sans attendre le résultat. Si vous utilisez une base de données PostgreSQL, cette solution est sûrement la plus élégante et la plus simple à mettre en place pour insérer des logs dans une base de données. Si toutefois vous avez besoin du résultat de la ou des requêtes exécutées, pg_get_result permet de récupérer les résultats des différentes requêtes.
vendredi 1 avril 2011
Fonctions natives vs Objets
La programmation orientée objet (POO) a de nombreux avantages sur la programmation procédurale, le code est plus simple à lire, à maintenir et à faire évoluer, l’on peut réutiliser ses objets dans différents projets, mettre en place des tests unitaires pour s’assurer de leur bon fonctionnement, et même générer la documentation à partir des commentaires. Bref à priori que des avantages. À priori car à force de vouloir développer un objet pour la moindre opération on fini par tomber dans l’excés. Prenons par exemple une opération simple de formatage de date où l’on doit transformer une date au format standard issue d’une base de données dans un format plus “user friendly”. J’utilise ZendFramework depuis maintenant un certains temps et j’ai vu ces dernières années cette manipulation faite très souvent en utilisant Zend_Date. ZendFramework est sans aucun doute un très bon outil, les objets sont riches et répondent à grand nombre de besoin ; un test similaire pourrait sûrement être effectué en utilisant des objets de Symphony ou de CakePHP.
Voici le test effectué, il donne le temps nécessaire et la mémoire utilisée pour réaliser la conversion de date en utilisant Zend_Date, DateTime un objet natif fourni par PHP et les fonctions natives de PHP strftime et strtotime
Et voici le résultat,
Sans surprises l’utilisation des fonctions natives est beaucoup moins gourmande en ressources. Attention je ne fait aucun reproche à Zend_Date, cet objet à été designé pour remplir des tâches autrement plus compliquées que la simple conversion de format. Le problème c’est l’utilisation que certains développeurs font des outils à leur disposition, à grand principe de “qui peut le plus, peut le moins” ils vont avoir tendance à sortir un objet implémentant un algorithme complexe développer pour détecter des mots clés dans un texte pour... afficher un “Hello word!”.
Quelque soit le langage, les objets disponibles sont conçus dans un but bien défini, vérifiez toujours que l’objet que vous voulez utiliser est fait pour ce que vous voulez en faire, et si possible, utilisez les fonctions natives du langage.
Voici le test effectué, il donne le temps nécessaire et la mémoire utilisée pour réaliser la conversion de date en utilisant Zend_Date, DateTime un objet natif fourni par PHP et les fonctions natives de PHP strftime et strtotime
$d = '2011-03-28 07:28:12';
$loop=1000;
$i=0;
$start = microtime(true);
$memory_start = memory_get_usage();
while ($i++ < $loop) {
$date = new Zend_Date($d, 'MM.dd.yyyy hh:mi:ss');
}
$memory_end = memory_get_usage();
$end = microtime(true);
echo 'Zend_Date - Time : ', $end - $start,
' / Memory : ', $memory_end - $memory_start, "\n";
unset ($date);
$i=0;
$start = microtime(true);
$memory_start = memory_get_usage();
while ($i++ < $loop) {
$date = new DateTime($d);
$date = $date->format('j M Y H:i:s');
}
$memory_end = memory_get_usage();
$end = microtime(true);
echo 'Native PHP Object - Time : ', $end - $start,
' / Memory : ', $memory_end - $memory_start, "\n";
unset ($date);
$i=0;
$start = microtime(true);
$memory_start = memory_get_usage();
while ($i++ < $loop) {
$date = strftime('%d %b %Y %H:%M:%S', strtotime($d));
}
$memory_end = memory_get_usage();
$end = microtime(true);
echo 'Native PHP - Time : ', $end - $start,
' / Memory : ', $memory_end - $memory_start, "\n";
Et voici le résultat,
Zend_Date - Time : 6.49872088432 / Memory : 5401472 Native PHP Object - Time : 0.00977492332458 / Memory : 400 Native PHP - Time : 0.00840377807617 / Memory : 176
Sans surprises l’utilisation des fonctions natives est beaucoup moins gourmande en ressources. Attention je ne fait aucun reproche à Zend_Date, cet objet à été designé pour remplir des tâches autrement plus compliquées que la simple conversion de format. Le problème c’est l’utilisation que certains développeurs font des outils à leur disposition, à grand principe de “qui peut le plus, peut le moins” ils vont avoir tendance à sortir un objet implémentant un algorithme complexe développer pour détecter des mots clés dans un texte pour... afficher un “Hello word!”.
Quelque soit le langage, les objets disponibles sont conçus dans un but bien défini, vérifiez toujours que l’objet que vous voulez utiliser est fait pour ce que vous voulez en faire, et si possible, utilisez les fonctions natives du langage.
Libellés :
PHP optimisation POO objet développement
| Réactions : |
mardi 23 novembre 2010
GIT en mode collaboratif
Ou plus exactement git en mode collaboratif, sans push, et en forçant les responsables à relire le code...
Gros programme... Mais si il y a un scm qui permet cela facilement c'est bien GIT. Voici donc un exemple complet expliquant aux développeur voulant débuter avec GIT comment utiliser les clones et les branches. Ont retrouve dans cet exemple deux intervenants, le developpeur lead qui est responsable du code et de sa relecture et le developpeur qui va faire les modifications.
Les avantages de l'utilisation décrite si dessous sont les suivants :
Création du projet
Premièrement, créons un projet...
Chaque projet a besoin d'un fichier README, ont prend donc un puissant éditeur de texte, vim (troll inside), pour en créer un...
Voyons ce que donne une des commandes de base de git "git status". Status affiche le... status du répertoire, les fichiers ajoutés, supprimés, à commité, etc
il nous dit d'ajouter le fichier, on obéit...
Maintenant il dit quoi ?
"Changes to be committed", ok on commit...
Créer les clones
Maintenant le travail va commencer, il nous faut cloner le dépôt. Pour le devlead, on clone le trunk...
Pour le dev, on clone le devlead...
Contrairement à svn où l'on fait de multiple checkout d'un dépôt, on se retrouve ici avec une arborescence plus proche d'un arbre généalogique.
Et on a donc les répertoires suivants...
Le dev travaille
Il faut maintenant commencer à coder... Quelles branches sont dispo...
On crée une branche pour la nouvelle feature...
L'étoile devant le nom de la branche indique la branche active.
On modifie des trucs, toujours avec vim (troll again ?), puis on commit...
Arriver là le dev doit juste informer le dev lead qu'il a implémenter le truc ds la branche "isseu1" de son clone. Pour ca il peut envoyer un mail, un sms, un tweet, lui taper sur l'épaule, laisser un post-it sur sa tasse de café, ou utiliser je ne sais quelle autre méthode de communication.
Le dev lead récup les commits du dev
On repasse au point de vue dev lead, dans son clone, le fichier README.txt est toujours l'ancien...
Le dev lead va créer une branche pour récupérer les modifs du dev sans pourrir tout son clone...
Reste plus qu'à récup les commits du dev... on prend les commit dans la branche issue1 du repertoire dev1 et on les met ds la branche courante...
On vérifie le code
Le dev lead merge les modifs
Le dev lead a peut être modifié le code en vérifiant le travail du dev, on vérifie...
Tout est ok, le dev lead peut merger sur son master, on repasse sur la branche master...
Vous vous souvenez du contenu du fichier README ? (regardez plus haut dans la branche issue1bydev1). Il est différent sur la branche master...
On merge la branche issue1bydev1 ds la branche courante (master)...
On vérifie le résultat du merge, le fichier README.txt a changé...
On peut supprimer la branche utilisé pour récupérer les commits du dev...
Le dev lead met à jour le trunk
Maintenant la modification peut être intégré au trunk, ce qui permettra à tous ceux qui ont cloner le trunk de la récupérer....
Le dev met régulièrement son clone à jour
Que se passe-t-il pour le dev ? Ds quel état sont ses branches...
Le fichier README.txt est toujours l'ancien... Normal le dev n'a pas mergé les modifications faites dans la branche issue1...
Essayons un pull...
Oh! Mes modifications ont été vérifiées et acceptées! Elles apparaissent...
Gros programme... Mais si il y a un scm qui permet cela facilement c'est bien GIT. Voici donc un exemple complet expliquant aux développeur voulant débuter avec GIT comment utiliser les clones et les branches. Ont retrouve dans cet exemple deux intervenants, le developpeur lead qui est responsable du code et de sa relecture et le developpeur qui va faire les modifications.
Les avantages de l'utilisation décrite si dessous sont les suivants :
- Pas de PUSH, et donc pas de PULLs risqués, en effet les développeurs qui viennent de SVN ont souvent au début le reflex de remplacer le svn commit par un git commit et git push affin de publier leur modif. On se retrouve alors avec des git pull difficile à gérer, et on a alors recours a git cherry-pick qui est franchement pénible à utiliser.
- Le développeur lead est obligé de vérifier le travail du dev (et ca c'est bien
Création du projet
Premièrement, créons un projet...
guil@laptop:~/dev$ mkdir -p project/trunk guil@laptop:~/dev$ cd project/trunk guil@laptop:~/dev/project/trunk$ git init Initialized empty Git repository in /home/gluchet/dev/project/trunk/.git/
Chaque projet a besoin d'un fichier README, ont prend donc un puissant éditeur de texte, vim (troll inside), pour en créer un...
guil@laptop:~/dev/project/trunk$ vim README.txt
Voyons ce que donne une des commandes de base de git "git status". Status affiche le... status du répertoire, les fichiers ajoutés, supprimés, à commité, etc
guil@laptop:~/dev/project/trunk$ git status # On branch master # # Initial commit # # Untracked files: # (use "git add..." to include in what will be committed) # # README.txt nothing added to commit but untracked files present (use "git add" to track)
il nous dit d'ajouter le fichier, on obéit...
guil@laptop:~/dev/project/trunk$ git add README.txt
Maintenant il dit quoi ?
guil@laptop:~/dev/project/trunk$ git status # On branch master # Changes to be committed: # (use "git reset HEAD..." to unstage) # # new file: README.txt #
"Changes to be committed", ok on commit...
guil@laptop:~/dev/project/trunk$ git commit -m 'initial commit' README.txt [master (root-commit) 90bd4da] initial commit 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 README.txt
Créer les clones
Maintenant le travail va commencer, il nous faut cloner le dépôt. Pour le devlead, on clone le trunk...
guil@laptop:~/dev/project$ git clone trunk/.git devlead Initialized empty Git repository in /home/gluchet/dev/project/devlead/.git/
Pour le dev, on clone le devlead...
guil@laptop:~/dev/project$ git clone devlead/.git dev1 Initialized empty Git repository in /home/gluchet/dev/project/dev1/.git/
Contrairement à svn où l'on fait de multiple checkout d'un dépôt, on se retrouve ici avec une arborescence plus proche d'un arbre généalogique.
Et on a donc les répertoires suivants...
guil@laptop:~/dev/project$ ls dev1 devlead trunk
Le dev travaille
Il faut maintenant commencer à coder... Quelles branches sont dispo...
guil@laptop:~/dev/project/dev1$ git branch * master
On crée une branche pour la nouvelle feature...
guil@laptop:~/dev/project/dev1$ git branch issue1 guil@laptop:~/dev/project/dev1$ git checkout issue1 Switched to branch 'issue1' guil@laptop:~/dev/project/dev1$ git branch * issue1 master
L'étoile devant le nom de la branche indique la branche active.
On modifie des trucs, toujours avec vim (troll again ?), puis on commit...
guil@laptop:~/dev/project/dev1$ git status # On branch issue1 # Changed but not updated: # (use "git add..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: README.txt # no changes added to commit (use "git add" and/or "git commit -a") guil@laptop:~/dev/project/dev1$ git add README.txt guil@laptop:~/dev/project/dev1$ git commit -m 'Fix the issue1' README.txt [issue1 ad0e465] Fix the issue1 1 files changed, 3 insertions(+), 0 deletions(-)
Arriver là le dev doit juste informer le dev lead qu'il a implémenter le truc ds la branche "isseu1" de son clone. Pour ca il peut envoyer un mail, un sms, un tweet, lui taper sur l'épaule, laisser un post-it sur sa tasse de café, ou utiliser je ne sais quelle autre méthode de communication.
Le dev lead récup les commits du dev
On repasse au point de vue dev lead, dans son clone, le fichier README.txt est toujours l'ancien...
guil@laptop:~/dev/project/devlead$ more README.txt This is my project README file
Le dev lead va créer une branche pour récupérer les modifs du dev sans pourrir tout son clone...
guil@laptop:~/dev/project/devlead$ git branch issue1bydev1 guil@laptop:~/dev/project/devlead$ git checkout issue1bydev1 Switched to branch 'issue1bydev1' guil@laptop:~/dev/project/devlead$ git branch * issue1bydev1 master
Reste plus qu'à récup les commits du dev... on prend les commit dans la branche issue1 du repertoire dev1 et on les met ds la branche courante...
guil@laptop:~/dev/project/devlead$ git pull ../dev1 issue1 remote: Counting objects: 5, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From ../dev1 * branch issue1 -> FETCH_HEAD Updating 90bd4da..ad0e465 Fast-forward README.txt | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
On vérifie le code
guil@laptop:~/dev/project/devlead$ more README.txt This is my project README file you can install this amazing software using the Makefile (as soon as it will be available)
Le dev lead merge les modifs
Le dev lead a peut être modifié le code en vérifiant le travail du dev, on vérifie...
guil@laptop:~/dev/project/devlead$ git status # On branch issue1bydev1 nothing to commit (working directory clean)
Tout est ok, le dev lead peut merger sur son master, on repasse sur la branche master...
guil@laptop:~/dev/project/devlead$ git checkout master Switched to branch 'master'
Vous vous souvenez du contenu du fichier README ? (regardez plus haut dans la branche issue1bydev1). Il est différent sur la branche master...
guil@laptop:~/dev/project/devlead$ more README.txt This is my project README file
On merge la branche issue1bydev1 ds la branche courante (master)...
guil@laptop:~/dev/project/devlead$ git merge issue1bydev1 Updating 90bd4da..ad0e465 Fast-forward README.txt | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
On vérifie le résultat du merge, le fichier README.txt a changé...
guil@laptop:~/dev/project/devlead$ more README.txt This is my project README file you can install this amazing software using the Makefile (as soon as it will be available)
On peut supprimer la branche utilisé pour récupérer les commits du dev...
guil@laptop:~/dev/project/devlead$ git branch -d issue1bydev1 Deleted branch issue1bydev1 (was ad0e465). guil@laptop:~/dev/project/devlead$ git branch * master
Le dev lead met à jour le trunk
Maintenant la modification peut être intégré au trunk, ce qui permettra à tous ceux qui ont cloner le trunk de la récupérer....
guil@laptop:~/dev/project/trunk$ git pull ../devlead master remote: Counting objects: 5, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From ../devlead * branch master -> FETCH_HEAD Updating 90bd4da..ad0e465 Fast-forward README.txt | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
Le dev met régulièrement son clone à jour
Que se passe-t-il pour le dev ? Ds quel état sont ses branches...
guil@laptop:~/dev/project/dev1$ git branch * issue1 master guil@laptop:~/dev/project/dev1$ git checkout master Switched to branch 'master' guil@laptop:~/dev/project/dev1$ git branch issue1 * master
Le fichier README.txt est toujours l'ancien... Normal le dev n'a pas mergé les modifications faites dans la branche issue1...
guil@laptop:~/dev/project/dev1$ more README.txt This is my project README file
Essayons un pull...
guil@laptop:~/dev/project/dev1$ git pull From /home/gluchet/dev/project/devlead/ 90bd4da..ad0e465 master -> origin/master Updating 90bd4da..ad0e465 Fast-forward README.txt | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
Oh! Mes modifications ont été vérifiées et acceptées! Elles apparaissent...
guil@laptop:~/dev/project/dev1$ more README.txt This is my project README file you can install this amazing software using the Makefile (as soon as it will be available)
Inscription à :
Messages (Atom)