A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="fr">
  4. <!-- Has to be within the first 1024 bytes, hence before the `title` element
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>The advantages of an email-driven git workflow (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f7f7f7">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
  26. <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
  27. <!-- Documented, feel free to shoot an email. -->
  28. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  29. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  30. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  31. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  36. <script>
  37. function toggleTheme(themeName) {
  38. document.documentElement.classList.toggle(
  39. 'forced-dark',
  40. themeName === 'dark'
  41. )
  42. document.documentElement.classList.toggle(
  43. 'forced-light',
  44. themeName === 'light'
  45. )
  46. }
  47. const selectedTheme = localStorage.getItem('theme')
  48. if (selectedTheme !== 'undefined') {
  49. toggleTheme(selectedTheme)
  50. }
  51. </script>
  52. <meta name="robots" content="noindex, nofollow">
  53. <meta content="origin-when-cross-origin" name="referrer">
  54. <!-- Canonical URL for SEO purposes -->
  55. <link rel="canonical" href="https://drewdevault.com/2018/07/02/Email-driven-git.html">
  56. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  57. <article>
  58. <header>
  59. <h1>The advantages of an email-driven git workflow</h1>
  60. </header>
  61. <nav>
  62. <p class="center">
  63. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  64. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  65. </svg> Accueil</a> •
  66. <a href="https://drewdevault.com/2018/07/02/Email-driven-git.html" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <p><a href="https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/2.18.0.txt">git 2.18.0</a> has been released, and with it my first contribution to
  71. git has shipped! My patch was for a git feature which remains disappointingly
  72. obscure: <a href="https://git-scm.com/docs/git-send-email">git send-email</a>. I want to
  73. introduce my readers to this feature and speak to the benefits of using an
  74. email-driven git workflow - the workflow git was originally designed for.</p>
  75. <p>Email isn’t as sexy as GitHub (and its imitators), but it has several
  76. advantages over the latter. Email is standardized, federated, well-understood,
  77. and venerable. A very large body of email-related software exists and is equally
  78. reliable and well-understood. You can interact with email using only open source
  79. software and customize your workflow at every level of the stack - filtering,
  80. organizing, forwarding, replying, and so on; in any manner you choose.</p>
  81. <p>Git has several built-in tools for leveraging email. The first one of note is
  82. <a href="https://git-scm.com/docs/git-format-patch">format-patch</a>. This can take a git
  83. commit (or series of commits) and format them as plaintext emails with embedded
  84. diffs. Here’s a small example of its output:</p>
  85. <pre><code class="language-mail" data-lang="mail">From 8f5045c871c3060ff5f5f99ce1ada09f4b4cd105 Mon Sep 17 00:00:00 2001
  86. From: Drew DeVault &lt;sir@cmpwn.com&gt;
  87. Date: Wed, 2 May 2018 08:59:27 -0400
  88. Subject: [PATCH] Silently ignore touch_{motion,up} for unknown ids
  89. ---
  90. types/wlr_seat.c | 2 --
  91. 1 file changed, 2 deletions(-)
  92. diff --git a/types/wlr_seat.c b/types/wlr_seat.c
  93. index f77a492d..975746db 100644
  94. --- a/types/wlr_seat.c
  95. +++ b/types/wlr_seat.c
  96. @@ -1113,7 +1113,6 @@ void wlr_seat_touch_notify_up(struct wlr_seat *seat, uint32_t time,
  97. struct wlr_seat_touch_grab *grab = seat-&gt;touch_state.grab;
  98. struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id);
  99. if (!point) {
  100. - wlr_log(L_ERROR, "got touch up for unknown touch point");
  101. return;
  102. }
  103. @@ -1128,7 +1127,6 @@ void wlr_seat_touch_notify_motion(struct wlr_seat *seat, uint32_t time,
  104. struct wlr_seat_touch_grab *grab = seat-&gt;touch_state.grab;
  105. struct wlr_touch_point *point = wlr_seat_touch_get_point(seat, touch_id);
  106. if (!point) {
  107. - wlr_log(L_ERROR, "got touch motion for unknown touch point");
  108. return;
  109. }
  110. --
  111. 2.18.0
  112. </code></pre>
  113. <p>git format-patch is at the bottom of git’s stack of outgoing email features. You
  114. can send the emails it generates manually, but usually you’ll use git send-email
  115. instead. It logs into the SMTP server of your choice and sends the email for
  116. you, after running git format-patch for you and giving you an opportunity to
  117. make any edits you like. Given that most popular email clients these days are
  118. awful and can’t handle basic tasks like “sending email” properly, I strongly
  119. recommend this tool over attempting to send format-patch’s output yourself.</p>
  120. <p><img src="https://sr.ht/wmKv.jpg"></p>
  121. <p>
  122. <em>
  123. I put a notch in my keyboard for each person who ignores my advice,
  124. struggles through sending emails manually, and eventually comes around
  125. to letting git send-email do it for them.
  126. </em>
  127. </p>
  128. <p>I recommend a few settings to apply to git send-email to make your workflow a
  129. bit easier. One is <code>git config --global sendemail.verify off</code>, which turns off
  130. a sometimes-annoying and always-confusing validation step which checks for
  131. features only supported by newer SMTP servers - newer, in this case, meaning
  132. more recent than November of 1995. I started a thread on the git mailing list
  133. this week to discuss changing this option to off by default.</p>
  134. <p>You can also set the default recipient for a given repository by using a local
  135. git config: <code>git config sendemail.to admin@example.org</code>. This lets you skip a
  136. step if you send your patches to a consistent destination for that project, like
  137. a mailing list. I also recommend <code>git config --global sendemail.annotate yes</code>,
  138. which will always open the emails in your editor to allow you to make changes
  139. (you can get this with <code>--annotate</code> if you don’t want it every time).</p>
  140. <p>The main edit you’ll want to make when annotating is to provide what some call
  141. “timely commentary” on your patch. Immediately following the <code>---</code> after your
  142. commit message, you can add a summary of your changes which can be seen by the
  143. recipient, but doesn’t appear in the final commit log. This is a useful place to
  144. talk about anything useful regarding the testing, review, or integration of your
  145. changes. You may also want to edit the <code>[PATCH]</code> text in the subject line to
  146. something like <code>[PATCH v2]</code> - this can also be done with the <code>-v</code> flag as well.
  147. I also like to add additional To’s, Cc’s, etc at this time.</p>
  148. <p>Git also provides tools for the recipient of your messages. One such tool is
  149. <a href="https://git-scm.com/docs/git-am">git am</a>, which accepts an email prepared with
  150. format-patch and integrates it into their repository. Several flags are provided
  151. to assist with common integration activities, like signing off on the commit or
  152. attempting a 3-way merge. The difficult part can be getting the email to git am
  153. in the first place. If you simply use the GMail web UI, this can be difficult. I
  154. use <a href="http://www.mutt.org/">mutt</a>, a TUI email client, to manage incoming
  155. patches. This is useful for being able to compose replies with vim rather than
  156. fighting some other mail client to write emails the way I want, but more
  157. importantly it has the <code>|</code> key, which prompts you for a command to pipe the
  158. email into. Other tools like <a href="http://www.offlineimap.org/">OfflineIMAP</a> are also
  159. useful here.</p>
  160. <p>On the subject of composing replies, reviewing patches is quite easy with the
  161. email approach as well. Many bad, yet sadly popular email clients have
  162. popularized the idea that the sender’s message is immutable, encouraging you to
  163. <a href="https://en.wikipedia.org/wiki/Posting_style#Top-posting">top post</a> and leave an endlessly growing chain of replies
  164. underneath your message. A secret these email clients have kept from you is that
  165. you are, in fact, permitted by the mail RFCs to edit the sender’s message as you
  166. please when replying - a style called <a href="https://en.wikipedia.org/wiki/Posting_style#Bottom-posting">bottom posting</a>. I
  167. strongly encourage you to get comfortable doing this in general, but it’s
  168. essential when reviewing patches received over email.</p>
  169. <p>In this manner, you can dissect the patch and respond to specific parts of it
  170. requesting changes or clarifications. It’s just email - you can reply, forward
  171. the message, Cc interested parties, start several chains of discussion, and so
  172. on. I recently sent the following feedback on a patch I received:</p>
  173. <pre><code class="language-mail" data-lang="mail">Date: Mon, 11 Jun 2018 14:19:22 -0400
  174. From: Drew DeVault &lt;sir@cmpwn.com&gt;
  175. To: Gregory Mullen &lt;omitted&gt;
  176. Subject: Re: [PATCH 2/3 todo] Filter private events from events feed
  177. On 2018-06-11 9:14 AM, Gregory Mullen wrote:
  178. &gt; diff --git a/todosrht/alembic/versions/cb9732f3364c_clear_defaults_from_tickets_to_support_.py b/todosrht/alembic/versions/cb9732f3364c_clear_defaults_from_tickets_to_support_.py
  179. &gt; -%&lt;-
  180. &gt; +class FlagType(types.TypeDecorator):
  181. I think you can safely import the srht FlagType here without implicating
  182. the entire sr.ht database support code
  183. &gt; diff --git a/todosrht/blueprints/html.py b/todosrht/blueprints/html.py
  184. &gt; -%&lt;-
  185. &gt; +def collect_events(target, count):
  186. &gt; + events = []
  187. &gt; + for e in EventNotification.query.filter(EventNotification.user_id == target.id).order_by(EventNotification.created.desc()):
  188. 80 cols
  189. I suspect this 'collect_events' function can be done entirely in SQL
  190. without having to process permissions in Python and do several SQL
  191. round-trips
  192. &gt; @html.route("/~&lt;username&gt;")
  193. &gt; def user_GET(username):
  194. &gt; - print(username)
  195. Whoops! Nice catch.
  196. &gt; user = User.query.filter(User.username == username.lower()).first()
  197. &gt; if not user:
  198. &gt; abort(404)
  199. &gt; trackers, _ = get_tracker(username, None)
  200. &gt; # TODO: only show public events (or events the current user can see)
  201. Can remove the comment
  202. </code></pre>
  203. <p>Obviously this isn’t the whole patch we’re seeing - I’ve edited it down to just
  204. the parts I want to talk about. I also chose to leave the file names in to aid
  205. in navigating my feedback, with casual <code>-%&lt;-</code> symbols indicating where I had
  206. trimmed out parts of the patch. This approach is common and effective.</p>
  207. <p>The main disadvantage of email driven development is that some people are more
  208. comfortable working with email in clients which are not well-suited to this kind
  209. of work. Popular email clients have caused terrible ideas like HTML email to
  210. proliferate, not only enabling spam, privacy leaks, and security
  211. vulnerabilities, but also making it more difficult for people to write emails
  212. that can be understood by git or tolerated by advanced email users.</p>
  213. <p>I don’t think that the solution to these problems is to leave these powerful
  214. tools hanging in the wind and move to less powerful models like GitHub’s pull
  215. requests. This is why on my own platform, <a href="https://sr.ht">sr.ht</a>, I chose to
  216. embrace git’s email-driven approach, and extend it with new tools that make it
  217. easier to participate without directly using email. For those like me, I still
  218. want the email to be there so you can dig my heels in and do it old-school, but
  219. I appreciate that it’s not for everyone.</p>
  220. <p>I started working on the sr.ht mailing list service a couple of weeks ago, which
  221. is where these goals will be realized with new email-driven code review tools.
  222. My friend <a href="https://emersion.fr">Simon</a> has been helping out with a Python module
  223. named <a href="https://git.sr.ht/~emersion/python-emailthreads/">emailthreads</a> which can
  224. be used to parse email discussions - with a surprising degree of accuracy,
  225. considering the flexibility of email. Once I get these tools into a usable
  226. state, we’ll likely see sr.ht registrations finally opened to the general public
  227. (interested in trying it earlier? <a href="mailto:sir@cmpwn.com">Email me</a>). Of course,
  228. it’s all <a href="https://git.sr.ht/~sircmpwn/?search=sr.ht">open source</a>, so you can
  229. follow along and try it on your own infrastructure if you like.</p>
  230. <p>Using email for git scales extremely well. The canonical project, of course, is
  231. the Linux kernel. A change is made to the Linux kernel an average of 7 times per
  232. hour, constantly. It is maintained by dozens of veritable clans of software
  233. engineers hacking on dozens of modules, and email allows these changes to
  234. efficiently flow code throughout the system. Without email, Linux’s maintenance
  235. model would be impossible. It’s worth noting that git was designed for
  236. maintaining Linux, of course.</p>
  237. <p>With the right setup, it’s well suited to small projects as well. Sending a
  238. patch along for review is a single git command. It lands directly in the
  239. maintainer’s inbox and can be integrated with a handful of keystrokes. All of
  240. this works without any centralization or proprietary software involved. We
  241. should embrace this!</p>
  242. <hr>
  243. <p>Related articles sent in by readers:</p>
  244. <p><a href="https://begriffs.com/posts/2018-06-05-mailing-list-vs-github.html">Mailing lists vs Github</a>
  245. by Joe Nelson</p>
  246. <p><a href="https://web.archive.org/web/20180522180815/https://dpc.pw/blog/2017/08/youre-using-git-wrong/">You’re using git wrong</a> by
  247. Dawid Ciężarkiewicz</p>
  248. </article>
  249. <hr>
  250. <footer>
  251. <p>
  252. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  253. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  254. </svg> Accueil</a> •
  255. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  256. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  257. </svg> Suivre</a> •
  258. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  259. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  260. </svg> Pro</a> •
  261. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  262. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  263. </svg> Email</a> •
  264. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  265. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  266. </svg> Légal</abbr>
  267. </p>
  268. <template id="theme-selector">
  269. <form>
  270. <fieldset>
  271. <legend><svg class="icon icon-brightness-contrast">
  272. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  273. </svg> Thème</legend>
  274. <label>
  275. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  276. </label>
  277. <label>
  278. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  279. </label>
  280. <label>
  281. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  282. </label>
  283. </fieldset>
  284. </form>
  285. </template>
  286. </footer>
  287. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  288. <script>
  289. function loadThemeForm(templateName) {
  290. const themeSelectorTemplate = document.querySelector(templateName)
  291. const form = themeSelectorTemplate.content.firstElementChild
  292. themeSelectorTemplate.replaceWith(form)
  293. form.addEventListener('change', (e) => {
  294. const chosenColorScheme = e.target.value
  295. localStorage.setItem('theme', chosenColorScheme)
  296. toggleTheme(chosenColorScheme)
  297. })
  298. const selectedTheme = localStorage.getItem('theme')
  299. if (selectedTheme && selectedTheme !== 'undefined') {
  300. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  301. }
  302. }
  303. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  304. window.addEventListener('load', () => {
  305. let hasDarkRules = false
  306. for (const styleSheet of Array.from(document.styleSheets)) {
  307. let mediaRules = []
  308. for (const cssRule of styleSheet.cssRules) {
  309. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  310. continue
  311. }
  312. // WARNING: Safari does not have/supports `conditionText`.
  313. if (cssRule.conditionText) {
  314. if (cssRule.conditionText !== prefersColorSchemeDark) {
  315. continue
  316. }
  317. } else {
  318. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  319. continue
  320. }
  321. }
  322. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  323. }
  324. // WARNING: do not try to insert a Rule to a styleSheet you are
  325. // currently iterating on, otherwise the browser will be stuck
  326. // in a infinite loop…
  327. for (const mediaRule of mediaRules) {
  328. styleSheet.insertRule(mediaRule.cssText)
  329. hasDarkRules = true
  330. }
  331. }
  332. if (hasDarkRules) {
  333. loadThemeForm('#theme-selector')
  334. }
  335. })
  336. </script>
  337. </body>
  338. </html>