No Relation To Blog
Subscribe to the personal musing of Emmanuel Bernard
Ever wanted a commit was actually made of two? Read on.
There are several reasons why you could wish a commit was actually made of several distinct ones:
- because it makes the history more readable
- because you are trying to reorder some commits and it creates nasty conflicts
- just because
Merging two commits into one is easy: look for squashing for more info. While I am relatively versed in Git, I never knew how to efficiently do the opposite - splitting commits - until today.
Split a commit in two for the busy ones
Let's see the sequence first before explaining it
git rebase -i <oldsha1> # mark the expected commit as `edit` (replace pick in front of the line), save a close git reset HEAD^ git add ... git commit -m "First part" git add ... git commit -m "Second part" git rebase --continue
What did we do?
A detailed explanation
git rebase -i <oldsha1> opens a list of commits from oldsha1 to the latest commit in the branch.
- reorder them,
- change the commit message of some,
- squash (merge) two commits together,
- and edit a commit.
We use edit in our case as we want to change the commit.
Simply replace the
pick word with
edit on the line of the commit you want to split.
When you save and close this "file",
you will be placed at that commit in the command line.
Undo the actual commit
If you do a
git status or a
you will see that git places you right after the commit.
What we want is to undo the commit
and place the changes in our working area.
This is what
git reset HEAD^ does: reset the state to the second last commit
and leave the changes of the last commit in the working area.
HEAD^ means the commit at
HEAD minus 1.
Create the two commits
Next is simple gittery where you add changes and commit them the way you wish you had.
Finish the interactive rebasing
Make sure to finish the rebase by calling
git rebase --continue.
Hopefully, there won't be any conflicts and your history will contain the new commits.
A few more tips
This tip becomes much more powerful when you know how to add to the staging area parts of a file changes - instead of all the file changes that is.
The magic tool for that is
git add -p myfile but it is quite arid.
I recommend you use either GitX (Mac OS X, GUI)
or tig (CLI).
They offer a more friendly interactive way to add chunks of changes (up to line by line additions).
Another interesting tip for people that work on topic branches forked off
You can do
git rebase -i master which will list the commits between master and your branch.
See my previous post on the subject
for more info.
I am working on Hibernate Search's ability to provide field bridge autodiscovery. I am usually pretty OK at getting a green bar on first run but I got out of luck today.
Can you spot the problem?
org.hibernate.HibernateException: Error while indexing in Hibernate Search (before transaction completion) at org.hibernate.search.backend.impl.EventSourceTransactionContext$DelegateToSynchronizationOnBeforeTx.doBeforeTransactionCompletion(EventSourceTransactionContext.java:194) at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:707) at org.hibernate.engine.spi.ActionQueue.beforeTransactionCompletion(ActionQueue.java:387) at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:516) at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:105) at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177) at org.hibernate.search.test.bridge.ArrayBridgeTest.prepareData(ArrayBridgeTest.java:95) at org.hibernate.search.test.bridge.ArrayBridgeTest.setUp(ArrayBridgeTest.java:58) at org.hibernate.search.test.SearchTestCase.runBare(SearchTestCase.java:191) at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:84) at org.junit.runner.JUnitCore.run(JUnitCore.java:160) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67) Caused by: org.hibernate.search.bridge.BridgeException: Exception while calling bridge#set class: org.hibernate.search.test.bridge.ArrayBridgeTestEntity path: dates at org.hibernate.search.bridge.util.impl.ContextualExceptionBridgeHelper.buildBridgeException(ContextualExceptionBridgeHelper.java:101) at org.hibernate.search.bridge.util.impl.ContextualExceptionBridgeHelper$OneWayConversionContextImpl.set(ContextualExceptionBridgeHelper.java:130) at org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity.buildDocumentFields(DocumentBuilderIndexedEntity.java:449) at org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity.getDocument(DocumentBuilderIndexedEntity.java:376) at org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity.createAddWork(DocumentBuilderIndexedEntity.java:292) at org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity.addWorkToQueue(DocumentBuilderIndexedEntity.java:235) at org.hibernate.search.engine.impl.WorkPlan$PerEntityWork.enqueueLuceneWork(WorkPlan.java:506) at org.hibernate.search.engine.impl.WorkPlan$PerClassWork.enqueueLuceneWork(WorkPlan.java:279) at org.hibernate.search.engine.impl.WorkPlan.getPlannedLuceneWork(WorkPlan.java:165) at org.hibernate.search.backend.impl.WorkQueue.prepareWorkPlan(WorkQueue.java:131) at org.hibernate.search.backend.impl.BatchedQueueingProcessor.prepareWorks(BatchedQueueingProcessor.java:73) at org.hibernate.search.backend.impl.PostTransactionWorkQueueSynchronization.beforeCompletion(PostTransactionWorkQueueSynchronization.java:87) at org.hibernate.search.backend.impl.EventSourceTransactionContext$DelegateToSynchronizationOnBeforeTx.doBeforeTransactionCompletion(EventSourceTransactionContext.java:191) ... 19 more Caused by: java.lang.ClassCastException: [Ljava.util.Date; cannot be cast to java.util.Date at org.hibernate.search.bridge.builtin.DateBridge.objectToString(DateBridge.java:90) at org.hibernate.search.bridge.builtin.impl.String2FieldBridgeAdaptor.set(String2FieldBridgeAdaptor.java:46) at org.hibernate.search.bridge.util.impl.ContextualExceptionBridgeHelper$OneWayConversionContextImpl.set(ContextualExceptionBridgeHelper.java:127) ... 30 more
Let me zoom a bit for you.
Caused by: java.lang.ClassCastException: [Ljava.util.Date; cannot be cast to java.util.Date
java.util.Date cannot be cast to
That is usually a good sign that you're mixing classloaders and that the two objects comes from different ones.
Except that in my unit test, I don't mess around with classloaders.
That's when it hit me.
Do you see the
It means that the first type is not
ClassClastException makes perfect sense.
I'm a idiot or (preferred one) this error message needs a serious UX take over. The code is dying already. Why not take the few extra nanoseconds to represent the types in a readable way?
Caused by: java.lang.ClassCastException: java.util.Date cannot be cast to java.util.Date
I lost 5 minutes instead of 5 seconds. When you write your next error report or exception, think of the children! Or rather the poor souls that will have to use your library or code.
You can find more info on the
[L syntax on stackoverflow.
I am currently trying to move from iTerm to Terminal.app.
Terminal.app maps the Page Up/Down action to move around its buffer. This is quite annoying when you use text applications with their own buffer like mutt, vim, weechat etc).
Fortunately, there are two solutions, one easy and one more permanent.
Use Shift + Page Up
The easy one is to use
Page Up instead of
Page Down works the same.
The inconvenient is that you need to train your muscle memory.
Update the Terminal.app mapping
The more permanent one is to change the Terminal.app behavior.
Go in Preferences->Settings, select the right profile and go in the Keyboard tab.
Find the Page Up entry and edit it to send text. Then type the
ESC key followed by
For good measure, I mapped Shift + Page Up to the Scroll Page Up action.
Do the same for page down with the following text
ESC key followed by
You're all set.
While discussing with a mom whose boy wants a computer for Christmas, the said "I suppose he wants to play games, what for otherwise?". To this I obviously replied "Programming!". The usual question popped up:
Why did you start (learning) programming?
I posed longer than usual this time along. This is not as trivial as it sounds.
Non programmers truly don't understand why someone would spend hours deep into this arcane thing. They would expect to spend time away from that damn machine or at least play games on it ; this could be considered fun at least, right?
Well, it's fairly complex but there is almost always a combination of the following:
- it was the only way to achieve A (wrote my own game because I could not get ones off the shelves)
- make this machine do what I want: empowerment and control in a deterministic world.
Programmers tend to be slightly autistic when in the zone: determinism is really reassuring
- fiddling with things. When you break dad's computer, you better have to find a way to fix it before he finds out. That required technical deep dives under adrenaline.
I asked the question (why did you start programming) to the twitter-sphere. Here is the compilation in 140 characters or less.
Raw data and other stories
for me it was ~10, staring at this C:> and knowing that I could control that beast to do what I wanted. - Emmanuel Bernard
Don't take it that I am a person that enjoys control over things. It was more having the power to do what I could imagine.
to have fun with my computer and display a rotating cube :) when I was 14 - Gerben Castel
Because it was the easiest way to render mathematics concrete - Fabrice Croiseaux
Apologies Fabrice but this on ought to be the weirdest ;)
I had to get a winmoden working with Linux in 96 - Fernando Meyer
I wanted to do like my older brother who could tell his programmable calculator what to do, and it would obey his orders! - Guillaume Laforge
Now Guillaume definitely is after power ;)
I was 8, I had a Commodore 64, and I wanted to know how I could tell it what to do. - Laurent Pireyn
for me it was seeing games on a C64 and thinking: I wanna make something like that! - Marvin
un pote qui m'a montré visual basic quand j'étais au lycée... le côté créatif et ludique m'a donné envie d'essayer - Loïc Descotte
got an old Mo5 from my cousins around 8 and had to learn how to use it to play games recorded on tapes... - benzonico
I got fascinated by those strange sentences on my VIC20 manual that could make my VIC20 display beautiful things on TV :) - Fabio Mancinelli
i was so excited to write my surname on the screen of our #zx81 - asicfr
11 in 1982, ZX81, Basic. Amazed to see a machine reproduce what I had written : a program. - Jean-Baptiste Briaud
In ~1980, Video games and computers were everywhere in Tokyo. First step was to play with, then to understand them. - Sébastien Douche
At 10, I was into drawing, he was into game programming. He's an architect (building), I'm a programmer. #BrotherAndSister - Corinne
Knowledge transfer at play ;)
My father brought a ZX81 in my lost village. - Didier Girard
Nothing else to do I guess.
As a kid I was not allowed to have a gaming console, only a C64. Had to build games myself. - Olivier Hubaut
age 9, tried format A: w/ ASM from P. Norton book, ended up fmting C:, my 1st app,1st OS reinstall & freaked out dad :) - Jo Voordeckers
I did that one too. I was annoyed by the
.. in each directory so I deleted them!
config.sys and ended up reinstalling the OS.
Fiddling with things is core to programmer psyche.
The need to automate things. Write once, Run many times ! - Laurent
A lazy guy right there! Another trait of programmers.
Because computer didn't blame me when I failed vs sport/friends being a shy child. + Proud by "I did it myself" - Nicolas De loof
Despite what others think, a computer is a very predictable environment and is very reassuring for all these mildly autistic programmer minds.
@julienkauffmann qui me fait découvrir Delphi et comment changer la couleur du fond en cliquant sur un bouton. - Faulomi
I was ~11 and L'Ordinateur Individuel had a paper computer in it, with working memory registers and operators :) - David Dossot
Now that's hardcore.
as a child, it was amazing. "Look what I can do!". Getting your hands dirty. - Cédric Champeau
Craftsman spirit (power of creation). It starts with #Lego and continues with programming ;) - Daniel PETISME
code my own video game on my Alice computer. And feel powerful.... - Da Follower
Never touched a computer before entering CS. Seemed like perfect mix hard/applied science + creativity. Was fucking right! - Cyril Lacôte
At 7, my father bought a ZX81. It seemed funny, I learned "Print" ! 32 years after, still coding :) - Laurent CARON
read an article / subliminal images. Wrote a zx spectrum program to influence myself to work more in school:-)
it was in basic. Never got enough fps to make the message subliminal, so it failed... or not:-) - chanezon
I guess that's another one in the lazy category. Pretty clever though :)
wanted to program my own adventure game on the ZX Spectrum, like The Hobbit - never got around to it though :-) - Thomas Conté
because you create things out of thin air. - Jean-Laurent Morlhon
Hum, I'm sure that's not what you thought when you started as a kid.
C++ when i was 14 to create modules for D&D neverwinter nights :) - Vivian Pennel
A man with a goal.
around ten, cpc464 as Christmas gift. I had to understand how games launched with load and run commands - Mat
i was 8 years old, my Father got a CPC464, i wanted Pac Man, my Father gave me a Basic book and told me "do it yourself" - Grégory M.
A dad with foresight. Though tough on his kid.
10 y.o.: 1st family computer. 11 y.o.: Word crash, lose 1h of homework. Next day: buy Linux CD, install. Then Bash, Perl... - Pierre Chapuis
Now imagine Mom and Dad's face when they tried to launch Word the next day :)
coder les musiques des séries tv en basic sur amstrad 6128 il y a ... 25 ans... k2000 supercopter & co #nostalgie - Alfred Almendra
be able to hack into systems like the boy in "wargames" - Laurent Bristiel
Movies are bad for you kids, you have been warned! Lucky you did not for nuclear strike hacking.
at 6 wanted to help Man to land again on the moon, I thought the computer were the tool that build rockets like movies - Guillaume Wallet
Let me tell you, these moon guys knew their logarithmic tables by heart.
What's your story? Why did you start programming?
Lors d'une discussion sur les différents garbage collectors disponibles sur la JVM, Rémi Forax est sorti de sa tanière et nous a fait un résumé que je trouve intéressant.
Un peu de contexte, on parlait des différents garbages collectors:
- CMS (Concurrent Mark Sweep): implémentation "classique" dans la JVM HotSpot
- G1: nouvelle implémentation dans la JVM HotSpot qui doit remplacer à terme CMS
- C4: Azul's "pauseless" garbage collector
On parlait notamment de l'impact du temps pendant lequel les garbages collectors arrêtent le monde (stop the world).
Je laisse Rémi parler (édition mineure de ses propos et emphases personnelle):
Un GC ça marche en deux grosses phases, le marquage et la réclamation de la mémoire des objets non marqués. Pour qu'un GC soit rapide et efficace il faut essayer de faire les deux phases en même temps que le programme Java tourne. Autant pour le marquage on peut faire les choses pendant que le programme tourne autant pour la partie collection c'est plus délicat.
Il y a deux principaux algorithmes de collection,
- le sweep où on alloue et désalloue comme malloc et free, dans ce cas les objets ne bougent pas en mémoire et on peut se retrouver avec du gruyère en mémoire
- ou le compact qui consiste à recopier les objets vivants (les marqués) vers une autre zone.
L'algo de compact est meilleur mais:
- pour bien marcher il faudrait 2 fois plus de mémoire que nécessaire, comme cela on a toujours la place de copier les objets d'un endroit à un autre.
- comme on bouge les objets en mémoire, le programme Java ne peut pas tourner en même temps car il faut mettre à jour toutes les références de tous les objets entre leur ancienne adresse et leur nouvelle adresse.
De plus, réclamer la mémoire de tous le tas est lent si il est gros, donc on découpe le tas en zone, et on essaye de faire l'algo de GC sur peu de zones à la fois. Pour cela, il faut que l'on sache si une zone possède des pointeurs vers les autres zones (et à peu près où sont situés les référence). Dans ce cas le GC est incrémental et on utilise le mécanisme de protection de page pour tracker les références inter-zones.
Pour CMS, le tas est composé de plusieurs zones ordonnées, on fait des compacts pour passer les objets d'une zone à une autre et la dernière zone (assez grosse) ne fait pas de compact mais un sweep.
Cette organisation permet de faire un tracking des objets inter-zone plus simple, on regarde uniquement les pointeurs entre les zones les plus grandes vers les zones les plus petites donc on a pas besoin de coder vers quelle zone pointent les références.
Pour G1, on track les références inter-zones complets, donc toutes les zones font des compacts mais le mécanisme de tracking est plus compliqué. L'idée de G1 est que l'on indique le temps de collection maximum et qu'il adapte le nombre de zone qu'il faudra collecter en fonction. Donc on a des pause, mais si tous ce passe bien, leur durée est bornée.
Pour C4, l'idée est de faire la phase de collection de façon lazy, comme un compact normal, on bouge les objets en mémoire mais on ne ré-écrit pas les adresses, les références restent avec l'ancienne adresse mais on protège les pages contenant les anciens objet. Comme cela lorsque le programme cherche à accéder à un objet, si il possède une référence sur l'ancienne adresse, le mécanisme de protection mémoire émet un page fault. Et là il y a une astuce, lors d'un page fault, on sait à quelle adresse on a voulu lire et aussi quel instruction a merdé. Donc si lors de la génération du code assembleur on est capable pour chaque instruction qui peut générer un page fault de dire de quel champ de quel objet il s'agit, on peut mettre à jour la référence dans l'objet donc on aura pas de page fault au prochain accès de ce champ car la référence stockée dans le champ sera mise à jour.
Comme on utilise le mécanisme de protection mémoire de façon importante, l'implantation Linux de base n'est pas assez efficace, donc il faut patcher ton Linux. Le patch n'est pas petit car pour que ça dépote, la structure de données derrière est très spécifique et assez grosse ce qui fait que le patch a peut de chance d'intégrer un jour le trunk de linux.
Donc on a pas de pause comme pour CMS ou G1. En fait, il reste quelques pauses car il faut quand même faire un peu de synchro entre les différentes phases de l'algo de GC, mais lors de l'exécution du code on paye le surcout de mise à jour des références au fur et à mesure. De plus, il faut garder pas mal d'information et au niveau du code généré et au niveau de chaque zone pour être capable de faire l'association entre l'ancienne adresse et la nouvelle. Donc on a pas de pause, mais on a un overhead plus important qu'avec CMS ou G1 (G1 a déjà un overhead plus important que CMS à cause du tracking des références inter-zones).
En conclusion, pas de solution miracle, le GC à un overhead la question est quand veut-on le payer.
You have your Apple TV but no remote and need to change the WiFi settings? Read on.
You're on vacations and as a good geek, you're fully autonomous and packed:
- your Apple TV
- a router
- an HDMI cable
- your iPhone
- a couple of ethernet cables
You forgot your Apple TV remote chiefly because you always use the iOS Remote App at home.
Now you try and change the Apple TV WiFi settings and oh horror, either you plug the Apple TV through the ethernet cable and you can use the iOS Remote App, or you unplug the cable to set the WiFi but the Remote App no longer sees the Apple TV. You are stuck!
My friends, there is a way out of catch 22 hell.
- Find a regular TV remote.
- Plug the ethernet cable to the Apple TV.
- With the iOS Remote App go to
Settings -> Geenral -> Remotes,
Learn Remoteand follow the easy steps to teach to Apple TV your TV remote.
- Once done, unplug the ethernet cable
and set the WiFi under
General -> Network -> Wi-FI.
Your vacations are saved. No need to thank me ;)
Now that most of my writing is done in a markup language like Markdown or Asciidoc, and now that I do version my writing in an SCM (Git to be specific), I have been wondering about how to spit text. I have considered 80 characters per line (plus auto wrapping) or one sentence per line.
Automatic line wrapping is aesthetically pleasant but if I change the very beginning of a paragraph, my whole paragraph is marked as changed in diff.
The problem with the former is that if I -change the very beginning of a paragraph, -my whole paragraph is marked as changed -in diff. The problem with the latter is -that sentences tend to be quite long and -thus wrap and changes are hard to track -to even though it is easier than with the -former approach. +happen to change the very beginning of a +paragraph, my whole paragraph is marked +as changed in diff. The problem with the +latter is that sentences tend to be quite +long and thus wrap and changes are hard +to track even though it is easier than +with the former approach.
One line per sentence
One sentence per line partly solves the diff problem but sentences tend to be quite long (and thus wrap) and changes are not fully obvious to track even though it is easier than with the line wrapping approach.
-The problem with the former is that if I change the very beginning of a paragraph, my whole paragraph is marked as changed in diff. +The problem with the former is that if I happen to change the very beginning of a paragraph, my whole paragraph is marked as changed in diff. The problem with the latter is that sentences tend to be quite long and thus wrap and changes are hard to track even though it is easier than with the former approach.
One line per idea
I came across this post which proposes to have line breaks between ideas (punctuation is a good indicator but clauses is probably even better).
The problem with the former is that -if I change the very beginning of a paragraph, +if I happen to change the very beginning of a paragraph, my whole paragraph is marked as changed in diff. The problem with the latter is that sentences tend to be quite long and thus wrap
This offers the advantages of the line per sentence without its disadvantages. What's interesting is that this approach has been proposed at least as far as 1974 :) and focuses on changes rather than the initial blurb. Clever!
That's on paper at least. I will try this approach from now on and see if that works for me.
This entry describes how to install EncFS on Mac OS X with homebew without breaking other tools using osxfuse (like TrueCrypt).
I have been looking of a smart solution to encrypt a directory file by file. TrueCrypt is an awesome tool but does not play well with backups or Dropbox / Google Drive / SkyDrive like syncing: a TrueCrypt volume appears like a single big file which has to be synced entirely for each tiny change.
I just discovered this week-end EncFS. This is the exact tool I was looking for:
- encrypts a directory file by file: each file and its name is encrypted
- when one file is changed, only this encrypted file has to be synced
- support for each major platform (Windows, Mac OS X, Linux)
I did choose EncFS over eCryptfe for its cross platform support.
Note that some metadata still leak in such system (as opposed to TrueCrypt) but I can happily live with that.
Installing EncFS with homebrew without breaking everything else
It took me far too much experimentation for my taste. Basically EncFS in Homebrew is using fuse4x which is incompatible with osxfuse - the now preferred solution. TrueCrypt - that I still use for disk encryption - happens to be using osxfuse and my attempts to make osxfuse cohabit with fuse4x and TrueCrypt to cohabit with fuse4x failed.
Here is the working solution:
- install osxfuse via the
.dmginstall - I did install the MacFUSE compatibility layer for good measure
- install TrueCrypt (
- install the EncFS version compatible with osxfuse via brew
The last step requires to use a non standard recipe
brew install https://raw.github.com/jollyjinx/encfs.macosx/master/encfsmacosxfuse.rb
From what I gathered, osxfuse build is too complicated to be made a standard brew recipe. Too bad as I don't like to manage these externalities.
At the time, we are talking about:
- Max OS X 10.7.5
- OSXFUSE 2.5.6
- EncFS 1.7.4
Once installed, it is super easy to use
encfs ~/Dropbox/Private.enc/ ~/Private/ # or with a custom volume name encfs ~/Dropbox/Private.enc/ ~/Private/ -- -o volname="Dropbox private"
mounts the encrypted
~/Dropbox/Private.enc directory as
under the volume name
If you add or change files from
~/Private, you will see an encrypted equivalent under
To unmount, simply do
That's it. Happy secure data.
I was wrong in assuming a few things:
- Dropbox can do partial sync of a big file, so the whole file is not reuploaded in its entirety. Reference here.
- TrueCrypt does encrypt by block so changing one file does not change the whole TrueCrypt container file.
- Mac OS SpaceBundle does roughly like TrueCrypt and split a bundle into files of 8MB each. Reference here.
It remains that encfs is a nice tool but not for the reasons I mentioned :)
Thanks for Renaud and Jekyll for pointing these errors. Bad karma for Jekyll for not resisting the urge to express it like a dick.
I can't believe autocompletion is still so stupid in 2013. Alright, I said it, I feel better. It's actually not quite true and a lot of smart things are going on in Eclipse or IntelliJ IDEA but completion remains incredibly stupid when it comes to discovering an API and offering the most appropriate choice.
It is very important for fluent APIs tailored to offer only the APIs that make
sense in a particular context. So when the autocompletion offers you the full
monty of useless methods of
Object, it kills the hours of work an API designer
put into his fluent API and its discoverability. Seriously
wait() x 3!
I developed, help develop or gave advice on a number of fluent APIs in Java including:
- Hibernate Search and Hibernate Validator programmatic mapping API,
- an upcoming Hibernate OGM programmatic configuration API
- ShrinkWrap API
- Hibernate Search Query DSL API
So I feel very personally offended when something gets between my work and the end user.
I use Eclipse these days and the sort by relevance works only in so far as relevance is synonymous to alphabetical. In other words it sucks... but in a predictably alphabetical way.
IntelliJ IDEA is smarter as it puts in bold the methods hosted on the type and leave supertype methods in regular.
If you dig enough though, you can find a setting
Sort lookup items lexicographically
by opening up preferences and search of autocompletion. Weirdly enough, if you uncheck this box, IntelliJ IDEA does the better thing and put the elements in bold next to each others.
And that concludes this half rant, half tip, half call to action to IDE developers. Yes I know but it takes at least 3 halfs for a good cocktail :)
Comme son nom l'indique, le podcast est une interview sur un sujet précis autour d'une tasse de thé. J'aime bien ce format qui limite les interview à 15 minutes et donc garde la discussion centrée.
Au lieu de prendre votre thé ou café et de parler de The Voice ou de la 87ème rediffusion de Top Gun avec vos collègues, prenez votre balladeur et écoutez un épisode. Pour l'instant il fait ses épisodes tous les 15 jours.
Bonne chance à Eric. Garde le rythme :)