<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Scott Chacon</title><description>Writing from Scott Chacon — Git, languages, technology, and life.</description><link>https://scottchacon.com/</link><language>en-us</language><item><title>My new Unfurler tool</title><link>https://scottchacon.com/2026/08/05/unfurler/</link><guid isPermaLink="true">https://scottchacon.com/2026/08/05/unfurler/</guid><description>A little tool that shows how a shared URL will look when it&apos;s unfurled in Slack and Twitter.</description><pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A long time ago, Twitter used to have a little crappy part of their docs site where you could put in a URL and it would show you what the unfurl would look like before you actually post the URL.&lt;/p&gt;
&lt;p&gt;For some reason, they removed that. I have never understood why.&lt;/p&gt;
&lt;p&gt;I often want to see what the unfurl of some new website or page I’m working on will look when I share it. Before I share it.&lt;/p&gt;
&lt;p&gt;Well, thanks to the lovely people building LLMs for us, I’ve spend less time building it than I have writing this blog post already.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/assets/images/stuff/unfurl.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;Check it out at &lt;a href=&quot;https://unfurler.dev&quot;&gt;unfurler.dev&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Fable, or whatever built it for me, added some pretty cool stuff too. It breaks down all the fields, if it sees them or not. It crawls it with the User-Agent that the service does so you can see if it’s needlessly blocked. It shows you what’s broken and gives you a clipboard thingy you can give to an agent to fix it.&lt;/p&gt;
&lt;p&gt;You can find the source at &lt;a href=&quot;https://github.com/schacon/unfurl&quot;&gt;schacon/unfurl&lt;/a&gt; and if anything isn’t what you expect, just drop me an issue.&lt;/p&gt;</content:encoded></item><item><title>Git Wire Protocol v2: A Deep Dive</title><link>https://scottchacon.com/2026/07/29/git-wire-protocol-v2/</link><guid isPermaLink="true">https://scottchacon.com/2026/07/29/git-wire-protocol-v2/</guid><description>How Git&apos;s wire protocol was quietly rebuilt for the age of huge repos, load-balanced servers, and CDNs — one capability at a time.</description><pubDate>Wed, 29 Jul 2026 00:00:00 GMT</pubDate><content:encoded>How Git&apos;s wire protocol was quietly rebuilt for the age of huge repos, load-balanced servers, and CDNs — one capability at a time.</content:encoded></item><item><title>On Using Flags to Represent Languages 🇯🇵 🇬🇧 🇫🇷</title><link>https://scottchacon.com/2024/10/19/on-using-flags-to-represent-languages/</link><guid isPermaLink="true">https://scottchacon.com/2024/10/19/on-using-flags-to-represent-languages/</guid><description>Why national flags remain the most universally recognizable way to represent languages in an interface — and where they fall short.</description><pubDate>Sat, 19 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;figure&gt;
  &lt;img src=&quot;/assets/images/flags-unsplash-ferl.jpg&quot; alt=&quot;A wall of national flags&quot;&gt;
  &lt;figcaption&gt;Photo by Dominik Ferl on Unsplash&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;(This was an internal whitepaper for Chatterbug that I wrote when we were debating the use of flags as an iconic representation of languages in our app)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Many websites and other user interfaces over the years, both online and offline, have faced a very interesting usability challenge concerning how to represent spoken languages with iconography.&lt;/p&gt;
&lt;p&gt;From websites to flight attendants to tour guides, what is a good way to communicate a language quickly, unambiguously and language agnostic?&lt;/p&gt;
&lt;h2 id=&quot;whats-awesome-about-flags&quot;&gt;What’s Awesome About Flags&lt;/h2&gt;
&lt;p&gt;It’s interesting in that it is in many cases actually much easier than normal iconography problems in that with flags there exists an internationally and language-agnostic icon that in many, many cases shares a word and connotation with a single spoken language.&lt;/p&gt;
&lt;p&gt;In normal iconography, like for computer user interfaces, designers have to figure out which image would best mean “home” or “back” to people of different cultures and this can be difficult.&lt;/p&gt;
&lt;p&gt;Homes can look different in different places, time can move in different directions culturally, etc.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/flags-recognizability.png&quot; alt=&quot;Chart of global flag recognizability&quot;&gt;
  &lt;figcaption&gt;One datapoint on global flag recognizability&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Also, if you put clarifying text under or next to an icon, you have to choose a language, where with flags, they’re the same flags in every language. For example, China doesn’t have a different flag in France than it does in the United States, even though the country itself has different names in these places.&lt;/p&gt;
&lt;p&gt;In the cases where there exists a country that shares the same word in most cultures with the language we’re targeting (ie: “England” for “English”, “Spain” for “Spanish”, etc), you can only use the icon and can avoid the issue of having to know beforehand what language the user speaks. It’s completely universal and unambiguous in a way that almost no other iconography can be.&lt;/p&gt;
&lt;p&gt;Flags are also highly recognizable and in many cases, totally unambiguous. If you want to represent Italian as a single image, the flag of Italy would be instantly and unambiguously recognizable to over 80% of people who see it (according to survey data from Sporacle from 3M people who took a “flags of the world” quiz).&lt;/p&gt;
&lt;h2 id=&quot;whats-not-awesome-about-flags&quot;&gt;What’s Not Awesome About Flags&lt;/h2&gt;
&lt;p&gt;So, this sounds great. For all four languages we offer, we have a single image we can use to immediately and unambiguously bring to mind the name of the language we’re trying to represent and is recognizable by more than 80% of everyone who sees the image, regardless of their cultural background or natively spoken language. This is almost unprecedented in UX design, it’s an amazing miracle.&lt;/p&gt;
&lt;p&gt;However, there are still issues, because flags specifically represent countries, not languages. Not all countries officially speak only one language and many languages are spoken officially by multiple countries.&lt;/p&gt;
&lt;p&gt;This introduces three major problems.&lt;/p&gt;
&lt;p&gt;The first is that for languages that are spoken over wide geographical areas, especially across country borders, there are major linguistic differences that split the language into dialects while often not technically dividing the language into distinct language families. This means that the French spoken in Quebec and France may be different in significant ways, though they are both still called “French”. To use the flag of France can infer a specific “France the country” dialect of French, which may not be what the user speaks or wants to learn.&lt;/p&gt;
&lt;p&gt;The second major problem are potential political issues that become inferred with the usage of a specific flag, especially since many languages are spoken in multiple countries due to colonialism or historical war or conquest.&lt;/p&gt;
&lt;p&gt;There are many examples of this, but let’s take the least politically sensitive of all of them, the case of me as an American having to click on the British flag because of its association with England and thus “English”. The language of my country’s historical colonial oppressors. (Queue me dumping my proverbial tea into the metaphorical harbor. Take that, you bloody Torries!) But seriously, for countries with heavy political issues, this can be an uncomfortable and unfortunate association to be constantly reminded of when using a product.&lt;/p&gt;
&lt;p&gt;The third problem is that some languages do not have the unambiguous namesake country that all of the languages we currently offer have. Swahili or Arabic, for example, are not named after modern countries and so do not enjoy the reverse reference that we take advantage of for French or Spanish, which have one clear namesake country and vice-versa.&lt;/p&gt;
&lt;h2 id=&quot;why-use-an-icon-at-all&quot;&gt;Why Use an Icon At All&lt;/h2&gt;
&lt;p&gt;Well, maybe the way out of this conundrum is to simply not use icons. We can use words or ISO codes like ‘fr’ or ‘en’. Something that specifically stands for the language itself. Forget icons.&lt;/p&gt;
&lt;p&gt;Well, in this case we have two options.&lt;/p&gt;
&lt;p&gt;We can choose the word in the language we’re trying to represent, so “Deutsch” as opposed to “German”, or “日本語” or “Nihongo” instead of “Japanese”. This has the rather large downside of people new to the language not being able to understand how to find or identify it. For instance, to choose it to start studying. Quick, what language is “Svensk”? Or “українська”? Well, now if you wanted to learn Swedish or Ukranian, you’re screwed.&lt;/p&gt;
&lt;p&gt;Using ISO language codes is even worse, because it’s in the target language and it’s abbreviated. Now you need to know how it’s said in the language and how it’s shortened. Quick, what is “Chinese” in two-letter ISO codes? It’s “zh” for 中文 (Zhōngwén). OK, what’s Japanese, knowing from the last paragraph that in Japanese, the language is pronounced にほんご (Nihongo). Ooops, turns out you’re wrong, it’s not “ni”. It’s actually “jp”, the random shortening of the English way to say the language, because hilariously, standards aren’t standardized.&lt;/p&gt;
&lt;p&gt;If you’re choosing the language you speak from a list of languages, then fine, we can use the native target language (Although then alphabetizing becomes interesting — is 日本語 under “J” or under “N”?) Otherwise, the target language is often a poor choice for a menu. The ISO code is almost always an even worse choice.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/flags-bork-hacker.png&quot; alt=&quot;A language dropdown listing options including Bork Bork Bork and Hacker&quot;&gt;
  &lt;figcaption&gt;What the hell is &quot;Bork Bork Bork&quot; and &quot;Hacker&quot;?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;OK, so then how about we use the name of the target language in the language that the user speaks. So instead of “Deutsch”, we use “German” for English speakers and “Allemand” for French speakers, etc.&lt;/p&gt;
&lt;p&gt;The problem here becomes when the website or UX does not know what language the user speaks. Now you actually need two different inputs, one that indicates which language we think you speak, and another dynamic one that is built from the first choice. Of course, in the case of dynamic websites, this is possible. In the case of printed name tags or mixed audiences, this is not an option.&lt;/p&gt;
&lt;p&gt;Often online this can be helped with the set browser language, but even then it’s problematic.&lt;/p&gt;
&lt;p&gt;Let’s say someone has their browser set to Polish and we don’t have the names of everything in Polish. Maybe they also speak German or English or Russian or whatever, but there is no way for us to know that. Some websites don’t even try to guess and just set the language to a default for where geographically you’re hitting the site from, which is horrible if you’re travelling. Because now you have to figure out what part of the site says “What is your language”.&lt;/p&gt;
&lt;p&gt;I will tell you know, if you don’t speak German, you will never be able to guess that “Ausgangssprache” means “The language you’re coming from”, nor will you be able to tell that “Deutsch” means “German”. Because nobody in the world uses “Deutsch” to mean German except Germans. This means you can’t even identify which part of the website you’re looking at could be involved in switching your language.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/flags-ausgangssprache.png&quot; alt=&quot;A German-language interface labeled Ausgangssprache: Deutsch&quot;&gt;
  &lt;figcaption&gt;But my Ausgangssprache isn&apos;t Deutsch!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;However, even if we don’t recognize the words, flags make this not only pretty easy to identify what language they’re talking about, but that they’re talking about languages at all.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/flags-polish-list.png&quot; alt=&quot;A list of languages written in Polish, each next to its flag&quot;&gt;
  &lt;figcaption&gt;Bet you know what these languages listed in Polish mean. Thanks Flags!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This is the other side benefit of using flags, if you see nothing else on the page that you can recognize, the flag is a flag and that gives you information that this is where language (or perhaps country, but that also indicates language in these contexts) is handled in some way.&lt;/p&gt;
&lt;p&gt;This is true in other contexts as well. For example, if I see a flight attendant with two flags on their badge, and one of them is not the German flag, I can be pretty sure that this person doesn’t speak German, because I immediately know this means “languages” and I don’t see the German one. But if it lists out “日本語, Svensk”, I am essentially given zero information unless I happen to know one of those languages.&lt;/p&gt;
&lt;h2 id=&quot;ok-so-how-about-newer-better-icons-for-languages&quot;&gt;OK, So How About NEWER BETTER Icons for Languages?&lt;/h2&gt;
&lt;p&gt;I mean, the ideal would obviously be that we have some iconic symbol for each language itself that is independent of a single country, or countries at all. To some degree, we actually have this with the Arab League, where there exists a flag that represents 22 Arab speaking countries in the world and is generally recognizable as it draws from the flag designs and colors of several of its member countries. However, we don’t have this for any other language I can think of.&lt;/p&gt;
&lt;p&gt;We could perhaps make up a new set, which would be beautiful I’m sure, but there is no point to that because then we would have to educate several billion people around the world what all of these new icons look like, which international events and travelling and people using flags to represent languages for decades has already done with this flag-based set.&lt;/p&gt;
&lt;p&gt;Everyone constantly sees flags on the badges of people giving tours of a city, or working on an airplane, or next to names during an olympic event. People have already been trained in what language dozens of flags represent and can recognize them immediately and from a distance. They convey information very powerfully and very quickly.&lt;/p&gt;
&lt;h2 id=&quot;what-about-other-images&quot;&gt;What About Other Images?&lt;/h2&gt;
&lt;p&gt;The problem with other images to represent languages is that they essentially have to be stereotypical for a large percentage of people to draw the correct association. Having a sombrero or Eiffel tower or something is both still heavily country-specific and now goes even further into stereotyping that has similar issues to flags.&lt;/p&gt;
&lt;h2 id=&quot;so-what-will-we-do&quot;&gt;So, What Will We Do?&lt;/h2&gt;
&lt;p&gt;I think that Chatterbug should do what’s best for the customer. As different strategies are effective in different situations, our design language should respond to what is best for the customer in each situation.&lt;/p&gt;
&lt;p&gt;If you see a flag on flight attendant’s badge, you can tell, from halfway down the airplane, exactly what languages they speak. Think of how difficult that would be to accomplish with any of the other methods listed here and how much more quickly, unambiguously and far-away this is accomplished with a couple of small flag icons than with any other method. This speaks to the flags usability power as an icon — compact, fast to recognize, no need to translate and visually pleasing.&lt;/p&gt;
&lt;p&gt;In situations that require these attributes, we should always use the flag iconography.&lt;/p&gt;
&lt;p&gt;If we know the user’s language, we know the name of the languages in that student’s language, and there is not the possibility of a mixed audience, then we should use the full name of the language in the student’s language (ie “German”), next to the flag icon.&lt;/p&gt;
&lt;p&gt;If we do not know the user’s language, we should use the names of the languages in English (as they are probably the most universally recognized). Again, along with flag iconography as it can be helpful to disambiguate that we’re speaking about languages.&lt;/p&gt;
&lt;p&gt;In the special case that we’re specifically asking the user to identify for us the language they speak, we should use the native language (“Deutsch”, “日本語”, “Svensk”) with no flag.&lt;/p&gt;
&lt;h2 id=&quot;ok-but-which-flags&quot;&gt;Ok, But Which Flags?&lt;/h2&gt;
&lt;p&gt;Of course, should we decide that flag iconography is helpful and should be used, which flag do we choose to represent a language? It should not be subjective and there should be a single one consistently used to represent each distinct language we teach.&lt;/p&gt;
&lt;p&gt;I propose that the criteria be the country or flag most commonly associated with the origin or name of the language. This will cover most languages I can think of for quite some time. It also avoids difficult things like “country with the most speakers” or “where people want to travel to speak this” or other more confusing or highly subjective criteria. This will still become problematic if we get into teaching both Urdu and Punjabi (do we use the Pakistani flag for both?), but that’s actually probably fine (to use the same flag for a few languages) and not a problem we’ll hit for quite some time most likely.&lt;/p&gt;
&lt;p&gt;In reality, I think it’s very easy to basically ask anyone what the most commonly associated flag for almost any language is and get the same honest answer from basically everyone.&lt;/p&gt;</content:encoded></item><item><title>Hungarian Desks: Assigning Office Seats with the Hungarian Algorithm</title><link>https://scottchacon.com/2020/08/07/hungarian-desks/</link><guid isPermaLink="true">https://scottchacon.com/2020/08/07/hungarian-desks/</guid><description>How we replaced our office desk lottery with a weighted-bet form and the Hungarian algorithm to compute the seating that makes everyone happiest.</description><pubDate>Fri, 07 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In nearly any company, but especially tech companies, how does one handle seating arrangements? Often this is done by department, but for smaller companies without so much space — and especially now with more flexible work-from-home and partial remote policies — a more fluid use of desk space is often preferable.&lt;/p&gt;
&lt;p&gt;A while ago, I wrote a little tool to solve this in an interesting way, which I’ve put up on GitHub as &lt;a href=&quot;https://github.com/schacon/hungarian-desks&quot;&gt;schacon/hungarian-desks&lt;/a&gt;. Here’s the thinking behind it.&lt;/p&gt;
&lt;h2 id=&quot;the-problem-with-seating&quot;&gt;The Problem with Seating&lt;/h2&gt;
&lt;p&gt;At Chatterbug, and at GitHub before this, my companies have always had a pretty open seating policy where people could sit pretty much wherever they wanted in the office. Sometimes teams sat near each other, but often they were remote anyhow, so that wasn’t possible. In fact, with remote companies it’s often better to seat people who &lt;em&gt;are&lt;/em&gt; in the office apart from each other, so that they communicate asynchronously as well.&lt;/p&gt;
&lt;p&gt;In fact, mixed seating is arguably good for cross-company communication and serendipitous interactions. At GitHub we even had a day every few months where we would play Musical Desks, where everyone who wanted to would pick up and move somewhere new. This is easier if everyone does it at the same time, so there are desks opening up at the same time.&lt;/p&gt;
&lt;h2 id=&quot;the-random-method&quot;&gt;The Random Method&lt;/h2&gt;
&lt;p&gt;At Chatterbug when we moved into a new space, we needed to determine how to allot seats and the first version was a simple lottery. Each person’s name is drawn and they choose what seat they want in the order drawn.&lt;/p&gt;
&lt;p&gt;This has the fairness of randomness to it, but it’s not actually ideal for optimizing the assignments. Someone could, for example, not care too much and choose a seat semi-randomly that someone else really wanted.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/hungarian-lottery.svg&quot; alt=&quot;A seat lottery where the first name drawn takes the seat another person wanted most&quot;&gt;
  &lt;figcaption&gt;In the lottery, whoever&apos;s name is drawn first picks first — so someone who doesn&apos;t much care can grab the one seat another person really wanted.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;the-hungarian-method&quot;&gt;The Hungarian Method&lt;/h2&gt;
&lt;p&gt;We tried to solve this issue with version 2 of the seat picker. In this version, we have people give us some information about a few top choices. Also, instead of just ranking them, we have them “bet” on the choices. This way if you don’t really care or want to sit in a specific area, you can put the same bet on all 4 choices. Or if you really care, you can put most of your bet on a single choice.&lt;/p&gt;
&lt;p&gt;This allows us to determine a “happiness” with a set of seats. We assign each user/seat combination a value from &lt;code&gt;0.0&lt;/code&gt; to &lt;code&gt;1.0&lt;/code&gt;, with zero being totally unhappy (ie, a random seat not in their list) and &lt;code&gt;1.0&lt;/code&gt; being totally happy (ie, the choice they bet the most on). We weight the user’s choices to be internally relative, so if one user bets &lt;code&gt;8&lt;/code&gt; on one seat and &lt;code&gt;4&lt;/code&gt; on another, they would get scores of &lt;code&gt;1.0&lt;/code&gt; and &lt;code&gt;0.5&lt;/code&gt; for those seats, since one is half as valuable to the user as the top choice. If the user bets the same value on all 4 choices, each seat is assigned &lt;code&gt;1.0&lt;/code&gt; since the user would be just as happy with any of them.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/hungarian-bets.svg&quot; alt=&quot;Two people&amp;#x27;s bets across four seat choices normalized into happiness scores from 0 to 1&quot;&gt;
  &lt;figcaption&gt;Each person spreads a fixed budget of &quot;bets&quot; across their top four choices. Bets are scored relative to that person&apos;s own biggest bet, giving a happiness value from 0 to 1 — so betting evenly (happy anywhere) and betting big on a single seat both work.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Now we essentially have the data to do a cost matrix that we can treat as a classical &lt;a href=&quot;https://en.wikipedia.org/wiki/Assignment_problem&quot;&gt;assignment problem&lt;/a&gt;, since the sets of seats and people are essentially a weighted bipartite graph. We can invert the values of the happiness score (so &lt;code&gt;0&lt;/code&gt; is totally happy — no cost, and &lt;code&gt;1&lt;/code&gt; is totally unhappy, the highest cost) and use that cost matrix as the input to an implementation of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Hungarian_algorithm&quot;&gt;Hungarian algorithm&lt;/a&gt; to come up with a set of assignments that optimizes for low cost, and thus, highest total assignment happiness.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/hungarian-cost-matrix.svg&quot; alt=&quot;A people-by-desks happiness matrix with the optimal one-per-row assignment highlighted&quot;&gt;
  &lt;figcaption&gt;Treating people and desks as a weighted bipartite graph, the Hungarian algorithm picks exactly one desk per person to maximize &lt;em&gt;total&lt;/em&gt; happiness — here Ada gives up her own top pick (desk A, worth 0.90 to her) so Cleo can take it, because that trade makes the whole team happier.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;the-data-and-code&quot;&gt;The Data and Code&lt;/h2&gt;
&lt;p&gt;Now we just need everyone in the company to fill out a form telling us their top choices and bets, so we can calculate the optimal assignments.&lt;/p&gt;
&lt;p&gt;We decided to try the easiest route, which is to have everyone fill out an Airtable form, which gives us a nice simple database of all the users and their choices that is easily API accessible. Then we wrote a script to pull those choices down, do the math, make the assignments and upload the assignments (as well as the calculated “happiness” score for each user).&lt;/p&gt;
&lt;p&gt;The code is on GitHub at &lt;a href=&quot;https://github.com/schacon/hungarian-desks&quot;&gt;schacon/hungarian-desks&lt;/a&gt; if you’d like to try it or adapt it for your own office.&lt;/p&gt;</content:encoded></item><item><title>MIT Scientists Prove Adults Learn Language to Fluency Nearly as Well as Children</title><link>https://scottchacon.com/2018/05/03/mit-adults-learn-language/</link><guid isPermaLink="true">https://scottchacon.com/2018/05/03/mit-adults-learn-language/</guid><description>A viral MIT study got badly misreported. The data actually shows adults can reach near-native mastery — here&apos;s what it really says.</description><pubDate>Thu, 03 May 2018 00:00:00 GMT</pubDate><content:encoded>&lt;figure&gt;
  &lt;img src=&quot;/assets/images/mit-header.jpg&quot; alt=&quot;A lecture hall full of students&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;Scott Chacon is CEO of the online language learning company Chatterbug.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This week a new paper was published in the journal Cognition titled “A Critical Period for Second Language Acquisition” that used a new, viral Facebook-quiz-powered method of gathering a huge linguistic dataset to provide new insights into how human beings learn language and what effect age has on that process.&lt;/p&gt;
&lt;p&gt;In a nutshell, this team found that if you start learning a language before the age of 18, you have a much better likelihood of obtaining a native-like mastery of the language’s grammar than if you start later. This is a much older age than has been generally assumed and is really interesting for reasons I’ll get into a bit later.&lt;/p&gt;
&lt;p&gt;This data has also given us a really amazing insight into language learning in general and shows that adults of any age can obtain incredible mastery nearly as quickly as children.&lt;/p&gt;
&lt;p&gt;Unfortunately, a number of journalists have misinterpreted this paper badly, resulting in a lot of articles falsely stating things as embarrassing and misleading as “Becoming fluent in another language as an adult might be impossible”, when in fact the opposite is shown. If you see an article saying that you need to start learning a language before you’re 10 to become fluent, be assured that it’s simply lazy reporting. The truth is much more interesting and encouraging.&lt;/p&gt;
&lt;p&gt;We know this, because in an incredible move, the researchers have released the entire dataset they based this paper off of, letting us take a look. Let’s go through some interesting things that this amazing data shows us about learning languages and why it should motivate you to try.&lt;/p&gt;
&lt;h2 id=&quot;many-late-learners-become-native-like&quot;&gt;Many late learners become native-like&lt;/h2&gt;
&lt;p&gt;Looking through the data, it’s quite clear that there is a statistical advantage to starting your learning earlier. If you compare the average score of learners who started at different ages, there is a clear advantage to having started earlier.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/mit-accuracy-by-age.png&quot; alt=&quot;Graph of accuracy as a function of years of experience by age of first exposure&quot;&gt;
  &lt;figcaption&gt;Accuracy as a function of years of experience, by age of first exposure for immersion learners (A) and non-immersion learners (B). Red: monolinguals. Orange: AoFE &amp;#x3C; 11. Green: 10 &amp;#x3C; AoFE &amp;#x3C; 21. Blue: AoFE &gt; 20. (Image from original paper)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;However, looking more closely at the data for the students who started learning after the age of 20, there are a lot of late learners who outperformed many native English speakers.&lt;/p&gt;
&lt;p&gt;First, we need to specify what “native-like” performance on this quiz is. Looking at the quiz results of the group of users who are tagged as native English speakers, we have a range of scores from 100% (which 12% of native speakers scored) down to scoring 90% for the bottom 5% of native speakers. To me, this indicates that scoring anything above a 90% on the test means you’re performing at least as well as many native speakers would.&lt;/p&gt;
&lt;p&gt;Many articles have breathlessly stated that it’s just impossible to achieve native-like mastery if you start after the age of 10 (or 18, depending on the article), but is that true? Certainly on average the later learner seems to have a rarer time getting there, but is it impossible?&lt;/p&gt;
&lt;p&gt;The data tells us that it’s not. On average less likely, certainly, but there are thousands of people who took this quiz, got a score in the range that a native speaker would, and started learning the language after the age of 20.&lt;/p&gt;
&lt;h2 id=&quot;thousands-of-adults-who-started-learning-after-20-years-old-scored-in-a-native-level-range&quot;&gt;Thousands of adults who started learning after 20 years old scored in a native-level range&lt;/h2&gt;
&lt;p&gt;Instead of looking at the median of this late-learning group, let’s just look at the top quartile of the speakers who started learning after the age of 20. We can graph that adjusted by how many years since they started studying the language.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/mit-top-quartile.png&quot; alt=&quot;Graph of the top quartile of learners who started after age 20&quot;&gt;
  &lt;figcaption&gt;The top quartile of results of learners who started after the age of 20, by number of years of exposure, showing that at around 8–10 years of exposure, many learners who started well into adulthood do just as well as many native speakers. All results above the 0.9 line are in the native results range.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Here we can see that many of the later learners score easily within the range that native speakers do (above the 0.9 line). In fact, if we graph the results of the top quartile of after-20 learners with the median scores of the other groups (those who started learning before 5, before 10 and before 20 years old), it doesn’t look much different.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/mit-compare-groups.png&quot; alt=&quot;Comparing the top quartile of over-20 learners with younger starting groups&quot;&gt;
  &lt;figcaption&gt;Comparing the performance of the top quartile of over-20 learners with students who started learning before 5 (red), before 10 (yellow) and before 20 (green)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Given the same amount of time, the top quarter of learners from the over-20 group do just as well as the average of those who started before 10.&lt;/p&gt;
&lt;p&gt;(After about 20 years of learning exposure, the over-20 group line gets pretty wobbly, since we have less and less data to work with — starting at 20 and having 20 years of exposure makes the youngest group there at least 40 years old).&lt;/p&gt;
&lt;h2 id=&quot;comparing-apples-to-apples&quot;&gt;Comparing apples to apples&lt;/h2&gt;
&lt;p&gt;But why would we do this? Shouldn’t we compare the medians of all of the groups, like the very first graph? Shouldn’t we compare apples to apples — it’s not really fair to compare the median of one group with the best of another, right?&lt;/p&gt;
&lt;p&gt;Well, the problem is that there is no way to compare apples to apples with this dataset, and the authors point this out.&lt;/p&gt;
&lt;p&gt;This is because the “drop” in learning advantage happens at 17 or 18 years old according to this paper, but why is that? What happens at 18 years old? Is it that there is some incredible and magical brain-change that happens? Or is it that people’s lives fundamentally change on average — you go to college, you enter the workforce, you move out of your parents house, etc?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“For instance, it remains possible that the critical period is an epiphenomenon of culture: the age we identified (17–18 years old) coincides with a number of social changes, any of which could diminish one’s ability, opportunity, or willingness to learn a new language. In many cultures, this age marks the transition to the workforce or to professional education, which may diminish opportunities to learn.” — from &lt;em&gt;A Critical Period&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you start “studying” a language at the age of 5, you’re not sitting down with a book and explicitly learning the language for an hour a day. You’re almost certainly in a classroom environment where that language is spoken, possibly for several hours per day. If you start learning a language after you’re 20 years old, you almost certainly cannot be in a classroom for several hours per day.&lt;/p&gt;
&lt;p&gt;This brings up a big problem with the interpretation of this data. It gives us a lot of information, but it doesn’t give us the most important thing, which is the total amount of exposure that these students have had. I would argue that on average, your exposure per day to a language if you start after the age of 20 is going to be way lower than if you start when you’re 5.&lt;/p&gt;
&lt;p&gt;If you’re in an English speaking school for 5 hours a day as a kid and your parent is studying the same language for an hour a day while you’re there and the kid learns 5 times faster than the parent, is it fair to then conclude that kids learn better than adults?&lt;/p&gt;
&lt;p&gt;It’s highly possible that this learning difference by age is not due to some magic change in brain plasticity, but simply that adults don’t have as much time to be exposed as children and often hit a point where it stops being helpful to improve after a while. They become totally fluent at this slower pace and reaching native-level mastery provides little additional advantage. Maybe it’s not that it’s harder for older learners or that they’re not capable, maybe it’s just that they don’t have the same opportunity.&lt;/p&gt;
&lt;p&gt;Comparing the top quartile of the post-20 group is a simple guess to isolate a cohort of language learners who may have had more opportunity for exposure. I would guess that the post-20 learners would have much less uniform types of opportunities than children, whose experiences are probably much more similar in nature. It’s certainly not perfect, but it does indicate that there is a cohort in the post-20 crowd that does very well.&lt;/p&gt;
&lt;h2 id=&quot;this-quiz-is-incredibly-hard&quot;&gt;This quiz is incredibly hard&lt;/h2&gt;
&lt;p&gt;There are a couple of other interesting things to note here. The first is how hard this quiz is. Even for the learners who started studying the language as a young child, it takes at least 7–8 years of exposure before any of the groups are consistently scoring above a 90% on this test.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/mit-quiz-example.png&quot; alt=&quot;An example grammar quiz question&quot;&gt;
  &lt;figcaption&gt;Which image is most correct for this sentence?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This test is not about fluency, it’s about highly pedantic grammatical accuracy. If you got through this entire test at all, you’re probably pretty close to basic fluency.&lt;/p&gt;
&lt;p&gt;If you’re thinking that this paper provides some reason why you’re not fluently speaking French after your 3 months of using some language-game app, you are wrong. Children won’t learn a language masterfully that way either.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Studies that compare children and adults exposed to comparable material in the lab or during the initial months of an immersion program show that adults perform better, not worse, than children (Huang, 2015; Krashen, Long, &amp;#x26; Scarcella, 1979; Snow &amp;#x26; Hoefnagel-Höhle, 1978), perhaps because they deploy conscious strategies and transfer what they know about their first language.” — from &lt;em&gt;A Critical Period&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Adults are actually better in many ways at learning a language up to a point of general fluency, but getting to where you can answer the most subtle of grammatical points with the accuracy of a native speaker takes a decade no matter how old you are when you start.&lt;/p&gt;
&lt;p&gt;That so many adults who started learning a language after 20 years old (or even later) did so well on this test should be encouraging. If you want to put in the effort, it’s entirely possible to perform at a native level on an incredibly difficult test like this — thousands of people did just that.&lt;/p&gt;
&lt;h2 id=&quot;it-doesnt-matter-what-language-you-come-from&quot;&gt;It doesn’t matter what language you come from&lt;/h2&gt;
&lt;p&gt;This paper also gives us some really interesting insights from a broad range of backgrounds. They had nearly 3/4 million people take the quiz and got demographic data from everyone.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/mit-demographics.png&quot; alt=&quot;Chart of learner demographics by age of first exposure and native language&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;The first thing to notice is how little data they actually were able to get from people who started after the age of 20. It’s a tiny fraction of their dataset.&lt;/p&gt;
&lt;p&gt;The other thing is how many different languages learners came from.&lt;/p&gt;
&lt;p&gt;It’s common to say “this language is hard to learn” or “that language is so easy”, but this data showed that these things may not be true. They found that there was little difference in the learning speeds or ultimate attainment of English coming from any linguistic background.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“In fact, the differences across language groups were small (see Fig. S14) and generally not reliable.” — from &lt;em&gt;A Critical Period&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/mit-fig-s14.png&quot; alt=&quot;Figure S14: boxplots overlaid on violin plots by language family&quot;&gt;
  &lt;figcaption&gt;Fig S14, showing Boxplots (in red) overlayed on violin plots (white) for asymptotic immersion bilinguals overall (left) and for five language families (right) (only part B: age of first exposure 1–5)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;studying-a-language-for-a-year-can-make-you-quite-fluent&quot;&gt;Studying a language for a year can make you quite fluent&lt;/h2&gt;
&lt;p&gt;Finally, the thing I would like everyone to take away from this is how good adults can be at language learning. It may be harder for us to get to where we could pass for a native, but that’s probably pretty obvious and not why most of us start learning a language, right? You’re not trying to be a spy.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/mit-closeup.png&quot; alt=&quot;Close-up of the first few years of exposure&quot;&gt;
  &lt;figcaption&gt;Close-up of our previous graph, focusing on the results of the first few years of exposure.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Take a look at my previous graph of the various language groups. You can see that even after a year of studying, the 20+ year old start group is commonly scoring 80-85% on this incredibly difficult grammar test.&lt;/p&gt;
&lt;p&gt;Sure, it takes another 10 years to get to where you might pass for a native on this test, but look at what you can do in a single year!&lt;/p&gt;
&lt;p&gt;Don’t use poorly reported studies like this convince you not to try learning a language. The truth is that you’re almost certainly very good at it. People consistently learn new languages in a year or two from no knowledge to very capable, fluent levels and in my personal experience, much faster than children given the same amount of time.&lt;/p&gt;
&lt;p&gt;That last mile, getting from fluent to native-like, is statistically more difficult, as this paper shows, but like any good 80/20 rule, the first 80% of the results takes 20% of the time.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“What is remarkable about language is that we are (nearly) all extremely good at it, including adult learners.” — from &lt;em&gt;A Critical Period&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can become fluent in a language in a few years of work, I see it all the time. This paper further proves that even starting much later in life, it’s still possible (if not as common) to reach incredibly high levels of mastery.&lt;/p&gt;</content:encoded></item><item><title>How to Talk about Language Learning</title><link>https://scottchacon.com/2017/12/15/how-to-talk-about-language-learning/</link><guid isPermaLink="true">https://scottchacon.com/2017/12/15/how-to-talk-about-language-learning/</guid><description>The six CEFR stages every learner passes through from first word to fluency — what you can do at each, and roughly how long each takes.</description><pubDate>Fri, 15 Dec 2017 00:00:00 GMT</pubDate><content:encoded>&lt;figure&gt;
  &lt;img src=&quot;/assets/images/cefr-hero.jpg&quot; alt=&quot;Illustration of the stages of language learning&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;One of the most difficult parts of learning a new language, especially if you’ve never done so before, is having a good idea of just what exactly it is that you’re in for. Here we’ll take a look at the various stages of language learning and how many European language teachers talk about these stages.&lt;/p&gt;
&lt;p&gt;To many people (Americans especially) you either know: a) none of a language, b) a little of a language or c) you are fluent. There is actually, however, a vast spectrum of language learning stages that everyone goes through on a journey from learning their first word to being comfortably fluent.&lt;/p&gt;
&lt;p&gt;I feel that knowing how to talk about language learning is valuable when you’re planning to tackle a new language. There is a long journey between knowing a little of a language and being fluent. If you’re not prepared for this journey, it can seem like you’re not making progress on the way.&lt;/p&gt;
&lt;p&gt;So let’s take a look at that what this trip will look like, what you can expect to do at each of the different stages along the way and how long it will generally take to reach each point.&lt;/p&gt;
&lt;h2 id=&quot;the-cefr-scale&quot;&gt;The CEFR Scale&lt;/h2&gt;
&lt;p&gt;While there are many language proficiency scales and testing services that all have their own systems, I find it pretty valuable to talk about language stages using the 6 point scale referred to as the Common European Framework of Reference for Languages, or the CEFR scale (I pronounce this “See-Fur”). It’s simple, widely recognized throughout Europe, and pretty easy to follow.&lt;/p&gt;
&lt;p&gt;In the CEFR scale, there are six stages of language learning – A1, A2, B1, B2, C1 and C2.&lt;/p&gt;
&lt;p&gt;In a nutshell, the A levels are beginning speakers, the B levels are intermediate, and the C levels are advanced. Let’s take a closer look.&lt;/p&gt;
&lt;h2 id=&quot;level-a1-super-beginner&quot;&gt;Level A1: Super Beginner&lt;/h2&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/cefr-a1.jpg&quot; alt=&quot;The Hulk at the A1 level&quot;&gt;
  &lt;figcaption&gt;&quot;Hulk Smash&quot; – the Hulk at A1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When you reach the A1 level, you will have a rough working vocabulary of about 700 words. Some will be derivatives (“run” and “running”), some you will be able to recall quickly and others only slowly and with a lot of effort, but it’s a good ballpark number.&lt;/p&gt;
&lt;p&gt;Grammatically, you should be able to understand some really simple past and future constructions, some comparatives and superlatives (“this is larger than that”, “this is the biggest”), basic questions, modal verbs (“could”, “should”) and a little more, but overall it’s pretty basic. The ability to consistently produce all of this is even more limited, if you can produce many coherent sentences at all.&lt;/p&gt;
&lt;p&gt;Now, 700 words and superlatives may sound like a lot, but in a spoken language, it’s a surprisingly limited set. You’ll be able to explain simple things in a very basic way and interact with native speakers only if they really are willing to slow down and work with you.&lt;/p&gt;
&lt;p&gt;This is a super important point. At A1, you’re not having really sophisticated conversations with people. Think Paulo, Rachel’s Italian boyfriend from the first season of Friends. Or, from another of Jennifer Aniston’s former love interests, Brad Pitt’s Italian in Inglorious Basterds. Perhaps the Hulk from the Avengers movies. Hulk smash. You can get things across, but it’s not going to be easy on anyone involved.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/cefr-brad-pitt.gif&quot; alt=&quot;Brad Pitt speaking Italian in Inglorious Basterds&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;It will take you about 80-120 hours of work to get here from zero, depending on what you count and how you do it. For example, if you take an Alliance Française intensive course, it’s 80 hours of class time plus probably another 20-40 hours of home study, which can be done in four weeks, but that’s all day, every day. If you’re spending maybe an hour a day on it, it will take closer to four months or so.&lt;/p&gt;
&lt;h2 id=&quot;level-a2-slightly-less-beginner&quot;&gt;Level A2: Slightly Less Beginner&lt;/h2&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/cefr-a2.jpg&quot; alt=&quot;The Hulk at the A2 level&quot;&gt;
  &lt;figcaption&gt;&quot;the Hulk likes to smashes things&quot; – the Hulk at A2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When you reach the A2 level, you should have a working vocabulary of about 1500 words and far more grammar than at A1.&lt;/p&gt;
&lt;p&gt;Here you can have some real conversations, but they’re still simpler and every sentence is sprinkled with “um’s” while you search for the word. Now you can talk very basically about your family, your past, a movie you saw, your favorite foods, things like this. You’re still pretty dependent on your speaking partner slowing down and working with you a lot, but it’s not quite so painful for them.&lt;/p&gt;
&lt;p&gt;Think maybe Tom Hanks’s English around the end of The Terminal.&lt;/p&gt;
&lt;p&gt;It will take about another 100-150 hours to get here from A1 so let’s say 250 hours from absolute zero. All of these estimates are for somewhat similar languages like English to Spanish, French or Dutch. At an hour a day, this means about 8 months of work from zero. If it’s 4-6 hours a day, we’re talking about 3 months total.&lt;/p&gt;
&lt;h2 id=&quot;level-b1-intermediate&quot;&gt;Level B1: Intermediate&lt;/h2&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/cefr-b1.jpg&quot; alt=&quot;The Hulk at the B1 level&quot;&gt;
  &lt;figcaption&gt;&quot;Both the advantages and disadvantages of being the Hulk is the constantly need to smash things&quot; – the Hulk at B1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;At B1, you’re getting more comfortable. You now have a working knowledge of around 2500 words total, maybe half of which you can recall and use pretty quickly. This makes conversations much more fluid generally.&lt;/p&gt;
&lt;p&gt;You’ll still hesitate if it’s a topic you haven’t talked about much, but this is where you can start going into stores or ordering at restaurants and it’s not much of a burden on the people working there. You can read simpler books or read a good amount of newspaper articles if you know the context. You can even watch TV if the subtitles in the target language are on – it’s still pretty rough without them.&lt;/p&gt;
&lt;p&gt;Think Dev’s Italian in season two of Master of None. Or maybe Colin Firth’s Jamie proposing to Aurelia in Portuguese at the end of Love Actually. They’re not totally fluent, but it’s not a huge struggle to understand what they’re getting at or generally communicate with them.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/cefr-jaime.jpg&quot; alt=&quot;Colin Firth&amp;#x27;s Jamie proposing in Portuguese in Love Actually&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;Reaching B1 from A2 takes about 200 hours, so you’re looking at roughly 450 hours of work from scratch. At one hour per day, this is about 18 months of work total. If you’re working at it full time in a program, you can probably get here in about 4 or 5 months.&lt;/p&gt;
&lt;h2 id=&quot;level-b2-basic-fluency&quot;&gt;Level B2: Basic Fluency&lt;/h2&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/cefr-b2.jpg&quot; alt=&quot;The Hulk at the B2 level&quot;&gt;
  &lt;figcaption&gt;&quot;Being the Hulk is difficult, because although I don&apos;t want to smash things, it&apos;s quite hard for me to resist the urge to&quot; – the Hulk at B2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Reaching B2 is generally considered by most people as having basic fluency. You’ll have a working vocabulary of around 4000 words.&lt;/p&gt;
&lt;p&gt;It’s not always effortless and it’s not always perfect, but neither you nor your native speaking partners are having a really hard time in most circumstances at this point. Conversations about wide ranges of things are pretty easy for everyone involved. Most television or movies in the target language are understandable.&lt;/p&gt;
&lt;p&gt;Think of Massimo, Jennifer Lopez’s unrequited Italian courtier from The Wedding Planner, or maybe Jackie Chan in Rush Hour. You’re perfectly understandable and you don’t have issues understanding what native speakers are saying, even at the speed of a Chris Tucker.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/cefr-jackiechan.gif&quot; alt=&quot;Jackie Chan in Rush Hour&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;Getting to B2 from B1 is another 200 hours probably, making it about 650 hours from zero. At one hour per day, this is nearly two years of work if you’re starting from scratch. With intensive language courses, missing nothing, and taking courses 4-5 hours every day plus a ton of homework, this is still 28 weeks (7 months) of work.&lt;/p&gt;
&lt;h2 id=&quot;level-c1-and-c2-advanced-fluency&quot;&gt;Level C1 and C2: Advanced Fluency&lt;/h2&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/cefr-clevel.jpg&quot; alt=&quot;The Hulk at the C1 level&quot;&gt;
  &lt;figcaption&gt;&quot;When we really consider the etymology of the word Hulk, is it at all surprising that he defines himself by the destruction of the objects that surround him?&quot; – the Hulk at C1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I’m combining these levels into one description because they’re shades of fluency. A C1 speaker rarely has to search for a phrase, has full grammatical knowledge, knows idioms, can read technical papers, etc. A C2 speaker has essentially the grammar and vocabulary grasp of a native speaker.&lt;/p&gt;
&lt;p&gt;Often at the C levels, learners will dig into a specific topic more, such as legal, scientific or business specific language skills.&lt;/p&gt;
&lt;p&gt;Think Christoph Waltz’s English in Inglorious Basterds. Not really native, but super close.&lt;/p&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/cefr-waltz.gif&quot; alt=&quot;Christoph Waltz in Inglorious Basterds&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;The time it takes to progress through the C levels vary based on what your specific goals are and how you’re working your way there. The Goethe Institut says it takes about 1000 hours of total class time to reach C2, so maybe another 300 hours after you’re done with B2. From scratch, 1000 hours means about 4 years of being in class for an hour every single weekday, plus studying time.&lt;/p&gt;
&lt;p&gt;If you did it absolutely full time, in a classroom or with a tutor just about all day, every day, you could probably get to C-level language proficiency from scratch in about a year of constant work.&lt;/p&gt;
&lt;h2 id=&quot;reaching-your-goals&quot;&gt;Reaching Your Goals&lt;/h2&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/cefr-goals.jpg&quot; alt=&quot;Illustration about reaching your language goals&quot;&gt;
&lt;/figure&gt;
&lt;p&gt;Of course, all of these estimates are based on various assumptions – it will take longer to reach these levels in languages that are more dissimilar from your native language. Learning Portuguese if you know Spanish will probably take much less time, whereas learning Japanese if you only speak English may well take twice as long as what we’ve talked about here.&lt;/p&gt;
&lt;p&gt;The main point is that it takes a long, long time with a lot of work. There is no shortcutting this. There are many organizations and governments who have worked for a very long time to make this process more efficient and it’s just not possible to get to these levels a whole lot faster.&lt;/p&gt;
&lt;p&gt;If someone is telling you that they hold the key to becoming fluent in a few months, they generally mean by working 8 hours a day. If they say you can learn a language in only a few minutes per day, their definition of “language learning” is probably different from what we’re talking about here – a complete lack of verbal production or listening skills perhaps.&lt;/p&gt;
&lt;p&gt;What is possible for nearly everyone, however, is reaching these milestones if you plan and put in the time. Also, people can get to these levels at different rates. I’m giving good ballpark estimates, but your journey to each level depends on how and how often you study and practice and what your motivation is.&lt;/p&gt;
&lt;p&gt;If you want to learn a new language, figure out what your goal is.&lt;/p&gt;
&lt;p&gt;If it’s to pass for a native speaker, you have a decade of work in front of you. If it’s to relatively effortlessly understand or produce everything in the language, you have a few years of work to do. If it’s to have simple conversations in fallback circumstances, you have a few months to work to do.&lt;/p&gt;
&lt;p&gt;Determine your goal, make your plans, and start down your path. It’s entirely achievable and the journey itself is fun and interesting the entire time as you immerse yourself deeper and more complexly into a new culture.&lt;/p&gt;</content:encoded></item><item><title>Tips from GitHub: Customer Service is King</title><link>https://scottchacon.com/2017/11/08/tips-from-github-customer-service/</link><guid isPermaLink="true">https://scottchacon.com/2017/11/08/tips-from-github-customer-service/</guid><description>A lesson from GitHub: how fanatical customer service creates superfans, and why most companies get it wrong.</description><pubDate>Wed, 08 Nov 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Since I’ve been fortunate enough to go from a pretty successful valley startup to a new startup just getting off the ground, I tend to get asked pretty often what I learned at GitHub and what lessons we learned that we’re carrying over.&lt;/p&gt;
&lt;p&gt;I figured since I’m thinking about it a lot that it might be valuable to make a little blog post series out of it. So here you go, I give you the first edition of The Tips of GitHub (branching pun intended) — Customer Service.&lt;/p&gt;
&lt;h2 id=&quot;customer-service-and-the-superfan&quot;&gt;Customer Service and The Superfan&lt;/h2&gt;
&lt;p&gt;An incredible focus on customer service is one of the most important differentiating attitudes that GitHub had early on. It’s something that I feel most startups (and large companies) don’t think enough about — a fanatical devotion to helping the customer.&lt;/p&gt;
&lt;p&gt;From the very, very first days of GitHub, the founders cared deeply about customer service.&lt;/p&gt;
&lt;p&gt;We had this internal concept that I remember being repeated again and again, which was the idea of the superfan. That spending extra time and attention on an individual user or group of users, even if it was expensive and maybe didn’t justify how much they may spend on our service, was valuable in the long term in that it may create a superfan.&lt;/p&gt;
&lt;p&gt;Superfans were people who absolutely loved GitHub. People who were so surprised that we went out of our way to help them or understand their needs, that they would tell everyone they knew how great we were.&lt;/p&gt;
&lt;p&gt;We would routinely spend time and money on things that we knew would not immediately give us a monetary return, because we thought that it would make some users so happy with us that they would tell everyone they knew that we were awesome.&lt;/p&gt;
&lt;p&gt;This is something that I think is often not in an ROI analysis of an event or marketing campaign — it’s a soft and unmeasurable benefit that is almost always ignored.&lt;/p&gt;
&lt;p&gt;Ironically, some of the best cases were when we messed something up. It was almost better to have a problem happen and then respond to it above and beyond than to never have a problem at all. People are just so not used to a company caring about their issues that when one does quickly and decisively respond, it’s a revelation.&lt;/p&gt;
&lt;p&gt;GitHub always tried to make it as easy as possible to report a problem and tried to respond as quickly as they could. This is a level of attention that I just don’t see often in startups (or even larger companies, honestly).&lt;/p&gt;
&lt;h2 id=&quot;why-is-customer-service-really-important&quot;&gt;Why Is Customer Service Really Important?&lt;/h2&gt;
&lt;p&gt;I think this is a huge issue with a lot of companies today. So few companies seem to believe that customer service is really important. I’m honestly not sure why this is. Service is something that is so closely tied to loyalty and in this age of MRR and SaaS revenue streams one would imagine that loyalty is insanely important. Even so, most companies see it at the very bottom of the list of things they care about, which makes no sense.&lt;/p&gt;
&lt;p&gt;I give you this exercise.&lt;/p&gt;
&lt;p&gt;Go to every other tab that you have open in your browser right now and see how long it takes you to submit a suggestion or bug report. Just try to tell each site that you don’t appreciate the kerning of the font that they’re using. Or even better, say that you can’t click on some made up UI element or the site is showing up in German for you and you don’t know why — something basically unsolvable.&lt;/p&gt;
&lt;p&gt;Now answer these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is the way to report the problem or ask for help where you thought it would be? Is there even a way to report it at all?&lt;/li&gt;
&lt;li&gt;Did you scroll all the way to the bottom of the page, search the links in the footer, not find it, then search the menus at the top until finding something that could be the link?&lt;/li&gt;
&lt;li&gt;Assuming you found a link, how many different fields did you have to fill out before being allowed to submit your question or issue?&lt;/li&gt;
&lt;li&gt;Did they ask you for contact information even when you’re logged in and they obviously know how to get in touch with you?&lt;/li&gt;
&lt;li&gt;Did they make you fill out a subject line or area of concern, instead of triaging the issue themselves?&lt;/li&gt;
&lt;li&gt;Did they force you to research the issue in their knowledge base before bothering them with your stupid problem?&lt;/li&gt;
&lt;li&gt;Do they answer you? Ever?&lt;/li&gt;
&lt;li&gt;If they do respond, are they nice to you even though you asked an objectively stupid question?&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img src=&quot;/assets/images/github-tips-customer-service.jpg&quot; alt=&quot;An old-fashioned telephone switchboard operator&quot;&gt;
  &lt;figcaption&gt;Modern customer service is doing just fine.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I just tried this in the tabs I happen to have open right now while writing this post. Two had no discoverable help link at all. Three had only knowledge base links with no free form field I could discover with any amount of clicking (Google Calendar, United and LinkedIn). One had help in the third link I tried. One was in the fourth drop down menu I tried, then went to an endless knowledge base labyrinth.&lt;/p&gt;
&lt;p&gt;Let’s look at a quick sampling.&lt;/p&gt;
&lt;p&gt;Medium makes you type in your Medium username, even when you click on it from a logged in Medium account. Medium, I’m pretty sure you know my goddamn Medium username. It also has a scrolling dropdown menu of categories, even though it only has like 7 categories — only 2 of the 7 are beyond the fold, which is ridiculous.&lt;/p&gt;
&lt;p&gt;Twitter is one of the worst I’ve seen in awhile. You have to go down a rabbit hole of knowledge base bullshit before they even show you a “Contact Us” link, whence they then subject you to a litany of insane questions and fields, the last of which on the link I tried was actually a “Signature” field where you are asked to “Please electronically sign this notice by typing your full name”. Are you fucking kidding me? I feel like that’s meant specifically to discourage people from sending feedback.&lt;/p&gt;
&lt;p&gt;Basecamp was pretty good, giving me a bunch of fields, but pre-filling most of them with what it thought I might want. It still made me do a category dropdown though.&lt;/p&gt;
&lt;p&gt;GitHub was, and I swear this was unplanned — it just happened to be in the the tabs I had open when I had this idea — among the best of the sites I tried for this experiment. You scroll to the bottom and click ‘Contact GitHub’ and type in a question. There is unfortunately a subject line requested but it’s not required. (GitHub, btw, I would recommend getting rid of this, it’s unnecessary).&lt;/p&gt;
&lt;h2 id=&quot;how-to-really-do-customer-service&quot;&gt;How to Really Do Customer Service&lt;/h2&gt;
&lt;p&gt;Customer service should be one of the top priorities of your website and I’ll tell you why. Customer service is what people turn to when they can’t figure it out themselves. It’s the last fallback of people desperate to get something done on your website. It’s your fallback UI when everything else the customer has tried has failed them.&lt;/p&gt;
&lt;p&gt;To me, this means that it should be as easy as possible to accomplish. It means that you’ve failed your customer — your top priority should be making this process as easy and frictionless as possible, because it indicates a total failure of something your customer wanted to do. This information is gold.&lt;/p&gt;
&lt;p&gt;If your attitude is that you care so little about your customer’s frustrations and problems that you’ll make them triage their own shit by searching knowledge bases first so that they don’t bother you, or you’ll make them choose from a dropdown of categories because your people have more important things to do than determine who in your organization can best deal with the problem, then what are you even doing?&lt;/p&gt;
&lt;p&gt;If a customer at some point thinks “I need help”, then it means your product has a problem. You should be going out of your way to make it as easy as possible for your customer to tell you “I have a problem with something”. Even if it’s answerable somewhere in your knowledge base, it should be really valuable to you to know that people are running into that problem regularly. It means you might need to change something to make it not be a question for people in the first place.&lt;/p&gt;
&lt;p&gt;So, here are a couple of guidelines for your customer service solution. Go to your website and see how many of these are true.&lt;/p&gt;
&lt;h3 id=&quot;it-shouldnt-take-more-than-1-click-to-report-a-problem-or-idea&quot;&gt;It shouldn’t take more than 1 click to report a problem or idea&lt;/h3&gt;
&lt;p&gt;A single click should take you to a form you can fill out to describe the problem you’re having, from any and every page on your website. No exceptions. If that click is a mailto: link, that’s fine too.&lt;/p&gt;
&lt;h3 id=&quot;there-shouldnt-be-more-than-one-text-area-required-to-report-something&quot;&gt;There shouldn’t be more than one text area required to report something&lt;/h3&gt;
&lt;p&gt;No matter what that 1 click is, the resulting page should have a single text area to fill out that says “tell us what your problem is”. Free form, open question, no bullshit. In absolutely no circumstances should submitting that form result in a form error.&lt;/p&gt;
&lt;h3 id=&quot;you-should-have-helpyourwebsitecom&quot;&gt;You should have &lt;a href=&quot;mailto:help@yourwebsite.com&quot;&gt;help@yourwebsite.com&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;No matter who you are, you should have a &lt;a href=&quot;mailto:help@something.com&quot;&gt;help@something.com&lt;/a&gt; that people can email for any reason whatsoever.&lt;/p&gt;
&lt;h3 id=&quot;every-email-you-send-should-be-reply-able&quot;&gt;Every email you send should be reply-able&lt;/h3&gt;
&lt;p&gt;If you send any email from &lt;a href=&quot;mailto:no-reply@my-site.com&quot;&gt;no-reply@my-site.com&lt;/a&gt;, there is a special circle of hell reserved just for you. If you don’t want people wasting your time by emailing you, then don’t you dare fucking email them.&lt;/p&gt;
&lt;p&gt;Every email you send out should go directly to your customer service team if the person replies to it. Every single one.&lt;/p&gt;
&lt;h3 id=&quot;your-customers-shouldnt-need-to-choose-the-category-of-shit-you-fucked-up&quot;&gt;Your customers shouldn’t need to choose the category of shit you fucked up&lt;/h3&gt;
&lt;p&gt;If they’re contacting help, it’s almost always something you did wrong, not something they did wrong. Asking them to make your job easier by helping you triage your problems by choosing a category from a drop-down list is a dick move. How about try to make sure they’re not frustrated in the first place, and then you won’t be put through the burden of triaging their problems.&lt;/p&gt;
&lt;h3 id=&quot;your-customers-shouldnt-need-to-choose-a-subject&quot;&gt;Your customers shouldn’t need to choose a subject&lt;/h3&gt;
&lt;p&gt;I honestly don’t understand why anyone does this. Subject lines in emails are for people to scan and decide if they want to read the body or not.&lt;/p&gt;
&lt;p&gt;This concept has no place in customer support. It makes the customer do a second pass of “what am I trying to report, succinctly” but why is that their job? If you’re not making your customer triage your problem for you with a stupid dropdown menu, then that means someone in your organization will have to do that, and in that case, it’s a few seconds more to just scan a paragraph than to read a subject (and then inevitably also scan a paragraph to make sure they get the full context) before assigning to another group.&lt;/p&gt;
&lt;p&gt;There is almost no advantage to having a subject line for your team, and yet it seriously increases the friction involved in the customer submitting the report.&lt;/p&gt;
&lt;h3 id=&quot;dont-tell-them-to-go-elsewhere-in-your-customer-service-chain&quot;&gt;Don’t tell them to go elsewhere in your customer service chain&lt;/h3&gt;
&lt;p&gt;This is maybe the worst of things I’ve seen organizations do. WeWork is especially bad at this and has done it to me several times. If I email anyone in your organization, don’t tell me to email someone else. It’s not my job as your customer to know your stupid organizational structure.&lt;/p&gt;
&lt;p&gt;Figure out a way for your employees to forward things to other people. Inform the customer for future reference if necessary for a faster response, but never say “say this same thing, but to this other person”. That’s your job. If they contacted the wrong person, it’s because your product or support structure is confusing. Spend some time figuring out why they contacted the wrong person and then fix that problem instead.&lt;/p&gt;
&lt;h2 id=&quot;customer-service-attitudes&quot;&gt;Customer Service Attitudes&lt;/h2&gt;
&lt;p&gt;So now that you know how to get feedback from your customers properly, how should you respond to them?&lt;/p&gt;
&lt;h3 id=&quot;have-a-voice&quot;&gt;Have a Voice&lt;/h3&gt;
&lt;p&gt;One of the things I always loved about GitHub was that we spent a lot of time thinking about and teaching people what we called the GitHub Voice. We wrote out guidelines of what a GitHubber sounds like, what their attitude is, what they sound like when they reply and tried to make sure all external communication fell along those lines.&lt;/p&gt;
&lt;p&gt;I forget all the guidelines, but it was about always being friendly, classy and kind. Don’t be mean, don’t be snide, don’t be robotic — be human. A GitHubber writing something to a customer should sound like Neil DeGrasse Tyson.&lt;/p&gt;
&lt;p&gt;Whatever your voice is, whatever you want your company to be — write it down and communicate it to people. Even now, when a support request comes in where I want to say something snide because it seems ridiculous to me, I take a beat and ask myself “how would Neil respond?”. Develop a voice and try to make sure everyone knows what it is and can use it.&lt;/p&gt;
&lt;p&gt;Most importantly, don’t be afraid to be human. You’re putting in the effort of having a real human being help and respond, don’t force them to sound like a robot. Add some humor or levity if you can, take the issue seriously but try not to sound like a soulless machine is responding. Don’t use buzzwords, don’t talk down, don’t ever say “thought leadership”.&lt;/p&gt;
&lt;h3 id=&quot;nothing-is-stupid&quot;&gt;Nothing is Stupid&lt;/h3&gt;
&lt;p&gt;At Chatterbug, since we use Intercom and responses are super easy to do, we get a good number of messages that make almost no sense. Sometimes we get people sending us screenshots of JS consoles and full debugging of what they think is happening, other times we simply get the word “Jello”.&lt;/p&gt;
&lt;p&gt;No matter how ridiculous you may find something, try to give them the benefit of the doubt and see if you can find a way to be helpful. A lot of the time it won’t pay off — you’ll simply never get a response, but occasionally you will and people will be extra appreciative.&lt;/p&gt;
&lt;h3 id=&quot;creating-superfans-is-about-paying-attention&quot;&gt;Creating superfans is about paying attention&lt;/h3&gt;
&lt;p&gt;You don’t need to be the fastest at replying, but you do need to reply. Even if it’s a “Thanks, but no thanks”, the idea that someone there read it and cared at all is a win.&lt;/p&gt;
&lt;p&gt;People will also forgive a lot of stuff if they feel like they’re being paid attention to. There is little that is more frustrating than not being able to voice your problem or have someone listen to you. A technical problem directed to a knowledge base is a tale told by an idiot, full of sound and fury, signifying nothing.&lt;/p&gt;
&lt;p&gt;If someone has a frustrating problem and there is no way for them to vent, they just keep that anger. If they vent and you respond, “you’re totally right, that sucks, we’ll see what we can do” then it goes a long way towards making them like you. If you actually then fix it and follow up, you might just have a customer for life.&lt;/p&gt;
&lt;h3 id=&quot;everyone-should-do-customer-service&quot;&gt;Everyone should do customer service&lt;/h3&gt;
&lt;p&gt;Every single person in your organization should do customer service on a regular basis. We used to try to get as many GitHub employees to user meetups as possible, because people loved telling GitHubbers what they thought the product needed changing. They also tended to get more honest after a few beers. In vino veritas, I suppose, but in that vino’d veritas were invaluable seeds of empathy and understanding.&lt;/p&gt;
&lt;p&gt;At GitHub at one point we had a rotating support position where developers would volunteer for a week of customer support duty and would take issues flagged as being highly technical during that week and work on them with the support team. This was called King/Queen of Developers and nearly everyone did it at some point. It was highly important to us that engineers and product people had a real understanding of what was really happening from the point of view of our customers. What the common problems were, the real frustrations.&lt;/p&gt;
&lt;p&gt;Every single person in your company should do customer service or otherwise meet with real customers on a regular basis.&lt;/p&gt;
&lt;h2 id=&quot;intercom-a-great-first-step&quot;&gt;Intercom: A Great First Step&lt;/h2&gt;
&lt;p&gt;So now that you know what it takes from the customer’s perspective and from an internal attitude, if you want to implement a great customer service solution, what is the easiest way to go about doing it?&lt;/p&gt;
&lt;p&gt;At GitHub, we built our own internal customer service application that we called Halp. Nowadays, there are a few different options for good out of the box customer service solutions and at Chatterbug we’re using Intercom. While I don’t like most of the auto-messaging options that are almost impossible to not abuse when they’re used, the concept of having a little icon in the bottom corner to click on any time you need help is amazing.&lt;/p&gt;
&lt;p&gt;If you go through the list of things I mentioned, Intercom seamlessly solves all of them. Any time I’m on a website that has Intercom on it and I have an issue, I can always just click on it, type my problem and I nearly always get a response back quickly, often within the hour. I can go from experiencing an issue or problem to reporting it in a matter of seconds.&lt;/p&gt;
&lt;h2 id=&quot;customer-service-should-be-king&quot;&gt;Customer Service Should Be King&lt;/h2&gt;
&lt;p&gt;No matter how you do it, no matter what your philosophy or reporting technology or company voice, you should treat customer service as one of your most important priorities from your very first day.&lt;/p&gt;
&lt;p&gt;Your customers are human beings, stand out from the crowd of services by making your best effort at treating them as such.&lt;/p&gt;</content:encoded></item><item><title>GitHub Flow</title><link>https://scottchacon.com/2011/08/31/github-flow/</link><guid isPermaLink="true">https://scottchacon.com/2011/08/31/github-flow/</guid><description>Why we don&apos;t use git-flow at GitHub, and the simpler branch-and-deploy workflow we use instead.</description><pubDate>Wed, 31 Aug 2011 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;issues-with-git-flow&quot;&gt;Issues with git-flow&lt;/h2&gt;
&lt;p&gt;I travel all over the place teaching Git to people and nearly every class and workshop I’ve done recently has asked me what I think about &lt;a href=&quot;http://nvie.com/posts/a-successful-git-branching-model/&quot;&gt;git-flow&lt;/a&gt;.  I always answer that I think that it’s great - it has taken a system (Git) that has a million possible workflows and documented a well tested, flexible workflow that works for lots of developers in a fairly straightforward manner.  It has become something of a standard so that developers can move between projects or companies and be familiar with this standardized workflow.&lt;/p&gt;
&lt;p&gt;However, it does have its issues. I have heard a number of opinions from people along the lines of not liking that new feature branches are started off of &lt;code&gt;develop&lt;/code&gt; rather than &lt;code&gt;master&lt;/code&gt;, or the way it handles hotfixes, but those are fairly minor.&lt;/p&gt;
&lt;p&gt;One of the bigger issues for me is that it’s more complicated than I think most developers and development teams actually require. It’s complicated enough that a big &lt;a href=&quot;https://github.com/nvie/gitflow&quot;&gt;helper script&lt;/a&gt; was developed to help enforce the flow.  Though this is cool, the issue is that it cannot be enforced in a Git GUI, only on the command line, so the only people who have to learn the complex workflow really well, because they have to do all the steps manually, are the same people who aren’t comfortable with the system enough to use it from the command line.  This can be a huge problem.&lt;/p&gt;
&lt;p&gt;Both of these issues can be solved easily just by having a much more simplified process. At GitHub, we do not use git-flow.  We use, and always have used, a much simpler Git workflow.&lt;/p&gt;
&lt;p&gt;Its simplicity gives it a number of advantages.  One is that it’s easy for people to understand, which means they can pick it up quickly and they rarely if ever mess it up or have to undo steps they did wrong. Another is that we don’t need a wrapper script to help enforce it or follow it, so using GUIs and such are not a problem.&lt;/p&gt;
&lt;h2 id=&quot;github-flow&quot;&gt;GitHub Flow&lt;/h2&gt;
&lt;p&gt;So, why don’t we use git-flow at GitHub?  Well, the main issue is that we deploy all the time.  The git-flow process is designed largely around the “release”.  We don’t really have “releases” because we deploy to production every day - often several times a day.  We can do so through our chat room robot, which is the same place our CI results are displayed. We try to make the process of testing and shipping as simple as possible so that every employee feels comfortable doing it.&lt;/p&gt;
&lt;p&gt;There are a number of advantages to deploying so regularly. If you deploy every few hours, it’s almost impossible to introduce large numbers of big bugs.  Little issues can be introduced, but then they can be fixed and redeployed very quickly.  Normally you would have to do a ‘hotfix’ or something outside of the normal process, but it’s simply part of our normal process - there is no difference in the GitHub flow between a hotfix and a very small feature.&lt;/p&gt;
&lt;p&gt;Another advantage of deploying all the time is the ability to quickly address issues of all kinds.  We can respond to security issues that are brought to our attention or implement small but interesting feature requests incredibly quickly, yet we can use the exact same process to address those changes as we do to handle normal or even large feature development.  It’s all the same process and it’s all very simple.&lt;/p&gt;
&lt;h3 id=&quot;how-we-do-it&quot;&gt;How We Do It&lt;/h3&gt;
&lt;p&gt;So, what is GitHub Flow?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Anything in the &lt;code&gt;master&lt;/code&gt; branch is deployable&lt;/li&gt;
&lt;li&gt;To work on something new, create a descriptively named branch off of &lt;code&gt;master&lt;/code&gt; (ie: &lt;code&gt;new-oauth2-scopes&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Commit to that branch locally and regularly push your work to the same named branch on the server&lt;/li&gt;
&lt;li&gt;When you need feedback or help, or you think the branch is ready for merging, open a &lt;a href=&quot;http://help.github.com/send-pull-requests/&quot;&gt;pull request&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;After someone else has reviewed and signed off on the feature, you can merge it into master&lt;/li&gt;
&lt;li&gt;Once it is merged and pushed to ‘master’, you can and &lt;em&gt;should&lt;/em&gt; deploy immediately&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is the entire flow.  It is very simple, very effective and works for fairly large teams - GitHub is 35 employees now, maybe 15-20 of whom work on the same project (github.com) at the same time.  I think that most development teams - groups that work on the same logical code at the same time which could produce conflicts - are around this size or smaller.  Especially those that are progressive enough to be doing rapid and consistent deployments.&lt;/p&gt;
&lt;p&gt;So, let’s look at each of these steps in turn.&lt;/p&gt;
&lt;h4 id=&quot;1---anything-in-the-master-branch-is-deployable&quot;&gt;#1 - anything in the master branch is deployable&lt;/h4&gt;
&lt;p&gt;This is basically the only hard &lt;em&gt;rule&lt;/em&gt; of the system. There is only one branch that has any specific and consistent meaning and we named it &lt;code&gt;master&lt;/code&gt;.  To us, this means that it has been deployed or at the worst will be deployed within hours.  It’s incredibly rare that this gets rewound (the branch is moved back to an older commit to revert work) - if there is an issue, commits will be reverted or new commits will be introduced that fixes the issue, but the branch itself is almost never rolled back.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;master&lt;/code&gt; branch is stable and it is always, always safe to deploy from it or create new branches off of it.  If you push something to master that is not tested or breaks the build, you break the social contract of the development team and you normally feel pretty bad about it.  Every branch we push has tests run on it and reported into the chat room, so if you haven’t run them locally, you can simply push to a topic branch (even a branch with a single commit) on the server and wait for &lt;a href=&quot;http://jenkins-ci.org/&quot;&gt;Jenkins&lt;/a&gt; to tell you if it passes everything.&lt;/p&gt;
&lt;p&gt;You could have a &lt;code&gt;deployed&lt;/code&gt; branch that is updated only when you deploy, but we don’t do that.  We simply expose the currently deployed SHA through the webapp itself and &lt;code&gt;curl&lt;/code&gt; it if we need a comparison made.&lt;/p&gt;
&lt;h4 id=&quot;2---create-descriptive-branches-off-of-master&quot;&gt;#2 - create descriptive branches off of master&lt;/h4&gt;
&lt;p&gt;When you want to start work on anything, you create a descriptively named branch off of the stable &lt;code&gt;master&lt;/code&gt; branch.  Some examples in the GitHub codebase right now would be &lt;code&gt;user-content-cache-key&lt;/code&gt;, &lt;code&gt;submodules-init-task&lt;/code&gt; or &lt;code&gt;redis2-transition&lt;/code&gt;.  This has several advantages - one is that when you fetch, you can see the topics that everyone else has been working on.  Another is that if you abandon a branch for a while and go back to it later, it’s fairly easy to remember what it was.&lt;/p&gt;
&lt;p&gt;This is nice because when we go to the GitHub branch list page we can easily see what branches have been worked on recently and roughly how much work they have on them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/70/6769774/7988902c-d0a8-11e4-94c9-dc132461ffe4.png&quot; alt=&quot;github branch list&quot;&gt;&lt;/p&gt;
&lt;p&gt;It’s almost like a list of upcoming features with current rough status. This page is awesome if you’re not using it - it only shows you branches that have unique work on them relative to your currently selected branch and it sorts them so that the ones most recently worked on are at the top. If I get really curious, I can click on the ‘Compare’ button to see what the actual unified diff and commit list is that is unique to that branch.&lt;/p&gt;
&lt;p&gt;So, as of this writing, we have 44 branches in our repository with unmerged work in them, but I can also see that only about 9 or 10 of them have been pushed to in the last week.&lt;/p&gt;
&lt;h4 id=&quot;3---push-to-named-branches-constantly&quot;&gt;#3 - push to named branches constantly&lt;/h4&gt;
&lt;p&gt;Another big difference from git-flow is that we push to named branches on the server constantly.  Since the only thing we really have to worry about is &lt;code&gt;master&lt;/code&gt; from a deployment standpoint, pushing to the server doesn’t mess anyone up or confuse things - everything that is not &lt;code&gt;master&lt;/code&gt; is simply something being worked on.&lt;/p&gt;
&lt;p&gt;It also make sure that our work is always backed up in case of laptop loss or hard drive failure. More importantly, it puts everyone in constant communication.  A simple ‘git fetch’ will basically give you a TODO list of what every is currently working on.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git fetch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;remote: Counting objects: 3032, done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;remote: Compressing objects: 100% (947/947), done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;remote: Total 2672 (delta 1993), reused 2328 (delta 1689)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Receiving objects: 100% (2672/2672), 16.45 MiB | 1.04 MiB/s, done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Resolving deltas: 100% (1993/1993), completed with 213 local objects.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;From github.com:github/github&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      charlock-linguist -&gt; origin/charlock-linguist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      enterprise-non-config -&gt; origin/enterprise-non-config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      fi-signup  -&gt; origin/fi-signup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   2647a42..4d6d2c2  git-http-server -&gt; origin/git-http-server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      knyle-style-commits -&gt; origin/knyle-style-commits&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   157d2b0..d33e00d  master     -&gt; origin/master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      menu-behavior-act-i -&gt; origin/menu-behavior-act-i&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   ea1c5e2..dfd315a  no-inline-js-config -&gt; origin/no-inline-js-config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      svg-tests  -&gt; origin/svg-tests&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   87bb870..9da23f3  view-modes -&gt; origin/view-modes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      wild-renaming -&gt; origin/wild-renaming&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It also lets everyone see, by looking at the GitHub Branch List page, what everyone else is working on so they can inspect them and see if they want to help with something.&lt;/p&gt;
&lt;h4 id=&quot;4---open-a-pull-request-at-any-time&quot;&gt;#4 - open a pull request at any time&lt;/h4&gt;
&lt;p&gt;GitHub has an amazing code review system called &lt;a href=&quot;http://help.github.com/send-pull-requests/&quot;&gt;Pull Requests&lt;/a&gt; that I fear not enough people know about.  Many people use it for open source work - fork a project, update the project, send a pull request to the maintainer.  However, it can also easily be used as an internal code review system, which is what we do.&lt;/p&gt;
&lt;p&gt;Actually, we use it more as a branch conversation view more than a pull request. You can send pull requests from one branch to another in a single project (public or private) in GitHub, so you can use them to say “I need help or review on this” in addition to “Please merge this in”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/70/6769770/61a2dcba-d0a8-11e4-9924-3576232053ee.png&quot; alt=&quot;early pr message&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here you can see Josh cc’ing Brian for review and Brian coming in with some advice on one of the lines of code.  Further down we can see Josh acknowledging Brian’s concerns and pushing more code to address them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/70/6769767/5054b4ba-d0a8-11e4-8d38-548ecf157018.png&quot; alt=&quot;more discussion&quot;&gt;&lt;/p&gt;
&lt;p&gt;Finally you can see that we’re still in the trial phase - this is not a deployment ready branch yet, we use the Pull Requests to review the code long before we actually want to merge it into &lt;code&gt;master&lt;/code&gt; for deployment.&lt;/p&gt;
&lt;p&gt;If you are stuck in the progress of your feature or branch and need help or advice, or if you are a developer and need a designer to review your work (or vice versa), or even if you have little or no code but some screenshot comps or general ideas, you open a pull request.  You can cc people in the GitHub system by adding in a @username, so if you want the review or feedback of specific people, you simply cc them in the PR message (as you saw Josh do above).&lt;/p&gt;
&lt;p&gt;This is cool because the Pull Request feature let’s you comment on individual lines in the unified diff, on single commits or on the pull request itself and pulls everything inline to a single conversation view.
It also let you continue to push to the branch, so if someone comments that you forgot to do something or there is a bug in the code, you can fix it and push to the branch, GitHub will show the new commits in the conversation view and you can keep iterating on a branch like that.&lt;/p&gt;
&lt;p&gt;If the branch has been open for too long and you feel it’s getting out of sync with the master branch, you can merge master into your topic branch and keep going.  You can easily see in the pull request discussion or commit list when the branch was last brought up to date with the ‘master’.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/70/6769754/2162f69e-d0a8-11e4-8c98-d2bb581f7152.png&quot; alt=&quot;master merge&quot;&gt;&lt;/p&gt;
&lt;p&gt;When everything is really and truly done on the branch and you feel it’s ready to deploy, you can move on to the next step.&lt;/p&gt;
&lt;h4 id=&quot;5---merge-only-after-pull-request-review&quot;&gt;#5 - merge only after pull request review&lt;/h4&gt;
&lt;p&gt;We don’t simply do work directly on &lt;code&gt;master&lt;/code&gt; or work on a topic branch and merge it in when we think it’s done - we try to get signoff from someone else in the company.  This is generally a +1 or emoji or “:shipit:” comment, but we try to get someone else to look at it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/70/6769753/0ea37c4a-d0a8-11e4-8b61-7aa73b7e3b03.png&quot; alt=&quot;shipit comment&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once we get that, and the branch passes CI, we can merge it into master for deployment, which will automatically close the Pull Request when we push it.&lt;/p&gt;
&lt;h4 id=&quot;6---deploy-immediately-after-review&quot;&gt;#6 - deploy immediately after review&lt;/h4&gt;
&lt;p&gt;Finally, your work is done and merged into the &lt;code&gt;master&lt;/code&gt; branch.  This means that even if you don’t deploy it now, people will base new work off of it and the next deploy, which will likely happen in a few hours, will push it out.  So since you really don’t want someone else to push something that you wrote that breaks things, people tend to make sure that it really is stable when it’s merged and people also tend to push their own changes.&lt;/p&gt;
&lt;p&gt;Our campfire bot, hubot, can do deployments for any of the employees, so a simple:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;hubot depoy github to production&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;will deploy the code and zero-downtime restart all the necessary processes. You can see how common this is at GitHub:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cloud.githubusercontent.com/assets/70/6769750/ebc7b3da-d0a7-11e4-96e3-cfe0cea6ae51.png&quot; alt=&quot;our campfire logs&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can see 6 different people (including a support guy and a designer) deploying about 24 times in one day.&lt;/p&gt;
&lt;p&gt;I have done this for branches with one commit containing a one line change.  The process is simple, straightforward, scalable and powerful. You can do it with feature branches with 50 commits on them that took 2 weeks, or 1 commit that took 10 minutes. It is such a simple and frictionless process that you are not annoyed that you have to do it even for 1 commit, which means people rarely try to skip or bypass the process unless the change is so small or insignificant that it just doesn’t matter.&lt;/p&gt;
&lt;p&gt;This is an incredibly simple and powerful process.  I think most people would agree that GitHub has a very stable platform, that issues are addressed quickly if they ever come up at all, and that new features are introduced at a rapid pace.  There is no compromise of quality or stability so that we can get more speed or simplicity or less process.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Git itself is fairly complex to understand, making the workflow that you use with it more complex than necessary is simply adding more mental overhead to everybody’s day.  I would always advocate using the simplest possible system that will work for your team and doing so until it doesn’t work anymore and then adding complexity only as absolutely needed.&lt;/p&gt;
&lt;p&gt;For teams that have to do formal releases on a longer term interval (a few weeks to a few months between releases), and be able to do hot-fixes and maintenance branches and other things that arise from shipping so infrequently, &lt;a href=&quot;http://nvie.com/posts/a-successful-git-branching-model/&quot;&gt;git-flow&lt;/a&gt; makes sense and I would highly advocate it’s use.&lt;/p&gt;
&lt;p&gt;For teams that have set up a culture of shipping, who push to production every day, who are constantly testing and deploying, I would advocate picking something simpler like GitHub Flow.&lt;/p&gt;</content:encoded></item><item><title>Reset Demystified</title><link>https://scottchacon.com/2011/07/11/reset/</link><guid isPermaLink="true">https://scottchacon.com/2011/07/11/reset/</guid><description>Demystifying git reset by following Git&apos;s three trees. This walkthrough later became part of the Pro Git book.</description><pubDate>Mon, 11 Jul 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Thanks for visiting!&lt;/p&gt;
&lt;p&gt;This blog post content has been incorporated into the &lt;a href=&quot;https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified&quot;&gt;Pro Git Book, Chapter 7.7&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please check it out there, as it’s more up to date and has pictures and stuff.&lt;/p&gt;</content:encoded></item><item><title>Note to Self</title><link>https://scottchacon.com/2010/08/25/notes/</link><guid isPermaLink="true">https://scottchacon.com/2010/08/25/notes/</guid><description>Attaching extra data to a commit without changing its SHA, using the little-known git notes command.</description><pubDate>Wed, 25 Aug 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the cool things about Git is that it has strong cryptographic
integrity.  If you change any bit in the commit data or any of the files it
keeps, all the checksums change, including the commit SHA and every commit
SHA since that one.  However, that means that in order to amend the commit
in any way, for instance to add some comments on something or even sign off
on a commit, you have to change the SHA of the commit itself.&lt;/p&gt;
&lt;p&gt;Wouldn’t it be nice if you could add data to a commit without changing its
SHA?  If only there existed an external mechanism to attach data to a commit
without modifying the commit message itself.  Happy day!  It turns out there
exists just such a feature in newer versions of Git!  As we can see from the
Git 1.6.6 release notes where this new functionality was first introduced:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;* &quot;git notes&quot; command to annotate existing commits.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Need any more be said?  Well, maybe.  How do you use it?  What does it do?
How can it be useful?  I’m not sure I can answer all of these questions, but
let’s give it a try.  First of all, how does one use it?&lt;/p&gt;
&lt;p&gt;Well, to add a note to a specific commit, you only need to run
&lt;code&gt;git notes add [commit]&lt;/code&gt;, like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git notes add HEAD&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will open up your editor to write your commit message.  You can also use
the &lt;code&gt;-m&lt;/code&gt; option to provide the note right on the command line:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git notes add -m &apos;I approve - Scott&apos; master~1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That will add a note to the first parent on the last commit on the master
branch.  Now, how to view these notes?  The easiest way is with the &lt;code&gt;git log&lt;/code&gt;
command.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;commit 0385bcc3bc66d1b1ec07346c237061574335c3b8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Author: Ryan Tomayko &amp;#x3C;rtomayko@gmail.com&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Date:   Tue Jun 22 20:09:32 2010 -0700&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  yield to run block right before accepting connections&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;commit 06ca03a20bb01203e2d6b8996e365f46cb6d59bd&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Author: Ryan Tomayko &amp;#x3C;rtomayko@gmail.com&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Date:   Wed May 12 06:47:15 2010 -0700&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  no need to delete these header names now&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Notes:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  I approve - Scott&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see the notes appended automatically in the log output.  You can only
have one note per commit in a namespace though (I will explain namespaces in
the next section), so if you want to add a note to that commit, you have to
instead edit the existing one.  You can either do this by running:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git notes edit master~1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which will open a text editor with the existing note so you can edit it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I approve - Scott&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Write/edit the notes for the following object:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# commit 06ca03a20bb01203e2d6b8996e365f46cb6d59bd&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Author: Ryan Tomayko &amp;#x3C;rtomayko@gmail.com&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Date:   Wed May 12 06:47:15 2010 -0700&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#     no need to delete these header names now&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#  kidgloves.rb |    2 --&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#  1 files changed, 0 insertions(+), 2 deletions(-)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;~                                                                               &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;~                                                                               &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;~                                                                               &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&quot;.git/NOTES_EDITMSG&quot; 13L, 338C&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sort of weird, but it works.  If you just want to add something to the end of
an existing note, you can run &lt;code&gt;git notes append SHA&lt;/code&gt;, but only in newer
versions of Git (I think 1.7.1 and above).&lt;/p&gt;
&lt;h2 id=&quot;notes-namespaces&quot;&gt;Notes Namespaces&lt;/h2&gt;
&lt;p&gt;Since you can only have one note per commit, Git allows you to have multiple
namespaces for your notes.  The default namespace is called ‘commits’, but
you can change that.  Let’s say we’re using the ‘commits’ notes namespace to
store general comments but we want to also store bugzilla information for our
commits.  We can also have a ‘bugzilla’ namespace.  Here is how we would add
a bug number to a commit under the bugzilla namespace:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git notes --ref=bugzilla add -m &apos;bug #15&apos; 0385bcc3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, now you have to tell Git to specifically look in that namespace:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --show-notes=bugzilla&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;commit 0385bcc3bc66d1b1ec07346c237061574335c3b8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Author: Ryan Tomayko &amp;#x3C;rtomayko@gmail.com&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Date:   Tue Jun 22 20:09:32 2010 -0700&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  yield to run block right before accepting connections&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Notes (bugzilla):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  bug #15&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;commit 06ca03a20bb01203e2d6b8996e365f46cb6d59bd&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Author: Ryan Tomayko &amp;#x3C;rtomayko@gmail.com&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Date:   Wed May 12 06:47:15 2010 -0700&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  no need to delete these header names now&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Notes:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  I approve - Scott&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that it also will show your normal notes.  You can actually have it show
notes from all your namespaces by running &lt;code&gt;git log --show-notes=*&lt;/code&gt; - if you
have a lot of them, you may want to just alias that.  Here is what your log
output might look like if you have a number of notes namespaces:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log -1 --show-notes=*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;commit 0385bcc3bc66d1b1ec07346c237061574335c3b8&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Author: Ryan Tomayko &amp;#x3C;rtomayko@gmail.com&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Date:   Tue Jun 22 20:09:32 2010 -0700&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    yield to run block right before accepting connections&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Notes:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    I approve of this, too - Scott&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Notes (bugzilla):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    bug #15&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Notes (build):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    build successful (8/13/10)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also switch the current namespace you’re using so that the default for
writing and showing notes is not ‘commits’ but, say, ‘bugzilla’ instead.  If you
export the variable &lt;code&gt;GIT_NOTES_REF&lt;/code&gt; to point to something different, then the
&lt;code&gt;--ref&lt;/code&gt; and &lt;code&gt;--show-notes&lt;/code&gt; options are not neccesary.  For example:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ export GIT_NOTES_REF=refs/notes/bugzilla&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That will set your default to ‘bugzilla’ instead.  It has to start with the ‘refs/notes/’ though.&lt;/p&gt;
&lt;h2 id=&quot;sharing-notes&quot;&gt;Sharing Notes&lt;/h2&gt;
&lt;p&gt;Now, here is where the general usability of this really breaks down.  I am
hoping that this will be improved in the future and I put off writing this post
because of my concern with this phase of the process, but I figured it has
interesting enough functionality as-is that someone might want to play with it.&lt;/p&gt;
&lt;p&gt;So, the notes (as you may have noticed in the previous section) are stored as
references, just like branches and tags.  This means you can push them to a
server.  However, Git has a bit of magic built in to expand a branch name
like ‘master’ to what it really is, which is ‘refs/heads/master’. Unfortunately,
Git has no such magic built in for notes.  So, to push your notes to a server,
you cannot simply run something like &lt;code&gt;git push origin bugzilla&lt;/code&gt;.  Git will do
this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git push origin bugzilla&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;error: src refspec bugzilla does not match any.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;error: failed to push some refs to &apos;git@github.com:schacon/kidgloves.git&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, you can push anything under ‘refs/’ to a server, you just need to be
more explicit about it.  If you run this it will work fine:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git push origin refs/notes/bugzilla&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Counting objects: 3, done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Delta compression using up to 2 threads.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Compressing objects: 100% (2/2), done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Writing objects: 100% (3/3), 263 bytes, done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Total 3 (delta 0), reused 0 (delta 0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;To git@github.com:schacon/kidgloves.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      refs/notes/bugzilla -&gt; refs/notes/bugzilla&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In fact, you may want to just make that &lt;code&gt;git push origin refs/notes/*&lt;/code&gt; which
will push all your notes.  This is what Git does normally for something like
tags.  When you run &lt;code&gt;git push origin --tags&lt;/code&gt; it basically expands to
&lt;code&gt;git push origin refs/tags/*&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;getting-notes&quot;&gt;Getting Notes&lt;/h2&gt;
&lt;p&gt;Unfortunately, getting notes is even more difficult.  Not only is there no
&lt;code&gt;git fetch --notes&lt;/code&gt; or something, you have to specify both sides of the
refspec (as far as I can tell).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git fetch origin refs/notes/*:refs/notes/*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;remote: Counting objects: 12, done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;remote: Compressing objects: 100% (8/8), done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;remote: Total 12 (delta 0), reused 0 (delta 0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Unpacking objects: 100% (12/12), done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;From github.com:schacon/kidgloves&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      refs/notes/bugzilla -&gt; refs/notes/bugzilla&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is basically the only way to get them into your repository from the server.
Yay.  If you want to, you can setup your Git config file to automatically pull
them down though.  If you look at your &lt;code&gt;.git/config&lt;/code&gt; file you should have a
section that looks like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[remote &quot;origin&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  fetch = +refs/heads/*:refs/remotes/origin/*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  url = git@github.com:schacon/kidgloves.git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ‘fetch’ line is the refspec of what Git will try to do if you run just
&lt;code&gt;git fetch origin&lt;/code&gt;.  It contains the magic formula of what Git will fetch and
store local references to.  For instance, in this case it will take every
branch on the server and give you a local branch under ‘remotes/origin/’ so
you can reference the ‘master’ branch on the server as ‘remotes/origin/master’
or just ‘origin/master’ (it will look under ‘remotes’ when it’s trying to
figure out what you’re doing).  If you change that line to
&lt;code&gt;fetch = +refs/heads/*:refs/remotes/manamana/*&lt;/code&gt; then even though your remote
is named ‘origin’, the master branch from your ‘origin’ server will be under
‘manamana/master’.&lt;/p&gt;
&lt;p&gt;Anyhow, you can use this to make your notes fetching easier.  If you add
multiple &lt;code&gt;fetch&lt;/code&gt; lines, it will do them all.  So in addition to the current
&lt;code&gt;fetch&lt;/code&gt; line, you can add a line that looks like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  fetch = +refs/notes/*:refs/notes/*&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which says also get all the notes references on the server and store them
as though they were local notes.  Or you can namespace them if you want, but
that can cause issues when you try to push them back again.&lt;/p&gt;
&lt;h2 id=&quot;collaborating-on-notes&quot;&gt;Collaborating on Notes&lt;/h2&gt;
&lt;p&gt;Now, this is where the main problem is.  Merging notes is super difficult.
This means that if you pull down someone’s notes, you edit any note in a
namespace locally and the other developer edits any note in that same namespace,
you’re going to have a hard time getting them back in sync.  When the second
person tries to push their notes it will look like a non-fast-forward just
like a normal branch update, but unline a normal branch you can’t just run
&lt;code&gt;git pull&lt;/code&gt; and then try again.  You have to check out your notes ref as if it
were a normal branch, which will look ridiculously confusing and then do the
merge and then switch back.  It is do-able, but probably not something you
really want to do.&lt;/p&gt;
&lt;p&gt;Because of this, it’s probably best to namespace your notes or better
just have an automated process create them (like build statuses or bugzilla
artifacts).  If only one entity is updating your notes, you won’t have merge
issues.  However, if you want to use them to comment on commits within a team,
it is going to be a bit painful.&lt;/p&gt;
&lt;p&gt;So far, I’ve heard of people using them to have their ticketing system attach
metadata automatically or have
&lt;a href=&quot;http://article.gmane.org/gmane.comp.version-control.git/109074&quot;&gt;a system&lt;/a&gt;
attach associated mailing list emails
to commits they concern.
Other people just use them entirely locally without pushing them
anywhere to store reminders for themselves and whatnot.&lt;br&gt;
Probably a good start, but the ambitious among you may come up with
something else interesting to do.  Let me know!&lt;/p&gt;</content:encoded></item><item><title>Pro Git 简体中文版</title><link>https://scottchacon.com/2010/06/09/pro-git-zh/</link><guid isPermaLink="true">https://scottchacon.com/2010/06/09/pro-git-zh/</guid><description>Pro Git is now available in a simplified Chinese translation, thanks to the tireless work of Chunzi.</description><pubDate>Wed, 09 Jun 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The amazing &lt;a href=&quot;http://chunzi.me/&quot;&gt;Chunzi&lt;/a&gt;, in addition to translating
and helping to coordinate the translation of the Chinese version of Pro Git,
has just sent me a draft epub of the book in Chinese.&lt;/p&gt;
&lt;p&gt;As he says:&lt;/p&gt;
&lt;p&gt;“下载地址：&lt;a href=&quot;http://cl.ly/da7a450319adfac01108&quot;&gt;http://cl.ly/da7a450319adfac01108&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;此书基本完成翻译，但仍有大半尚未审阅。今心血来潮，做了 epub 版本，可在 iPad 上阅读。生成代码在 &lt;a href=&quot;http://github.com/chunzi/progit/tree/master/epub-zh%E3%80%82&quot;&gt;http://github.com/chunzi/progit/tree/master/epub-zh。&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;既然是开源图书，所以优先级比较低。后续更新时再作新版。”&lt;/p&gt;
&lt;p&gt;I have also uploaded the file to S3 in addition to his cl.ly link.  If you want
Pro Git in Chinese on your iPad, you can download it
&lt;a href=&quot;https://github.s3.amazonaws.com/media/progit-zh.epub&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;祝你好运！&lt;/p&gt;</content:encoded></item><item><title>Pro Git Kindle Version Available</title><link>https://scottchacon.com/2010/06/06/pro-git-on-kindle/</link><guid isPermaLink="true">https://scottchacon.com/2010/06/06/pro-git-on-kindle/</guid><description>Pro Git is now available as a proper Kindle edition, downloadable straight from the Amazon store.</description><pubDate>Sun, 06 Jun 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When Pro Git was first released, I asked about being able to get it on my Kindle.  In fact, one of the very first people to read the book was &lt;a href=&quot;http://twitter.com/adelcambre&quot;&gt;@adelcambre&lt;/a&gt; with a mobi file I generated myself.  It was horrible looking, because I didn’t do it very well, but it did work. My editor at Apress wanted to get a professional Kindle version produced, but wasn’t sure if it was going to get done anytime soon.&lt;/p&gt;
&lt;p&gt;Well, I just randomly found out that it did in fact finally get done.  About 9 months after it was first published, I saw on my Amazon referal account that I had sold a Kindle version, which confused me.  However, I went to Amazon and there it was:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/gp/product/B003NHRMXA?ie=UTF8&amp;#x26;tag=prgi-20&amp;#x26;linkCode=as2&amp;#x26;camp=1789&amp;#x26;creative=390957&amp;#x26;creativeASIN=B003NHRMXA&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://ecx.images-amazon.com/images/I/51joD88JCAL._SL500_AA266_PIkin2,BottomRight,-3,34_AA300_SH20_OU01_.jpg&quot;&gt;&lt;/a&gt;&lt;img src=&quot;http://www.assoc-amazon.com/e/ir?t=prgi-20&amp;#x26;l=as2&amp;#x26;o=1&amp;#x26;a=B003NHRMXA&quot; width=&quot;1&quot; height=&quot;1&quot; border=&quot;0&quot; alt=&quot;&quot; style=&quot;border:none !important; margin:0px !important;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I assume it looks much, much better than the version I originally did for Andy.  (I should probably get myself a copy).  So, if you feel like reading it on the Kindle, &lt;a href=&quot;http://www.amazon.com/gp/product/B003NHRMXA?ie=UTF8&amp;#x26;tag=prgi-20&amp;#x26;linkCode=as2&amp;#x26;camp=1789&amp;#x26;creative=390957&amp;#x26;creativeASIN=B003NHRMXA&quot;&gt;have at it&lt;/a&gt;.  There have also been a number of people who have asked me how they can support the book without getting a dead-tree version, so here’s your chance.  You can even get a Kindle reader for your computer, so you can search through it and whatnot.  Enjoy!&lt;/p&gt;</content:encoded></item><item><title>My New Blog</title><link>https://scottchacon.com/2010/05/25/blog-over/</link><guid isPermaLink="true">https://scottchacon.com/2010/05/25/blog-over/</guid><description>Retiring my old blog that hopped from host to host and domain to domain over the years, for something new.</description><pubDate>Tue, 25 May 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My previous blog, which moved from host to host, domain to domain over the years
has built up a lot of cruft.  I believe I went from a Ruby blog to WordPress to
a custom blog engine back to WordPress to Jekyll and it’s a mess.  Plus, a lot
of the stuff I used to blog about is not really relevant to me anymore and the
domain name I did everything under, “jointheconversation.org”, was too long and
not as relevant to what I’m doing as it used to be.&lt;/p&gt;
&lt;p&gt;So, I’ve decided to declare blog bankruptcy - blogruptcy - and start over again
with a new site design (this time done by me, as you can probably tell) and
new content.  I pulled over a few of the last entries of my previous blog but
for the most part I’m starting over.&lt;/p&gt;
&lt;p&gt;So there.&lt;/p&gt;</content:encoded></item><item><title>Git Loves the Environment</title><link>https://scottchacon.com/2010/04/11/environment/</link><guid isPermaLink="true">https://scottchacon.com/2010/04/11/environment/</guid><description>A tour of the environment variables Git respects, and how they can quietly shape your workflow.</description><pubDate>Sun, 11 Apr 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the things that people that come from the Subversion world tend
to find pretty cool about Git is that there is no &lt;code&gt;.svn&lt;/code&gt; directory in
every subdirectory of your project, but instead just one &lt;code&gt;.git&lt;/code&gt; directory
in the root of your project.  Actually, it’s even better than that.
The &lt;code&gt;.git&lt;/code&gt; directory does not even need to actually be within your project.
Git allows you to tell it where your &lt;code&gt;.git&lt;/code&gt; directory is, and there are
a couple of ways to do that.&lt;/p&gt;
&lt;p&gt;Let’s say you have your project and want to move the &lt;code&gt;.git&lt;/code&gt; directory
somewhere else.  First let’s see what happens when we move our &lt;code&gt;.git&lt;/code&gt;
directory without telling Git.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;6e948ec my second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fda8c93 my initial commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ mv .git /opt/myproject.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fatal: Not a git repository (or any of the parent directories): .git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well, since Git can’t find a &lt;code&gt;.git&lt;/code&gt; direcotry, it appears that you are
simply in a directory that is not controlled by Git.  However, it’s
pretty easy to tell Git to look elsewhere by providing the &lt;code&gt;--git-dir&lt;/code&gt;
option to any Git call:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git --git-dir=/opt/myproject.git log --oneline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;6e948ec my second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fda8c93 my initial commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, you probably don’t want to do that for every Git call, as
that is a lot of typing.  You could create a shell alias, but you can
also export an environment variable called &lt;code&gt;GIT_DIR&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ export GIT_DIR=/opt/myproject.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;6e948ec my second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fda8c93 my initial commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a number of ways to customize Git functionality via specific
environment variables.  You can also tell Git where your working directory
is with &lt;code&gt;GIT_WORK_TREE&lt;/code&gt;, so you can run the Git commands from any
directory you are in, not just the current working directory. To see this,
first we’ll change a file and then change directories and run &lt;code&gt;git status&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ echo &apos;test&apos; &gt;&gt; README &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git status --short&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;M README&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OK, but now if we change working directories, we’ll get weird output.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ cd /tmp&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git status --short&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; D README&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; ?? .ksda.1F5/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; ?? aprKhGx02&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; ?? qlm.log&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; ?? qlmlog.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; ?? smsi02122&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now Git is comparing your last commit to what is in your current working
directory.  However, you can tell it where your real Git working directory
is without being in it, either with the &lt;code&gt;--work-tree&lt;/code&gt; option or by exporting
the &lt;code&gt;GIT_WORK_TREE&lt;/code&gt; variable:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git --work-tree=/tmp/myproject status --short&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; M README&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ export GIT_WORK_TREE=/tmp/myproject&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git status --short&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; M README&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you’re doing operations on a Git repository outside of your working
directory, which you’re not even in.&lt;/p&gt;
&lt;p&gt;The last interesting variable you can set is your staging area.  That
is normally in the &lt;code&gt;.git/index&lt;/code&gt; file, but again, you can set it somewhere
else, so that you can have multiple staging areas that you can switch
between if you want.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ export GIT_INDEX_FILE=/tmp/index1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git add README&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git status&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# On branch master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Changes to be committed:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#   (use &quot;git reset HEAD &amp;#x3C;file&gt;...&quot; to unstage)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# modified:   README&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have the README file changed staged in the new index file.  If we
switch back to our original index, we can see that the file is no longer
staged:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ export GIT_INDEX_FILE=/opt/myproject.git/index&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git status&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# On branch master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Changed but not updated:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#   (use &quot;git add &amp;#x3C;file&gt;...&quot; to update what will be committed)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#   (use &quot;git checkout -- &amp;#x3C;file&gt;...&quot; to discard changes in working directory)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# modified:   README&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;no changes added to commit (use &quot;git add&quot; and/or &quot;git commit -a&quot;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is not quite as useful in day to day work, but it is pretty cool for
building arbitrary trees and whatnot.  We’ll explore how to use that to
do neat things in a future post when we talk more about some of the lower
level Git plumbing commands.&lt;/p&gt;</content:encoded></item><item><title>Replace Kicker</title><link>https://scottchacon.com/2010/03/17/replace/</link><guid isPermaLink="true">https://scottchacon.com/2010/03/17/replace/</guid><description>Another hidden Git feature: using git replace to transparently swap one object in for another.</description><pubDate>Wed, 17 Mar 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In another of my series of “seemingly hidden Git features”, I would like to
introduce &lt;code&gt;git replace&lt;/code&gt;.  Now, documentation exists for &lt;code&gt;git replace&lt;/code&gt;, but it
is rather unclear on what you would actually use it for (or even what it really
does), so let’s go through a couple of examples as it really is quite powerful.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;replace&lt;/code&gt; command basically will take one object in your Git database and
for most purposes replace it with another object.  This is most commonly useful
for replacing one commit in your history with another one.&lt;/p&gt;
&lt;p&gt;For example, say you want to split your history into one short history for
new developers and one much longer and larger history for people interested
in data mining.  You can graft one history onto the other by &lt;code&gt;replace&lt;/code&gt;ing
the earliest commit in the new line with the latest commit on the older one.  This
is nice because it means that you don’t actually have to rewrite every commit
in the new history, as you would normally have to do to join them together
(because the parentage effects the SHAs).&lt;/p&gt;
&lt;p&gt;Let’s try this out.  Let’s take an existing repository, split it into two
repositories, one recent and one historical, and then we’ll see how we can
recombine them without modifying the recent repositories SHA values via &lt;code&gt;replace&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We’ll use a simple repository with five simple commits:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ef989d8 fifth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c6e1e95 fourth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9c68fdc third commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;945704c second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c1822cf first commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want to break this up into two lines of history.  One line goes from commit
one to commit four - that will be the historical one.  The second line will
just be commits four and five - that will be the recent history.&lt;/p&gt;
&lt;center&gt;&lt;img src=&quot;/images/replace1.png&quot;&gt;&lt;/center&gt;
&lt;p&gt;Well, creating the historical history is easy, we can just put a branch in the
history and then push that branch to the master branch of a new remote repository.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git branch history c6e1e95&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline --decorate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ef989d8 (HEAD, master) fifth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c6e1e95 (history) fourth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9c68fdc third commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;945704c second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c1822cf first commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;center&gt;&lt;img src=&quot;/images/replace2.png&quot;&gt;&lt;/center&gt;
&lt;p&gt;Now we can push the new history branch to the master branch of our new repository:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git remote add history git@github.com:schacon/project-history.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git push history history:master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Counting objects: 12, done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Delta compression using up to 2 threads.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Compressing objects: 100% (4/4), done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Writing objects: 100% (12/12), 907 bytes, done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Total 12 (delta 0), reused 0 (delta 0)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Unpacking objects: 100% (12/12), done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;To git@github.com:schacon/project-history.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      history -&gt; master&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OK, so our history is published.  Now the harder part is truncating our current
history down so it’s smaller.  We need an overlap so we can replace a commit
in one with an equivalent commit in the other, so we’re going to truncate this
to just commits four and five (so commit four overlaps).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline --decorate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ef989d8 (HEAD, master) fifth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c6e1e95 (history) fourth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9c68fdc third commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;945704c second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c1822cf first commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I think it’s useful in this case to create a base commit that has instructions
on how to expand the history, so other developers know what to do if they
hit the first commit in the truncated history and need more.  So, what we’re
going to do is create an initial commit object as our base point with instructions,
then rebase the remaining commits (four and five) on top of it.  To do that,
we need to choose a point to split at, which for us is the third commit, which
is &lt;code&gt;9c68fdc&lt;/code&gt; in SHA-speak.  So, our base commit will be based off of that tree.
We can create our base commit using the &lt;code&gt;commit-tree&lt;/code&gt; command, which just takes
a tree and will give us a brand new, parentless commit object SHA back.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ echo &apos;get history from blah blah blah&apos; | git commit-tree 9c68fdc^{tree}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;622e88e9cbfbacfb75b5279245b9fb38dfea10cf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;center&gt;&lt;img src=&quot;/images/replace3.png&quot;&gt;&lt;/center&gt;
&lt;p&gt;OK, so now that we have a base commit, we can rebase the rest of our history
on top of that with &lt;code&gt;git rebase --onto&lt;/code&gt;.  The &lt;code&gt;--onto&lt;/code&gt; argument will be the
SHA we just got back from &lt;code&gt;commit-tree&lt;/code&gt; and the rebase point will be the third
commit (&lt;code&gt;9c68fdc&lt;/code&gt; again):&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git rebase --onto 622e88 9c68fdc&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;First, rewinding head to replay your work on top of it...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Applying: fourth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Applying: fifth commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;center&gt;&lt;img src=&quot;/images/replace4.png&quot;&gt;&lt;/center&gt;
&lt;p&gt;OK, so now we’ve re-written our recent history on top of a throw away base commit
that now has instructions in it on how to reconstitute the entire history if
we wanted to. Now let’s see what those instructions would be (here is where
&lt;code&gt;replace&lt;/code&gt; finally comes into play).&lt;/p&gt;
&lt;p&gt;So to get the history data after cloning this truncated repository, one would
have to add a remote for the historical repository and fetch:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git remote add history git://github.com/schacon/project-history.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git fetch history&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;From git://github.com/schacon/project-history.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      master     -&gt; history/master&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the collaborator would have their recent commits in the ‘master’ branch
and the historical commits in the ‘history/master’ branch.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;e146b5f fifth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;81a708d fourth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;622e88e get history from blah blah blah&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline history/master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c6e1e95 fourth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9c68fdc third commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;945704c second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c1822cf first commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To combine them, you can simply call &lt;code&gt;git replace&lt;/code&gt; with the commit you want
to replace and then the commit you want to replace it with.  So we want to
replace the ‘fourth’ commit in the master branch with the ‘fourth’ commit in
the ‘history/master’ branch:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git replace 81a708d c6e1e95&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, if you look at the history of the &lt;code&gt;master&lt;/code&gt; branch, it looks like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;e146b5f fifth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;81a708d fourth commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9c68fdc third commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;945704c second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c1822cf first commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cool, right?  Without having to change all the SHAs upstream, we were able to
replace one commit in our history with an entirely different commit and all the
normal tools (&lt;code&gt;bisect&lt;/code&gt;, &lt;code&gt;blame&lt;/code&gt;, etc) will work how we would expect them to.&lt;/p&gt;
&lt;center&gt;&lt;img src=&quot;/images/replace5.png&quot;&gt;&lt;/center&gt;
&lt;p&gt;Interestingly, it still shows &lt;code&gt;81a708d&lt;/code&gt; as the SHA, even though it’s actually
using the &lt;code&gt;c6e1e95&lt;/code&gt; commit data that we replaced it with.  Even if you run a
command like &lt;code&gt;cat-file&lt;/code&gt;, it will show you the replaced data:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git cat-file -p 81a708d&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;tree 7bc544cf438903b65ca9104a1e30345eee6c083d&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;parent 9c68fdceee073230f19ebb8b5e7fc71b479c0252&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;author Scott Chacon &amp;#x3C;schacon@gmail.com&gt; 1268712581 -0700&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;committer Scott Chacon &amp;#x3C;schacon@gmail.com&gt; 1268712581 -0700&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fourth commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember that the actual parent of &lt;code&gt;81a708d&lt;/code&gt; was our placeholder commit (&lt;code&gt;622e88e&lt;/code&gt;),
not &lt;code&gt;9c68fdce&lt;/code&gt; as it states here.&lt;/p&gt;
&lt;p&gt;The other cool thing is that this is kept in our references:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git for-each-ref&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit	refs/heads/master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c6e1e95051d41771a649f3145423f8809d1a74d4 commit	refs/remotes/history/master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit	refs/remotes/origin/HEAD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;e146b5f14e79d4935160c0e83fb9ebe526b8da0d commit	refs/remotes/origin/master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c6e1e95051d41771a649f3145423f8809d1a74d4 commit	refs/replace/81a708dd0e167a3f691541c7a6463343bc457040&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means that it’s easy to share our replacement with others, because we can
push this to our server and other people can easily download it.  This is not that
helpful in the history grafting scenario we’ve gone over here (since everyone
would be downloading both histories anyhow, so why seperate them?) but it can
be useful in other circumstances.  I’ll cover some other interesting scenarios
in another post - I think this is probably enough to process for now.&lt;/p&gt;</content:encoded></item><item><title>Git&apos;s Little Bundle of Joy</title><link>https://scottchacon.com/2010/03/10/bundles/</link><guid isPermaLink="true">https://scottchacon.com/2010/03/10/bundles/</guid><description>Moving commits between repositories with no network at all, by packaging a push into a git bundle file.</description><pubDate>Wed, 10 Mar 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The scenario is thus: you need to sneakernet a &lt;code&gt;git push&lt;/code&gt;.  Maybe your network
is down and you want to send changes to your co-workers.  Perhaps you’re working
somewhere onsite and don’t have access to the local network for security reasons.
Maybe your wireless/ethernet card just broke.  Maybe you don’t have access to
a shared server for the moment, you want to email someone updates and you don’t
want to transfer 40 commits via &lt;code&gt;format-patch&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Enter &lt;code&gt;git bundle&lt;/code&gt;.  The &lt;code&gt;bundle&lt;/code&gt; command will package up everything that would
normally be pushed over the wire with a &lt;code&gt;git push&lt;/code&gt; command into a binary file
that you can email or sneakernet around, then unbundle into another repository.&lt;/p&gt;
&lt;p&gt;Let’s see a simple example.  Let’s say you have a repository with two commits:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;commit 9a466c572fe88b195efd356c3f2bbeccdb504102&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Author: Scott Chacon &amp;#x3C;schacon@gmail.com&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Date:   Wed Mar 10 07:34:10 2010 -0800&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;commit b1ec3248f39900d2a406049d762aa68e9641be25&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Author: Scott Chacon &amp;#x3C;schacon@gmail.com&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Date:   Wed Mar 10 07:34:01 2010 -0800&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    first commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to send that repository to someone and you don’t have access to
a repository to push to, or simply don’t want to set one up, you can bundle it.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git bundle create repo.bundle master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Counting objects: 6, done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Delta compression using up to 2 threads.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Compressing objects: 100% (2/2), done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Writing objects: 100% (6/6), 441 bytes, done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Total 6 (delta 0), reused 0 (delta 0)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you have a file named &lt;code&gt;repo.bundle&lt;/code&gt; that has all the data needed to re-create
the repository.  You can email that to someone else, or put it on a USB drive
and walk it over.&lt;/p&gt;
&lt;p&gt;Now on the other side, say you are sent this &lt;code&gt;repo.bundle&lt;/code&gt; file and want to work
on the project.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git clone repo.bundle -b master repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Initialized empty Git repository in /private/tmp/bundle/repo/.git/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ cd repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9a466c5 second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;b1ec324 first commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had to specify &lt;code&gt;-b master&lt;/code&gt; because otherwise it couldn’t find the HEAD
reference for some reason, but you may not need to do that.  The point is, you
have now cloned directly from a file, rather than from a remote server.&lt;/p&gt;
&lt;p&gt;Now let’s say you do three commits on it and want to send the new commits back
via a bundle on a usb stick or email.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;71b84da last commit - second repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c99cf5b fourth commit - second repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;7011d3d third commit - second repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9a466c5 second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;b1ec324 first commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First we need to determine the range of commits we want to include in the bundle.
The easiest way would have been to drop a branch when we started, so we could
say &lt;code&gt;start_branch..master&lt;/code&gt; or &lt;code&gt;master ^start_branch&lt;/code&gt;, but if we didn’t we can
just list the starting SHA explicitly:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline master ^9a466c5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;71b84da last commit - second repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;c99cf5b fourth commit - second repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;7011d3d third commit - second repo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we have the list of commits we want to include in the bundle, let’s bundle
em up.  We do that with the &lt;code&gt;git bundle create&lt;/code&gt; command, giving it a filename
we want our bundle to be and the range of commits we want to go into it.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git bundle create commits.bundle master ^9a466c5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Counting objects: 11, done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Delta compression using up to 2 threads.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Compressing objects: 100% (3/3), done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Writing objects: 100% (9/9), 775 bytes, done.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Total 9 (delta 0), reused 0 (delta 0)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we will have a &lt;code&gt;commits.bundle&lt;/code&gt; file in our directory.  If we take that and
send it to our partner, she can then import it into the original repository,
even if more work has been done there in the meantime.&lt;/p&gt;
&lt;p&gt;When she gets the bundle, she can inspect it to see what it contains before she
imports it into her repository.  The first command is the &lt;code&gt;bundle verify&lt;/code&gt; command
that will make sure the file is actually a valid Git bundle and that you have
all the neccesary ancestors to reconstitute it properly.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git bundle verify ../commits.bundle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;The bundle contains 1 ref&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;The bundle requires these 1 ref&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;9a466c572fe88b195efd356c3f2bbeccdb504102 second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;../commits.bundle is okay&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the bundler had created a bundle of just the last two commits they had done,
rather than all three, the original repository would not be able to import it,
since it is missing requisite history.  The &lt;code&gt;verify&lt;/code&gt; command would have looked
like this instead:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git bundle verify ../commits-bad.bundle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;error: Repository lacks these prerequisite commits:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;error: 7011d3d8fc200abe0ad561c011c3852a4b7bbe95 third commit - second repo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, our first bundle is valid, so we can fetch in commits from it.  If you
want to see what branches are in the bundle that can be imported, there is also
a command to just list the heads:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git bundle list-heads ../commits.bundle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;verify&lt;/code&gt; sub-command will tell you the heads, too, as will a normal
&lt;code&gt;git ls-remote&lt;/code&gt; command, which you may have used for debugging before.  The point
is to see what can be pulled in, so you can use the &lt;code&gt;fetch&lt;/code&gt; or &lt;code&gt;pull&lt;/code&gt; commands
to import commits from this bundle.  Here we’ll fetch the ‘master’ branch of
the bundle to a branch named ‘other-master’ in our repository:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git fetch ../commits.bundle master:other-master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;From ../commits.bundle&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; * [new branch]      master     -&gt; other-master&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can see that we have the imported commits on the ‘other-master’ branch
as well as any commits we’ve done in the meantime in our own ‘master’ branch.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git log --oneline --decorate --graph --all&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;* 8255d41 (HEAD, master) third commit - first repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;| * 71b84da (other-master) last commit - second repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;| * c99cf5b fourth commit - second repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;| * 7011d3d third commit - second repo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;* 9a466c5 second commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;* b1ec324 first commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, &lt;code&gt;git bundle&lt;/code&gt; can be really useful for doing network-y, share-y operations
when you don’t have the proper network or shared repository to do so.&lt;/p&gt;</content:encoded></item><item><title>Rerere Your Boat...</title><link>https://scottchacon.com/2010/03/08/rerere/</link><guid isPermaLink="true">https://scottchacon.com/2010/03/08/rerere/</guid><description>Teaching Git to remember how you resolved a conflict so it can replay that resolution next time — git rerere.</description><pubDate>Mon, 08 Mar 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the things I didn’t touch on at all in the book is the &lt;code&gt;git rerere&lt;/code&gt;
functionality.  This also came up recently
during one of my trainings, and I realize that a lot of people probably could
use this, so I wanted to let you all now about it.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;http://ftp.sunet.se/pub/Linux/kernel.org/software/scm/git/docs/git-rerere.html&quot;&gt;&lt;code&gt;git rerere&lt;/code&gt;&lt;/a&gt;
functionality is a bit of a hidden feature (Git actually has
a lot of cool hidden features, if you haven’t figured that out yet).  The name
stands for “reuse recorded resolution” and as the name implies, it allows you
to ask Git to remember how you’ve resolved a hunk conflict so that the next time
it sees the same conflict, Git can automatically resolve it for you.&lt;/p&gt;
&lt;p&gt;There are a number of scenarios in which this functionality might be really
handy.  One of the examples that is mentioned in the documentation is if you
want to make sure a long lived topic branch will merge cleanly but don’t want
to have a bunch of intermediate merge commits. With &lt;code&gt;rerere&lt;/code&gt; turned on you
can merge occasionally, resolve the conflicts, then back out the merge. If
you do this continuously, then the final merge should be easy because &lt;code&gt;rerere&lt;/code&gt;
can just do everything for you automatically.&lt;/p&gt;
&lt;p&gt;This same tactic can be used if you want to keep a branch rebased so you don’t
have to deal with the same rebasing conflicts each time you do it.  Or if you
want to take a branch that you merged and fixed a bunch of conflicts and then
decide to rebase it instead - you likely won’t have to do all the same conflicts
again.&lt;/p&gt;
&lt;p&gt;The other situation I can think of is where you merge a bunch of evolving
topic branches together into a testable head occasionally.  If the tests fail,
you can rewind the merges and re-do them without the topic branch that made the
tests fail without having to re-resolve the conflicts again.&lt;/p&gt;
&lt;p&gt;To enable the rerere functionality, you simply have to run this config setting:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git config --global rerere.enabled true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also turn it on by creating the &lt;code&gt;.git/rr-cache&lt;/code&gt; directory in a specific
repository, but I think the config setting is clearer, and it can be done globally.&lt;/p&gt;
&lt;p&gt;Now let’s see a simple example.  If we have a file that looks like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#! /usr/bin/env ruby&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;def hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  puts &apos;hello world&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and in one branch we change the word ‘hello’ to ‘hola’, then in another branch
we change the ‘world’ to ‘mundo’.&lt;/p&gt;
&lt;img src=&quot;/images/rerere1.png&quot;&gt;
&lt;p&gt;When we merge the two branches together, we’ll get a merge conflict:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git merge i18n-world&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Auto-merging hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CONFLICT (content): Merge conflict in hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Recorded preimage for &apos;hello.rb&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Automatic merge failed; fix conflicts and then commit the result.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should notice the new line &lt;code&gt;Recorded preimage for FILE&lt;/code&gt; in there.  Otherwise
it should look exactly like a normal merge conflict.  At this point, &lt;code&gt;rerere&lt;/code&gt;
can tell us some stuff. Normally, you might run &lt;code&gt;git status&lt;/code&gt; at this point to
see what all conflicted:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git status&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# On branch master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Unmerged paths:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#   (use &quot;git reset HEAD &amp;#x3C;file&gt;...&quot; to unstage)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#   (use &quot;git add &amp;#x3C;file&gt;...&quot; to mark resolution)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#	both modified:      hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, &lt;code&gt;git rerere&lt;/code&gt; will also tell you what it has recorded the pre-merge
state for with &lt;code&gt;git rerere status&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git rerere status&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;hello.rb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &lt;code&gt;git rerere diff&lt;/code&gt; will show the current state of the resolution - what you
started with to resolve and what you’ve resolved it to.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git rerere diff&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;--- a/hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+++ b/hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;@@ -1,11 +1,11 @@&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; #! /usr/bin/env ruby&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; def hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-  puts &apos;hello mundo&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-=======&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C; HEAD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;   puts &apos;hola world&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-&gt;&gt;&gt;&gt;&gt;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+=======&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+  puts &apos;hello mundo&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+&gt;&gt;&gt;&gt;&gt;&gt;&gt; i18n-world&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also (and this isn’t really related to &lt;code&gt;rerere&lt;/code&gt;), you can use &lt;code&gt;ls-files -u&lt;/code&gt; to
see the conflicted files and the before, left and right versions:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git ls-files -u&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;100644 39804c942a9c1f2c03dc7c5ebcd7f3e3a6b97519 1	hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;100644 a440db6e8d1fd76ad438a49025a9ad9ce746f581 2	hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;100644 54336ba847c3758ab604876419607e9443848474 3	hello.rb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anyhow, so now you resolve it to just be “puts ‘hola mundo’” and you can run
the &lt;code&gt;rerere diff&lt;/code&gt; command again to see what rerere will remember:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git rerere diff&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;--- a/hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+++ b/hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;@@ -1,11 +1,7 @@&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; #! /usr/bin/env ruby&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; def hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-  puts &apos;hello mundo&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-=======&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-  puts &apos;hola world&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-&gt;&gt;&gt;&gt;&gt;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+  puts &apos;hola mundo&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So that basically says, when I see a hunk conflict that has ‘hello mundo’ on
one side and ‘hola world’ on the other, resolve it to ‘hola mundo’.&lt;/p&gt;
&lt;p&gt;Now we can mark it as resolved and commit it:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git add hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Recorded resolution for &apos;hello.rb&apos;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[master 68e16e5] Merge branch &apos;i18n&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see that it “Recorded resolution for FILE”.&lt;/p&gt;
&lt;img src=&quot;/images/rerere2.png&quot;&gt;
&lt;p&gt;Now, let’s undo
that merge and then rebase it on top of our master branch instead.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git reset --hard HEAD^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;HEAD is now at ad63f15 i18n the hello&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our merge is undone.  Now let’s rebase the topic branch.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git checkout i18n-world&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Switched to branch &apos;i18n-world&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git rebase master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;First, rewinding head to replay your work on top of it...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Applying: i18n one word&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Using index info to reconstruct a base tree...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Falling back to patching base and 3-way merge...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Auto-merging hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CONFLICT (content): Merge conflict in hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Resolved &apos;hello.rb&apos; using previous resolution.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Failed to merge in the changes.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Patch failed at 0001 i18n one word&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we got the same merge conflict like we expected, but check out the &lt;code&gt;Resolved FILE using previous resolution&lt;/code&gt; line.  If we look at the file, we’ll see that it’s
already been resolved:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ cat hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#! /usr/bin/env ruby&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;def hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  puts &apos;hola mundo&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also, &lt;code&gt;git diff&lt;/code&gt; will show you how it was automatically re-resolved:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git diff&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;diff --cc hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;index a440db6,54336ba..0000000&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;--- a/hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;+++ b/hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;@@@ -1,7 -1,7 +1,7 @@@&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  #! /usr/bin/env ruby&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  def hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;-   puts &apos;hola world&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; -  puts &apos;hello mundo&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;++  puts &apos;hola mundo&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&quot;/images/rerere3.png&quot;&gt;
&lt;p&gt;You can also recreate the conflicted file state with the &lt;code&gt;checkout&lt;/code&gt; command:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git checkout --conflict=merge hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ cat hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#! /usr/bin/env ruby&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;def hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C; ours&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  puts &apos;hola world&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;=======&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  puts &apos;hello mundo&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; theirs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That might be a new command to you as well, the &lt;code&gt;--conflict&lt;/code&gt; option to
&lt;code&gt;git checkout&lt;/code&gt;.  You can actually have &lt;code&gt;checkout&lt;/code&gt; do a couple of things in
this situation to help you resolve conflicts.  Another interesting value for
that option is ‘diff3’, which will give you left, right and common to help you
resolve the conflict manually:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git checkout --conflict=diff3 hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ cat hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#! /usr/bin/env ruby&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;def hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C;&amp;#x3C; ours&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  puts &apos;hola world&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;|||||||&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  puts &apos;hello world&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;=======&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  puts &apos;hello mundo&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; theirs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anyhow, then you can re-resolve it by just running &lt;code&gt;rerere&lt;/code&gt; again:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git rerere&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Resolved &apos;hello.rb&apos; using previous resolution.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ cat hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#! /usr/bin/env ruby&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;def hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  puts &apos;hola mundo&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Magical re-resolving! Then you can add and continue the rebase to complete it.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git add hello.rb&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git rebase --continue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Applying: i18n one word&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, if you do a lot of re-merges, or want to keep a topic branch up to date
with your master branch without a ton of merges, or you rebase often or any
of the above, turn on &lt;code&gt;rerere&lt;/code&gt; to help your life out a bit.&lt;/p&gt;</content:encoded></item><item><title>Smart HTTP Transport</title><link>https://scottchacon.com/2010/03/04/smart-http/</link><guid isPermaLink="true">https://scottchacon.com/2010/03/04/smart-http/</guid><description>The new Smart HTTP transport in Git, and how it makes cloning and fetching over plain HTTP fast.</description><pubDate>Thu, 04 Mar 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I was done writing Pro Git, the only transfer protocols that existed were
the &lt;code&gt;git://&lt;/code&gt;, &lt;code&gt;ssh://&lt;/code&gt; and basic &lt;code&gt;http://&lt;/code&gt; transports.  I wrote about the basic
strengths and weaknesses of each in &lt;a href=&quot;/book/ch4-1.html&quot;&gt;Chapter 4&lt;/a&gt;.
At the time, one of the big differences between Git and most other VCS’s was
that HTTP was not a mainly used protocol - that’s because it was read-only and
very inefficient.  Git would simply use the webserver to ask for individual
objects and packfiles that it needed.  It would even ask for big packfiles even
if it only needed one object from it.&lt;/p&gt;
&lt;p&gt;As of the release of version 1.6.6 at the end of last year, however, Git can
now use the HTTP protocol just about as efficiently as the &lt;code&gt;git&lt;/code&gt; or &lt;code&gt;ssh&lt;/code&gt;
versions (thanks to the amazing work by Shawn Pearce, who also happened to have
been the technical editor of Pro Git).  Amusingly, it has been given very little
fanfare - the release notes for 1.6.6 state only this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;* &quot;git fetch&quot; over http learned a new mode that is different from the&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  traditional &quot;dumb commit walker&quot;.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which is a huge understatement, given that I think this will become the
standard Git protocol in the very near future.  I believe this because it’s
both efficient and can be run either secure and authenticated (https) or open
and unauthenticated (http).  It also has the huge advantage that most
firewalls have those ports (80 and 443) open already and normal users don’t
have to deal with &lt;code&gt;ssh-keygen&lt;/code&gt; and the like.  Once most clients have updated
to at least v1.6.6, &lt;code&gt;http&lt;/code&gt; will have a big place in the Git world.&lt;/p&gt;
&lt;h2&gt;What is &quot;Smart&quot; HTTP?&lt;/h2&gt;
&lt;p&gt;Before version 1.6.6, Git clients, when you clone or fetch over HTTP would
basically just do a series of GETs to grab individual objects and packfiles on
the server from bare Git repositories, since it knows the layout of the repo.
This functionality is documented fairly completely in &lt;a href=&quot;http://progit.org/book/ch9-6.html&quot;&gt;Chapter 9&lt;/a&gt;.
Conversations over this protocol used to look like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git clone http://github.com/schacon/simplegit-progit.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Initialized empty Git repository in /private/tmp/simplegit-progit/.git/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;got ca82a6dff817ec66f44342007202690a93763949&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;walk ca82a6dff817ec66f44342007202690a93763949&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;got 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Getting alternates list for http://github.com/schacon/simplegit-progit.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Getting pack list for http://github.com/schacon/simplegit-progit.git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Getting index for pack 816a9b2334da9953e530f27bcac22082a9f5b835&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Getting pack 816a9b2334da9953e530f27bcac22082a9f5b835&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; which contains cfda3bf379e4f8dba8717dee55aab78aef7f4daf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;walk 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;walk a11bef06a3f659402fe7563abf99ad00de2209e6&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is a completly passive server, and if the client needs one object in a packfile
of thousands, the server cannot pull the single object out, the client is forced
to request the entire packfile.&lt;/p&gt;
&lt;img src=&quot;/images/smarthttp1.png&quot;&gt;
&lt;p&gt;In contrast, the smarter protocols (&lt;code&gt;git&lt;/code&gt; and &lt;code&gt;ssh&lt;/code&gt;) would instead have a
conversation with the &lt;code&gt;git upload-pack&lt;/code&gt; process on the server which would
determine the exact set of objects the client needs and build a custom packfile
with just those objects and stream it over.&lt;/p&gt;
&lt;p&gt;The new clients will now send a request with an extra GET parameter that older
servers will simply ignore, but servers running the smart CGI will recognize
and switch modes to a multi-POST mode that is similar to the conversation that
happens over the &lt;code&gt;git&lt;/code&gt; protocol.  Once this series of POSTs is complete, the
server knows what objects the client needs and can build a custom packfile and
stream it back.&lt;/p&gt;
&lt;img src=&quot;/images/smarthttp2.png&quot;&gt;
&lt;p&gt;Furthermore, in the olden days if you wanted to push over http, you had to setup
a DAV-based server, which was rather difficult and also pretty inefficient compared
to the smarter protocols.  Now you can push over this CGI, which again is very
similar to the push mechanisms for the &lt;code&gt;git&lt;/code&gt; and &lt;code&gt;ssh&lt;/code&gt; protocols.  You simply
have to authenticate via an HTTP-based method, like basic auth or the like
(assuming you don’t want your repository to be world-writable).&lt;/p&gt;
&lt;p&gt;The rest of this article will explain setting up a server with the “smart”-http
protocol, so you can test out this cool new feature.  This feature is referred
to as “smart” HTTP vs “dumb” HTTP because it requires having the Git binary
installed on the server, where the previous incantation of HTTP transfer
required only a simple webserver.  It has a real conversation with the client,
rather than just dumbly pushing out data.&lt;/p&gt;
&lt;h2&gt;Setting up Smart HTTP&lt;/h2&gt;
&lt;p&gt;So, Smart-HTTP is basically just enabling the new CGI script that is provided
with Git called
&lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html&quot;&gt;&lt;code&gt;git-http-backend&lt;/code&gt;&lt;/a&gt;
on the server.  This CGI will read the path and
headers sent by the revamped &lt;code&gt;git fetch&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt; binaries who have
learned to communicate in a specific way with a smart server.  If the CGI sees
that the client is smart, it will communicate smartly with it, otherwise it will
simply fall back to the dumb behavior (so it is backward compatible for reads
with older clients).&lt;/p&gt;
&lt;p&gt;To set it up, it’s best to walk through the instructions on the
&lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html&quot;&gt;&lt;code&gt;git-http-backend&lt;/code&gt;&lt;/a&gt;
documentation page.  Basically, you have to install Git v1.6.6 or higher on
a server with an Apache 2.x webserver (it has to be Apache, currently - other
CGI servers don’t work, last I checked).  Then you add something similar to this
to your http.conf file:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SetEnv GIT_PROJECT_ROOT /var/www/git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;SetEnv GIT_HTTP_EXPORT_ALL&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then you’ll want to make writes be authenticated somehow, possibly with an Auth
block like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;LocationMatch &quot;^/git/.*/git-receive-pack$&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        AuthType Basic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        AuthName &quot;Git Access&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        Require group committers&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&amp;#x3C;/LocationMatch&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is all that is really required to get this running.  Now you have a smart
http-based Git server that can do anonymous reads and authenticated writes with
clients that have upgraded to 1.6.6 and above.
How awesome is that?  The &lt;a href=&quot;http://www.kernel.org/pub/software/scm/git/docs/git-http-backend.html&quot;&gt;documentation&lt;/a&gt;
goes over more complex examples, like making it work with GitWeb and accelerating
the dumb fallback reads, if you’re interested.&lt;/p&gt;
&lt;h2&gt;Rack-based Git Server&lt;/h2&gt;
&lt;p&gt;If you’re not a fan of Apache or you’re running some other web server, you may
want to take a look at an app that I wrote called &lt;a href=&quot;http://github.com/schacon/grack&quot;&gt;Grack&lt;/a&gt;, which
is a &lt;a href=&quot;http://rack.rubyforge.org/&quot;&gt;Rack&lt;/a&gt;-based application for Smart-HTTP Git.
&lt;a href=&quot;http://rack.rubyforge.org/&quot;&gt;Rack&lt;/a&gt; is a generic webserver interface
for Ruby (similar to WSGI for Python) that has adapters for a ton of web servers.
It basically replaces &lt;code&gt;git http-backend&lt;/code&gt; for non-Apache servers that can’t run it.&lt;/p&gt;
&lt;p&gt;This means that I can write the web handler independent of the web server and it
will work with any web server that has a Rack handler.  This currently means any FCGI server,
Mongrel (and EventedMongrel and SwiftipliedMongrel), WEBrick, SCGI, LiteSpeed,
Thin, Ebb, Phusion Passenger and Unicorn.  Even cooler, using
&lt;a href=&quot;http://caldersphere.rubyforge.org/warbler/classes/Warbler.html&quot;&gt;Warbler&lt;/a&gt;
and JRuby, you can generate a WAR file that is deployable in any Java web
application server (Tomcat, Glassfish, Websphere, JBoss, etc).&lt;/p&gt;
&lt;p&gt;So, if you don’t use Apache and you are interested in a Smart-HTTP Git server,
you may want to check out Grack.  At &lt;a href=&quot;http://github.com&quot;&gt;GitHub&lt;/a&gt;, this
is the adapter we’re using to eventually implement Smart-HTTP support for all
the GitHub repositories. (It’s currently a tad bit behind, but I’ll be starting
up on it again soon as I get it into production at GitHub - send pull requests if you
find any issues)&lt;/p&gt;
&lt;p&gt;Grack is about half as fast as the Apache version for simple ref-listing stuff,
but we’re talking 10ths of a second.  For most clones and pushes, the data transfer
will be the main time-sink, so the load time of the app should be negligible.&lt;/p&gt;
&lt;h2&gt;In Conclusion&lt;/h2&gt;
&lt;p&gt;I think HTTP based Git will be a huge part of the future of Git, so if you’re
running your own Git server, you should really check it out.  If you’re not,
GitHub and I’m sure other hosts will soon be supporting it - upgrade your Git
client to 1.7ish soon so you can take advantage of it when it happens.&lt;/p&gt;</content:encoded></item><item><title>Undoing Merges</title><link>https://scottchacon.com/2010/03/02/undoing-merges/</link><guid isPermaLink="true">https://scottchacon.com/2010/03/02/undoing-merges/</guid><description>Kicking off a series of Git tips with the tricky business of undoing a merge that&apos;s already been committed.</description><pubDate>Tue, 02 Mar 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I would like to start writing more here about general Git tips, tricks and
upcoming features.  There has actually been a lot of cool stuff that has
happened since the book was first published, and a number of interesting
things that I didn’t get around to covering in the book.  I figure if I start
blogging about the more interesting stuff, it should serve as a pretty handy
guide should I ever start writing a second edition.&lt;/p&gt;
&lt;p&gt;For the first such post, I’m going to cover a topic that was asked about
at a training I did recently.  The question was about a workflow where long
running branches are merged occasionally, much like the &lt;a href=&quot;http://progit.org/book/ch5-3.html#largemerging_workflows&quot;&gt;Large Merging&lt;/a&gt;
workflow that I describe in the book.  They asked how to unmerge a branch,
either permenantly or allowing you to merge it in later.&lt;/p&gt;
&lt;p&gt;You can actually do this a number of ways.  Let’s say you have history that
looks something like this:&lt;/p&gt;
&lt;img src=&quot;http://git-scm.com/images/unmerge1.png&quot;&gt;
&lt;p&gt;You have a couple of topic branches that you have developed and then integrated
together by a series of merges.  Now you want to revert something back in the
history, say ‘C10’ in this case.&lt;/p&gt;
&lt;p&gt;The first way to solve the problem could be to rewind ‘master’ back to C8 and
then merge the remaining two lines back in again.  This requires that anyone
you’re collaborating with knows how to handle rewound heads, but if that’s not
an issue, this is a perfectly viable solution.  This is basically how the ‘pu’
branch is handled in the Git project itself.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git checkout master&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git reset --hard [sha_of_C8]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git merge jk/post-checkout&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git merge db/push-cleanup&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you rewind and remerge, you’ll instead have a history that looks more
like this:&lt;/p&gt;
&lt;img src=&quot;http://git-scm.com/images/unmerge2.png&quot;&gt;
&lt;p&gt;Now you can go back and work on that newly unmerged line and merge it again
at a later point, or perhaps ignore it entirely.&lt;/p&gt;
&lt;h2&gt;Reverting a Merge&lt;/h2&gt;
&lt;p&gt;However, what if you didn’t find this out until later, or perhaps you or one
of your collaborators have done work after this merge series? What if your
history looks more like this:&lt;/p&gt;
&lt;img src=&quot;http://git-scm.com/images/unmerge3.png&quot;&gt;
&lt;p&gt;Now you either have to revert one of the merges, or go back, remerge and then
cherry-pick the remaining changes again (C9 and C10 in this case), which is
confusing and difficult, especially if there are a lot of commits after those
merges.&lt;/p&gt;
&lt;p&gt;Well, it turns out that Git is actually pretty good at reverting an entire merge.
Although you’ve probably only used the &lt;code&gt;git revert&lt;/code&gt; command to revert a single
commit (if you’ve used it at all), you can also use it to revert merge commits.&lt;/p&gt;
&lt;p&gt;All you have to do is specify the merge commit you want to revert and the parent
line you want to keep.  Let’s say that we want to revert the merge of the
&lt;code&gt;jk/post-checkout&lt;/code&gt; line.  We can do so like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git revert -m 1 [sha_of_C8]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Finished one revert.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[master 88edd6d] Revert &quot;Merge branch &apos;jk/post-checkout&apos;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; 1 files changed, 0 insertions(+), 2 deletions(-)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That will introduce a new commit that undoes the changes introduced by merging
in the branch in the first place - sort of like a reverse cherry pick of all
of the commits that were unique to that branch.  Pretty cool.&lt;/p&gt;
&lt;img src=&quot;http://git-scm.com/images/unmerge4.png&quot;&gt;
&lt;p&gt;However, we’re not done.&lt;/p&gt;
&lt;h2&gt;Reverting the Revert&lt;/h2&gt;
&lt;p&gt;Let’s say now that you want to re-merge that work again.  If you try to merge
it again, Git will see that the commits on that branch are in the history and
will assume that you are mistakenly trying to merge something you already have.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git merge jk/post-checkout&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Already up-to-date.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oops - it did nothing at all. Even more confusing is if you went back and committed on that branch
and then tried to merge it in, it would only introduce the changes &lt;em&gt;since&lt;/em&gt; you
originally merged.&lt;/p&gt;
&lt;img src=&quot;http://git-scm.com/images/unmerge5.png&quot;&gt;
&lt;p&gt;Gah.  Now that’s really a strange state and is likely to cause a bunch of
conflicts or confusing errors.  What you want to do instead is revert the revert
of the merge:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git revert 88edd6d&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Finished one revert.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[master 268e243] Revert &quot;Revert &quot;Merge branch &apos;jk/post-checkout&apos;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; 1 files changed, 2 insertions(+), 0 deletions(-)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&quot;http://git-scm.com/images/unmerge6.png&quot;&gt;
&lt;p&gt;Cool, so now we’ve basically reintroduced everything that was in the branch
that we had reverted out before.  Now if we have more work on that branch
in the meantime, we can just re-merge it.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-light&quot; style=&quot;background-color:#fff;color:#24292e; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;$ git merge jk/post-checkout&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Auto-merging test.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Merge made by recursive.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; test.txt |    1 +&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; 1 files changed, 1 insertions(+), 0 deletions(-)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;img src=&quot;http://git-scm.com/images/unmerge7.png&quot;&gt;
&lt;p&gt;So, I hope that’s helpful.  This can be particularly useful if you have a merge-heavy
development process.  In fact, if you work mostly in topic branches before
merging for intergration purposes, you may want to use the &lt;code&gt;git merge --no-ff&lt;/code&gt;
option so that the first merge is not a fast forward and can be reverted out
in this manner.&lt;/p&gt;
&lt;p&gt;Until next time.&lt;/p&gt;</content:encoded></item><item><title>This Year</title><link>https://scottchacon.com/2009/12/17/this-year/</link><guid isPermaLink="true">https://scottchacon.com/2009/12/17/this-year/</guid><description>A year-end reflection, after mostly stepping away from blogging through 2009.</description><pubDate>Thu, 17 Dec 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So I basically stopped blogging this year, more or less since I started
working for &lt;a href=&quot;http://github.com&quot;&gt;GitHub&lt;/a&gt;. I’ve been busy, what can I say? I
wrote a technical book through APress publishing called &lt;a href=&quot;http://progit.org&quot;&gt;Pro
Git&lt;/a&gt;, I’ve traveled all over the world speaking at dozens
of conferences on Git and GitHub, and my wife had our first child, Josephine.&lt;/p&gt;
&lt;p&gt;I think writing the book is what really killed the blogging. I was writing for
hours every day or two, then spending hours each day the next week reading
over comments telling me how I was both technically wrong and generally bad at
writing in English. Sitting down and writing a blog post wasn’t really what I
wanted to do after that.&lt;/p&gt;
&lt;p&gt;Travel-wise it’s been a pretty amazing year. According to
&lt;a href=&quot;http://tripit.com/people/schacon&quot;&gt;Tripit&lt;/a&gt;, I’ve traveled 74,360 mi to 31
cities in 11 countries over the course of 80 days this year. That’s almost a
quarter of the year on the road. This year alone I’ve been to Shanghai, Tokyo,
Amsterdam, Edinburgh, Berlin (twice), Poznan, Stockholm, Oslo, London (twice),
Barcelona, Madrid, plus San Diego, Boston, Vegas, Chicago, New York, Virginia
and Honolulu.&lt;/p&gt;
&lt;p&gt;Physically I’ve changed kind of a lot, too. I got more or less in shape again
this year, mostly due to a simple routine of running and the &lt;a href=&quot;http://hundredpushups.com/&quot;&gt;hundred
pushups&lt;/a&gt; program. Though I haven’t quite yet made
it all the way to 100 pushups in a row, I can now do about 70 or 80, which is
pretty cool. I also lost about 30 pounds this year, from a high of about 195
when my baby was born to about 165 now.&lt;/p&gt;
&lt;p&gt;Finally, there is Josephine. When the year started, Jessica was a couple
months pregnant and now we have a little 5 month old baby girl.  I was never
really a fan of all the “it changes your whole life” comments, but it really
does.  It is by far the most amazing thing that has ever happened to me.
She even has a little passport already and has traveled to Hawaii and Spain
with us.&lt;/p&gt;
&lt;p&gt;Overall, this has been one of the most amazing years of my life, and I’m
really quite sad that I didn’t write more during it.  I will definitely try
to reverse that trend over the coming year as I have more amazing trips
coming up (for example, Australia, New Zealand, Paris, Ireland just in the
next few months), and there will be no shortage of baby stories and GitHub
amazingness.&lt;/p&gt;
&lt;p&gt;Looking forward to this year, everybody. Skál!&lt;/p&gt;</content:encoded></item><item><title>Translate This</title><link>https://scottchacon.com/2009/08/19/translate-this/</link><guid isPermaLink="true">https://scottchacon.com/2009/08/19/translate-this/</guid><description>Git makes forking and contributing easy — a call to help translate the Pro Git book into more languages.</description><pubDate>Wed, 19 Aug 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the things I love about Git is how easy it is to fork and contribute.
More than once on GitHub I’ve put up some content and a handful of helpful
souls go about translating it into another language.  Well, it’s happened
again with Pro Git.  Since I released it under the Creative Commons license
and put the code &lt;a href=&quot;http://github.com/progit/progit&quot;&gt;up on GitHub&lt;/a&gt;,
several people have forked and started translating the book into other
languages such as
&lt;a href=&quot;/book/de&quot;&gt;German&lt;/a&gt;,
&lt;a href=&quot;/book/zh&quot;&gt;Chinese&lt;/a&gt;,
&lt;a href=&quot;/book/ja&quot;&gt;Japanese&lt;/a&gt; and
&lt;a href=&quot;/book/ru&quot;&gt;Russian&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’ve been merging this work into &lt;a href=&quot;http://github.com/progit/progit&quot;&gt;my
main repository&lt;/a&gt; to make it easier for people who want to help and now I’ve
started publishing these translations on the main progit.org website.  At the
bottom of the page in the footer is now a list of ongoing translations (none
of them are complete yet) that link to the translated content.&lt;/p&gt;
&lt;p&gt;If you know another language and would like to make this Git resource
available to others in that language, please either jump into helping with
one of the ongoing translations or start a new one - I’ll begin publishing it
at progit.org once it gets going.&lt;/p&gt;
&lt;p&gt;The process to help with translations is to fork
&lt;a href=&quot;http://github.com/progit/progit&quot;&gt;my main content repository&lt;/a&gt;,
clone your new fork, make some changes and then send me a pull request through
GitHub.  Actually, even if you don’t send me a pull request, I tend to check
out my network graph every few days and pull in things that look good.&lt;/p&gt;
&lt;p&gt;There have so far been more than 30 people who have contributed translated
material, errata or formatting fixes for my book since I put it up here -
thanks to everyone!  However, special thanks for all the help in translating
the book so far to Mike Limansky (ru), Victor Portnov (ru), Egor Levichev (ru), Sven Fuchs (de), marcel (de), Fritz Thielemann (de), Yu Inao (ja), chunzi(zh) and DaNmarner (zh).  Also huge thanks to Shane (duairc)
for all the PDF creation software work.  Keep it up!&lt;/p&gt;</content:encoded></item><item><title>The Gory Details</title><link>https://scottchacon.com/2009/07/28/the-gory-details/</link><guid isPermaLink="true">https://scottchacon.com/2009/07/28/the-gory-details/</guid><description>Overwhelmed by the response to Pro Git&apos;s launch — 10,000 visitors on day one — plus notes on what comes next.</description><pubDate>Tue, 28 Jul 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;First of all, thanks to everyone for spreading the word about this book and this site - I got &lt;em&gt;way&lt;/em&gt; more attention on the first day live than I expected I might.  More than 10,000 individuals visited the site in the first 24 hours of it being online for over 38k pageviews.  We got on Hacker News, Reddit and Delicious Popular aggregators, not to mention the Twitterverse.&lt;/p&gt;
&lt;p&gt;In the comments of the initial post here, a user asked me about the “gory details” of writing this book. Specifically about “what tools you used to create the book and its figures”.  So here it is.&lt;/p&gt;
&lt;p&gt;Somewhere else I read that someone liked that I used Markdown for writing the book, as you can download the Markdown source for the book &lt;a href=&quot;http://github.com/progit/progit&quot;&gt;at GitHub&lt;/a&gt;.  Well, the entire writing process was unfortunately not done in Markdown.  At Apress most of the editing and review process is still MS Word centric.  Since I threw hissy fits at Word for the first few chapters the very nice people at Apress allowed me to write the remainder of the book in Markdown initially and the technical reviews were done via a Git repository on GitHub using the Markdown source.&lt;/p&gt;
&lt;p&gt;So, for most of the book, the process was : I would write the first draft of each chapter in Markdown, two reviewers would add comments inline.  Then, I would fix whatever they commented on then move the text into Word to submit it to the copy editor.  The copy editor would review the Word document and let the technical editor have another pass, then I would fix up anything they commented on.  Finally I get the chapter back in PDF form and I would do a final pass.  Then I took that text and put it back in Markdown to generate HTML from for the website.&lt;/p&gt;
&lt;p&gt;Fun, huh?&lt;/p&gt;
&lt;p&gt;For the diagrams, I always use OmniGraffle.  I personally think it’s one of the most amazing pieces of software ever created - I love it.  I think normally an Apress designer would take whatever the author sketched and redo them, but in this case we actually just used the diagrams that I made.  I just added the .graffle file to the &lt;a href=&quot;http://github.com/progit/progit&quot;&gt;GitHub repo&lt;/a&gt; if you’re interested in it.&lt;/p&gt;</content:encoded></item><item><title>Do What You Want</title><link>https://scottchacon.com/2009/02/19/do-what-you-want/</link><guid isPermaLink="true">https://scottchacon.com/2009/02/19/do-what-you-want/</guid><description>Reflections sparked by Chris Wanstrath&apos;s StartupRiot keynote on building a company by doing what you love.</description><pubDate>Thu, 19 Feb 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My friend Chris Wanstrath gave the &lt;a href=&quot;http://gist.github.com/67060&quot;&gt;keynote talk&lt;/a&gt; yesterday at StartupRiot on his lessons learned in building a successful startup company.  A comment on the &lt;a href=&quot;http://news.ycombinator.com/item?id=487686&quot;&gt;Hacker News&lt;/a&gt; post about it summed the keynote up thusly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;dont be afraid to fail, there is lots to learn from it, follow what you
genuinely love and use, and do what works for you best&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I largely agree, and I like the talk.  What I wanted to comment on was what I read on the same Hacker News post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I know that’s a popular piece of advice given around here (though HN has
an interesting mix of “stuffy” academia and “Wild West” hacker culture),
and I felt the same way when I was younger. I thought I knew all the CS
I needed to know to develop the stuff I’d want to develop. But I want to
say that I am so overwhelmingly happy I have done CS at the college level.
I knew so little back then (and still). It scares me that I almost
neglected the path of inquiry that is /the/ single most interesting
intellectual topic in my life.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That commenter was talking about a part at the end of Chris’s talk where
he mentions a kid that was trying to decide if he should go to college to
learn CS or just work at a startup.  Chris’s advice is to :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do whatever you want.
Don’t take VC because Twitter did. Don’t piss off investors because
signals likes to. Do whatever you want, whatever works best for you, what
makes you the most comfortable and seems like the best idea.
Do what you love.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I love the advice to not do what others did just because that is what is done, but I would probably advise that guy to go to college.  Not because it’s the only way to get things done, but because it’s a great way to get a broader perspective.  I’ve found that the better programmers I know are people that have interests in things other than programming.  I discovered tons of things that I would not have normally been interested in but made me think differently because of classes I took in college.&lt;/p&gt;</content:encoded></item></channel></rss>