<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"><title><![CDATA[ 개발새발 ]]></title><link href="http://dogfeet.github.io" /><updated>2013-05-21T12:48:54.022Z</updated><id>http://dogfeet.github.io</id><author><name>Changwoo Park</name><email>pismute@gmail.com</email></author><author><name>Sean Lee</name><email>sean@weaveus.com</email></author><author><name>Yongjae Choi</name><email>mage@weaveus.com</email></author><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/github/dogfeet" /><feedburner:info uri="github/dogfeet" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry><title><![CDATA[ 프로 Git 한글책 출간 ]]></title><link href="http://feedproxy.google.com/~r/github/dogfeet/~3/4Psr7zkAbUA/progit-ko-book-published.html" /><updated>2013-05-20T15:00:00.000Z</updated><id>http://dogfeet.github.io/articles/2013/progit-ko-book-published.html</id><content type="html">
&lt;p&gt;Pro Git 한글 번역이 &lt;a href="http://www.insightbook.co.kr/"&gt;인사이트&lt;/a&gt;의 도움을 받아 종이책으로 &lt;a href="http://www.insightbook.co.kr/post/5633"&gt;프로 Git&lt;/a&gt; 으로 출간되었습니다. 학생분들은 도서관에 직장인은 도서구매부에 얼른얼른 구매 신청해주세요! ㅎㅎ&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dogfeet.github.io/articles/2013/progit-ko-book-published/progit.jpg" alt=""/&gt; &lt;img src="/articles/2013/progit-ko-book-published/devops.png" alt=""/&gt;&lt;/p&gt;

&lt;h2&gt;프로 Git 출간 이야기&lt;/h2&gt;

&lt;p&gt;이미 Pro Git의 한글 번역 전문이 이 블로그 및 github을 통해 오픈소스로 공개되어 있지만, 인사이트 출판사의 도움으로 종이책으로 출간되었습니다. 종이책으로 나올 수 있었던 뒷 이야기는 인사이트 블로그의 &lt;a href="http://www.insightbook.co.kr/post/5633"&gt;&lt;프로 Git&gt;, 이미 공개된 내용을 왜 책으로 만들었냐고요?&lt;/a&gt; 글을 통해 살펴보실 수 있습니다.&lt;/p&gt;

&lt;p&gt;종이책으로 출간된 책의 기본 내용은 동일하지만 몇 번의 교정 과정을 더 거쳤고 이미지도 업데이트 되었으며 부록으로 미처 다루지 못한 몇 가지 내용을 더 담아두었습니다.&lt;/p&gt;

&lt;p&gt;주요 오프라인이나 온라인 서점을 통해 구입하실 수 있구요 출간된 것은 4월 중순인데 이렇게 글로 소식을 올리는 것이 늦어졌습니다. &lt;sup&gt;^&lt;/sup&gt;&lt;/p&gt;

&lt;h2&gt;또 다른 번역서, 데브옵스 출간 소식&lt;/h2&gt;

&lt;p&gt;또 하나, &amp;lsquo;프로 Git'에 이어 저희 팀원분이 번역한 &lt;a href="http://www.wikibook.co.kr/wiki/Wiki.jsp?page=DevOps"&gt;데브옵스&lt;/a&gt;도 5월 말에 출간됩니다. 데브옵스에 대한 다양한 문제 상황과 해결법을 다루고 있어 관심있는 분께는 많은 도움이 되리라 생각합니다. 현재 예약판매 중이며 곧 온라인과 오프라인 서점에서 구입하실 수 있습니다.&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/github/dogfeet/~4/4Psr7zkAbUA" height="1" width="1"/&gt;</content><feedburner:origLink>http://dogfeet.github.io/articles/2013/progit-ko-book-published.html</feedburner:origLink></entry><entry><title><![CDATA[ git: password caching ]]></title><link href="http://feedproxy.google.com/~r/github/dogfeet/~3/OH83ozFMrws/git-password-caching.html" /><updated>2013-03-27T15:00:00.000Z</updated><id>http://dogfeet.github.io/articles/2013/git-password-caching.html</id><content type="html">
&lt;p&gt;요즘은 뭐든 Smart한 시대인지라 http(s) 프로토콜도 Smart하지 않을 수 없다(&lt;a href="http://git-scm.com/2010/03/04/smart-http.html"&gt;Smart http&lt;/a&gt;). &lt;a href="http://git-scm.com/book/ko"&gt;Pro Git&lt;/a&gt;에서는 http(s)가 Smart하지 않다고 설명하지만 그건 &lt;a href="http://git-scm.com/book/ko"&gt;Pro Git&lt;/a&gt;이 출간되고 나서 만들어진 거라 책에는 내용이 빠져 있다. 이제는 ssh를 사용하든 http(s)를 사용하든 효율의 차이는 없다.&lt;/p&gt;

&lt;p&gt;ssh는 회사 방화벽에서 막아버릴 수도 있고 익명접근도 허용하지 않지만, http(s)는 그런 게 없다. GitHub도 이제는 ssh가 아닌 http(s)가 기본이다(https 주소를 먼저 보여준다):&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dogfeet.github.io/articles/2013/git-password-caching/http-ssh.png" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;그런데 ssh는 인증서를 사용하면 ssh-agent를 사용하면 암호를 한 번만 입력할 수 있는데, http(s)에서는 Basic 인증을 통해서 인증하는지라 다른 메커니즘이 필요하다. 이 글은 GitHub help 페이지에 있는 Password Caching을 요약한 글이다.&lt;/p&gt;

&lt;h2&gt;Password Caching.&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Git버전이 1.7.10 이상 돼야 한다. 그래야 이 기능을 사용할 수 있다.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;나는 주로 ssh를 사용하므로 http(s)를 잘 사용하지 않는다. ssh에 익숙해져서 http(s)가 더 어색하다. 하지만, gist에서는 http(s)를 사용하는 것이 편하다. GitHub이 Gist에서는 git 프로토콜 주소를 안내하지 않고 있다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2013/git-password-caching/gist-clone.png" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;화면의 주소는 다음과 같은 형태다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="label"&gt;https:&lt;/span&gt;//gist&lt;span class="preprocessor"&gt;.github&lt;/span&gt;&lt;span class="preprocessor"&gt;.com&lt;/span&gt;/xxxxxxxxxxxxxxxxxxxx&lt;span class="preprocessor"&gt;.git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ssh 프로토콜로도 사용할 수 있긴 하다. 단지 복사해서 붙여 넣을 수 없을 뿐이다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git@gist&lt;span class="variable"&gt;.github&lt;/span&gt;&lt;span class="variable"&gt;.com&lt;/span&gt;:xxxxxxxxxxxxxxxxxxxx&lt;span class="variable"&gt;.git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;gist는 git을 이용하지만 전문 버전관리 도구가 아니라 프로토타이핑 도구라서 http(s) 프로토콜만으로도 충분할 수 있다. 그래도 명령을 실행할 때마다 암호를 입력하는 일은 좀 불편하다. http(s)에도 ssh처럼 사용할 수 있는 매우 편리한 방법이 있다.&lt;/p&gt;

&lt;h3&gt;Linux&lt;/h3&gt;

&lt;p&gt;다음과 같이 설정하면 한번 입력한 암호가 저장된다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;%&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;config&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;global&lt;/span&gt; &lt;span class="comment"&gt;credential&lt;/span&gt;.&lt;span class="comment"&gt;helper&lt;/span&gt; &lt;span class="comment"&gt;cache&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;기본적으로 15분 저장해주는데 다음과 같이 기간을 수정할 수 있다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;%&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;config&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;global&lt;/span&gt; &lt;span class="comment"&gt;credential&lt;/span&gt;.&lt;span class="comment"&gt;helper&lt;/span&gt; &lt;span class="comment"&gt;'cache&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;timeout=3600'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lsquo;ssh-agent'로 하는 것과 거의 비슷하다.&lt;/p&gt;

&lt;h3&gt;Mac&lt;/h3&gt;

&lt;p&gt;Mac에서는 'osxkeychain credential helper'라는 게 있어서 ssh처럼 keychain을 사용할 수 있다.&lt;/p&gt;

&lt;p&gt;먼저 osxkeychain이 잘 동작하는지 확인하고:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% git credential-osxkeychain
Usage: git credential-osxkeychain &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;get|store|erase&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;설치돼 있지 않으면 아래와 같이 설치한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% git credential-osxkeychain
git: &lt;span class="string"&gt;'credential-osxkeychain'&lt;/span&gt; &lt;span class="keyword"&gt;is&lt;/span&gt; &lt;span class="keyword"&gt;not&lt;/span&gt; a git command. See &lt;span class="string"&gt;'git --help'&lt;/span&gt;.

% curl -s -O http:&lt;span class="regexp"&gt;//gi&lt;/span&gt;thub-media-downloads.s3.amazonaws.com&lt;span class="regexp"&gt;/osx/gi&lt;/span&gt;t-credential-osxkeychain
% chmod u+x git-credential-osxkeychain
% sudo mv git-credential-osxkeychain `&lt;span class="javascript"&gt;dirname \&lt;/span&gt;`which git\`&lt;span class="javascript"&gt;&lt;/span&gt;`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;'credential helper'로 osxkeychain을 사용할 것이라고 알린다.:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;%&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;config&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;global&lt;/span&gt; &lt;span class="comment"&gt;credential&lt;/span&gt;.&lt;span class="comment"&gt;helper&lt;/span&gt; &lt;span class="comment"&gt;osxkeychain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 ssh를 사용할 때는 ssh 인증서가 사용되고 http(s)를 사용할 때는 'osxkeychain credential helper'가 사용된다.&lt;/p&gt;

&lt;p&gt;이렇게 설정하면 CLI뿐만 아니라 &lt;a href="http://www.sourcetreeapp.com/"&gt;SourceTree&lt;/a&gt;같은 GUI에서도 매번 암호를 입력하지 않을 수 있다.&lt;/p&gt;

&lt;h3&gt;Windows&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://blob.andrewnurse.net/gitcredentialwinstore/git-credential-winstore.exe"&gt;git-credential-winstore&lt;/a&gt;를 내려받아서 실행경로에 넣고 한번 실행해준다. 그러면 msysgit 에서 잘 사용할 수 있다. 'GitHub for Windows'에는 이미 포함돼 있어서 별도로 설치할 필요가 없다.&lt;/p&gt;

&lt;p&gt;Windows XP에서는 SourceTree를 설치할 수 없어서 확인하지 못했다.&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/github/dogfeet/~4/OH83ozFMrws" height="1" width="1"/&gt;</content><feedburner:origLink>http://dogfeet.github.io/articles/2013/git-password-caching.html</feedburner:origLink></entry><entry><title><![CDATA[ mac: java-web-start ]]></title><link href="http://feedproxy.google.com/~r/github/dogfeet/~3/KugPMyVmvE8/mac-java-web-start.html" /><updated>2013-03-07T15:00:00.000Z</updated><id>http://dogfeet.github.io/articles/2013/mac-java-web-start.html</id><content type="html">
&lt;p&gt;언제부터 안되더라? 왜 안되더라? 그런건 잘 모르겠고 Mac에서는 Java Web Start가 그냥 실행되지 않는다. Apple이 막은 건데, Oracle이 미운 이유는 뭘까! 아무튼 다시 켜는 방법이 있다.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dogfeet.github.io/articles/2013/mac-java-web-start/mung-me.jpg" alt=""/&gt;&lt;/p&gt;

&lt;h2&gt;Java web start&lt;/h2&gt;

&lt;p&gt;윈도에서 처럼 매끄럽게 브라우저 안에서 Java App이 실행되지 않더라도 jnlp 파일을 다운로드 받아서 javaws 명령으로 실행시키면 실행해야 하는데 다음과 같은 에러를 내뱉는다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% javaws my.jnlp
Java Web Start splash screen process exiting &lt;span class="keyword"&gt;...&lt;/span&gt;
Can not find message file: No such file or directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Google님께 물어보면 Java7을 설치하라는 얘기가 많다. 그래서 설치하고 이것 저것 해봤는데도 됐다가 안됐다가 했다. 자세한 히스토리는 알고 싶지 않았고 그냥 실행만 됐으면 했는데, 그래도 그냥 Java7을 설치했었다. Oracle Java7을 설치하라고 하니 왠지 내 Mac을 욕보이는 것 같았지만 그냥 설치했다.&lt;/p&gt;

&lt;p&gt;어짜피 안돼서 Java7을 삭제했는데 Mac이 먹통ㅜㅜ. Mac까지 다시 설치해야 하나 싶었는데 이유는 모르겠지만 복구 됐다(야호!). 욕하고 싶은 상대가 Apple이 아닌 Oracle인건 왜일까!&lt;/p&gt;

&lt;p&gt;아래와 같이 파일을 열고:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% sudo vi /System/Library/CoreServices/CoreTypes&lt;span class="variable"&gt;.bundle&lt;/span&gt;/Contents/Resources/XProtect&lt;span class="variable"&gt;.meta&lt;/span&gt;&lt;span class="variable"&gt;.plist&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;주석처리를 좀 하고:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-xml"&gt;&lt;span class="pi"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="doctype"&gt;&amp;lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&amp;gt;&lt;/span&gt;
&lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;plist&lt;/span&gt; &lt;span class="attribute"&gt;version&lt;/span&gt;=&lt;span class="value"&gt;"1.0"&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;dict&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="comment"&gt;&amp;lt;!--
  &amp;lt;key&amp;gt;JavaWebComponentVersionMinimum&amp;lt;/key&amp;gt;
  &amp;lt;string&amp;gt;1.6.0_41-b02-446&amp;lt;/string&amp;gt;
--&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;LastModification&lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;string&lt;/span&gt;&amp;gt;&lt;/span&gt;Mon, 04 Mar 2013 21:47:02 GMT&lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;string&lt;/span&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;PlugInBlacklist&lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;dict&lt;/span&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;10&lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;dict&lt;/span&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;com.macromedia.Flash Player.plugin&lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;dict&lt;/span&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;MinimumPlugInBundleVersion&lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;string&lt;/span&gt;&amp;gt;&lt;/span&gt;11.6.602.171&lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;string&lt;/span&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;dict&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="comment"&gt;&amp;lt;!--
      &amp;lt;key&amp;gt;com.oracle.java.JavaAppletPlugin&amp;lt;/key&amp;gt;
      &amp;lt;dict&amp;gt;
        &amp;lt;key&amp;gt;MinimumPlugInBundleVersion&amp;lt;/key&amp;gt;
        &amp;lt;string&amp;gt;1.7.15.04&amp;lt;/string&amp;gt;
      &amp;lt;/dict&amp;gt;
--&amp;gt;&lt;/span&gt;
    &lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;dict&lt;/span&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;dict&lt;/span&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;Version&lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;key&lt;/span&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;integer&lt;/span&gt;&amp;gt;&lt;/span&gt;2033&lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;integer&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;dict&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class="tag"&gt;&amp;lt;/&lt;span class="title"&gt;plist&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;리부팅한다. 그리고 아래와 같이 실행한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% javaws &lt;span class="filename"&gt;my.jnlp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apple이 Flash와 Java Applet을 BlackList로까지 분류하고 있는 줄은 몰랐다.&lt;/p&gt;

&lt;p&gt;그리고 Mac update를 하면 다시 Disable된다. Disable되면 jre가 설치돼 있는데도 불구하고 javaws를 실행했을 때 아래와 같은 에러를 뱉는다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% javaws &lt;span class="keyword"&gt;my&lt;/span&gt;.jnlp
No Java runtime present, requesting install.
Unable &lt;span class="keyword"&gt;to&lt;/span&gt; locate a Java Runtime &lt;span class="keyword"&gt;to&lt;/span&gt; invoke.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;아래와 같이 실행하면 다시 javaws를 사용할 수 있다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;su&lt;span class="operator"&gt;&lt;span class="keyword"&gt;do&lt;/span&gt; ln -sf /System/Library/Frameworks/JavaVM.framework/Commands/javaws /usr/bin/javaws
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;수동으로 다시 disable하고 싶으면 아래와 같이 실행한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% su&lt;span class="operator"&gt;&lt;span class="keyword"&gt;do&lt;/span&gt; ln -sf /System/Library/Frameworks/JavaVM.framework/Versions/&lt;span class="keyword"&gt;Current&lt;/span&gt;/Commands/javaws /usr/bin/javaws
&lt;/code&gt;&lt;/pre&gt;
&lt;img src="http://feeds.feedburner.com/~r/github/dogfeet/~4/KugPMyVmvE8" height="1" width="1"/&gt;</content><feedburner:origLink>http://dogfeet.github.io/articles/2013/mac-java-web-start.html</feedburner:origLink></entry><entry><title><![CDATA[ grunt: node-coffee 템플릿 ]]></title><link href="http://feedproxy.google.com/~r/github/dogfeet/~3/1xkYPEvcs-A/grunt-init-node-coffee.html" /><updated>2013-02-20T15:00:00.000Z</updated><id>http://dogfeet.github.io/articles/2013/grunt-init-node-coffee.html</id><content type="html">
&lt;p&gt;&lt;a href="https://twitter.com/Outsideris"&gt;@Outsideris&lt;/a&gt;님의 &lt;a href="http://blog.outsider.ne.kr/892"&gt;자바스크립트 빌드 도구 Grunt&lt;/a&gt;와 &lt;a href="http://blog.outsider.ne.kr/894"&gt;Grunt에 사용자 템플릿 추가하기&lt;/a&gt;를 읽고 &lt;a href="https://github.com/pismute/grunt-init-node-coffee"&gt;node-coffee&lt;/a&gt; 템플릿을 만들었다. &lt;a href="https://twitter.com/docpad"&gt;@docpad&lt;/a&gt; 덕에 coffeescript에 익숙해졌고 항상 Compile해서 실행하기만 하면 디버그도 할 수 있기 때문에 node를 사용할 때는 coffee를 사용하려고 하는 편이다. 그동안 make를 사용해서 좀 불편했었는데, grunt를 적용하니 정말 편하다. 좀 더 편하려고 Coffeescript에 Mocha를 기본으로 하는 템플릿을 하나 만들었다.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dogfeet.github.io/articles/2013/grunt-init-node-coffee/gruntjs.png" alt=""/&gt;&lt;/p&gt;

&lt;h2&gt;grunt 0.4&lt;/h2&gt;

&lt;p&gt;갑자기 0.4 버전이 배포되는 바람에 &lt;a href="https://twitter.com/Outsideris"&gt;@Outsideris&lt;/a&gt;님의 글이 내용이 틀리게 됐다. 하지만 정리가 잘돼 있어서 grunt를 이해하기에는 여전히 좋은 글이다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/Outsideris"&gt;@Outsideris&lt;/a&gt;님의 글을 읽고 0.3 버전용 &lt;a href="https://github.com/pismute/grunt-init-node-coffee"&gt;node-coffee&lt;/a&gt;를 만들었다가 나중에 글을 써야지 하고 있었는데, 0.4가 나와 버렸다. &lt;a href="http://gruntjs.com/upgrading-from-0.3-to-0.4"&gt;upgrading-from-0.3-to-0.4&lt;/a&gt;을 잘 읽고 적용하는 게 좋다. 나는 길어서 대충 읽었다가 삽질을 좀 했다. 다 읽기 귀찮으면 새 템플릿으로 만든 코드를 좀 읽어보고 시작하는 것이 시간을 절약해줄 것 같다.&lt;/p&gt;

&lt;h2&gt;node-coffee 템플릿&lt;/h2&gt;

&lt;p&gt;이 템플릿은 특징을 요약하면 아래와 같다:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coffeescript

&lt;ul&gt;
&lt;li&gt;Gruntfile.coffee&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/src/lib/**/*.coffee&lt;/code&gt;를 &lt;code&gt;/out/lib/**/*.js&lt;/code&gt;로 컴파일&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/src/test/**/*.coffee&lt;/code&gt;를 &lt;code&gt;/out/test/**/*.js&lt;/code&gt;로 컴파일&lt;/li&gt;
&lt;li&gt;coffeelint&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Javascript

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/src/lib/**/*.js&lt;/code&gt;를 &lt;code&gt;/out/lib/**/*.js&lt;/code&gt;로 복사&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/src/test/**/*.js&lt;/code&gt;를 &lt;code&gt;/out/test/**/*.js&lt;/code&gt;로 복사&lt;/li&gt;
&lt;li&gt;jshint 그대로 포함&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;Mocha + Should로 변경&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;그 외는 &lt;a href="https://github.com/gruntjs/grunt-init-node"&gt;node&lt;/a&gt; 템플릿을 수정한 것이기 때문에 node 템플릿과 같다.&lt;/p&gt;

&lt;h3&gt;사용법&lt;/h3&gt;

&lt;p&gt;다음과 같이 설치한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone git@github&lt;span class="variable"&gt;.com&lt;/span&gt;:pismute/grunt-init-node-coffee&lt;span class="variable"&gt;.git&lt;/span&gt; ~/&lt;span class="variable"&gt;.grunt&lt;/span&gt;-init/node-coffee
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://github.com/gruntjs/grunt-init"&gt;grunt-init&lt;/a&gt;이 설치된 상태에서 다음과 같이 프로젝트를 만든다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="title"&gt;mkdir&lt;/span&gt; my-project
&lt;span class="title"&gt;cd&lt;/span&gt; my-project
&lt;span class="title"&gt;grunt&lt;/span&gt;-init node-coffee
&lt;/code&gt;&lt;/pre&gt;
&lt;img src="http://feeds.feedburner.com/~r/github/dogfeet/~4/1xkYPEvcs-A" height="1" width="1"/&gt;</content><feedburner:origLink>http://dogfeet.github.io/articles/2013/grunt-init-node-coffee.html</feedburner:origLink></entry><entry><title><![CDATA[ 그와 그녀의 tryios ]]></title><link href="http://feedproxy.google.com/~r/github/dogfeet/~3/6gWSlCO6qO0/he-she-codeschool-tryios.html" /><updated>2013-01-19T15:00:00.000Z</updated><id>http://dogfeet.github.io/articles/2013/he-she-codeschool-tryios.html</id><content type="html">
&lt;p&gt;&lt;a href="http://www.codeschool.com/"&gt;codeschool&lt;/a&gt;에 &lt;a href="http://www.codeschool.com/courses/try-ios"&gt;tryios&lt;/a&gt;가 올라오자마자 노리고 있었다. 왠지 재미도 있고 쉬울 것 같았다. 이 글은 iOS 초보인 &lt;a href="https://twitter.com/pismute"&gt;그&lt;/a&gt;와 코딩 초보인 &lt;a href="http://uniquenoun.tumblr.com/"&gt;그녀&lt;/a&gt;가 함께 &lt;a href="http://www.codeschool.com/courses/try-ios"&gt;tryios&lt;/a&gt;를 해보고 난 후기다.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dogfeet.github.io/articles/2013/codeschool-tryios/tryios.png" alt="tryios"/&gt;&lt;/p&gt;

&lt;h2&gt;tryios&lt;/h2&gt;

&lt;p&gt;이 강의는 제목에서처럼 정말 iOS를 소개하는 수준의 강의다. &lt;a href="https://twitter.com/greggpollack"&gt;@greggpollack&lt;/a&gt; 선생님이 천천히 쉬운 영어로 설명해주는 것 같지만 뭐라고 말하는지는 들을 수 없다. 내용이 쉬워서 슬라이드로도 충분하긴 하지만 그래도 영어를 알아듣고 싶다.&lt;/p&gt;

&lt;p&gt;무료인 &lt;a href="http://www.codecademy.com/"&gt;codecademy&lt;/a&gt;보다 퀄리티가 높을 거라고는 생각했지만 이렇게 친절할 줄은 생각도 못했다. 처음 접하는 사람이 쉽게 따라 할 수 있도록 기초적인 커리큘럼에 필요한 내용만 가르쳐준다. 거기다가 동영상도 공을 들인 티가 난다. 슬라이드도 아주 예쁘다.&lt;/p&gt;

&lt;p&gt;이 강의를 만드는데 무려 12명이나 참여했다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2013/codeschool-tryios/credit.png" alt="tryios-credit"/&gt;&lt;/p&gt;

&lt;p&gt;&amp;lsquo;Mr. Higgie'라는 캐릭터 까지 있다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2013/codeschool-tryios/higgie.png" alt="higgie"/&gt;&lt;/p&gt;

&lt;p&gt;설명을 듣고, 코딩해서, 바로 검사하는 구조는 너무 매력적인 학습방법이다. &lt;a href="https://class.coursera.org/progfun-2012-001/"&gt;Coursera의 Scala 강의&lt;/a&gt;도 그런 방식이였는데 굉장히 잼있었다. 무엇보다 바로 내가 틀렸는지 알 수 있기 때문에 될 때까지 버티면 정말 된다.&lt;/p&gt;

&lt;p&gt;Gamification이 잘 돼있어서 강의를 하나씩 끝날 때 마다 뱃지를 준다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2013/codeschool-tryios/badge.png" alt="badge"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://beta.openbadges.org/"&gt;http://beta.openbadges.org/&lt;/a&gt; 에 연계도 된다. &lt;a href="http://openbadges.org/"&gt;Open Badges&lt;/a&gt;라는 거 처음 봤는데, 뭔지 모르겠다.&lt;/p&gt;

&lt;p&gt;Coursera Scala 강의에서는 오덕스키 선생님의 사인이 든 수료증도 줬는데 &lt;a href="https://twitter.com/greggpollack"&gt;@greggpollack&lt;/a&gt; 선생님의 사인이든 수료증을 별도로 주지 않는다. 하지만 'Report Card'라는 게 있다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2013/codeschool-tryios/report-card.png" alt="report-card"/&gt;&lt;/p&gt;

&lt;p&gt;이 'Report Card'는 공개할 수도 있고 비공개로 설정할 수도 있다. 하지만 타인의 Report Card를 보는 페이지는 찾지 못했다.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.codeschool.com/"&gt;CodeSchool&lt;/a&gt;은 한달에 $25 단위로 결제해야 한다. $25 결제하면 원하는 대로 강의를 들을 수 있다. 무료 강의도 몇개 있어서 &lt;a href="http://www.codeschool.com/courses/try-git"&gt;trygit&lt;/a&gt;같은 것을 무료로 해볼 수 있다.&lt;/p&gt;

&lt;p&gt;뭔가 처음이라면 codeschool의 강의를 들어보는 게 좋겠다. 유료지만 한달에 $25이 아깝지 않다.&lt;/p&gt;

&lt;p&gt;Kickstarter에서 $152,912를 펀딩받아 제작됐다고 한다(&lt;a href="http://www.codeschool.com/2012"&gt;http://www.codeschool.com/2012&lt;/a&gt;). &lt;a href="http://macminicolo.net/"&gt;http://macminicolo.net/&lt;/a&gt; 사람들이 만든 것 같은데(추측임), 강의 처음에 나오는 광고도 귀엽다. 앞으로도 재미난 iOS 강의가 많을 것 같아 기대된다.&lt;/p&gt;

&lt;h3&gt;code TV&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://www.codeschool.com/"&gt;CodeSchool&lt;/a&gt;은 &lt;a href="http://www.codeschool.com/code_tv"&gt;Code TV&lt;/a&gt;라는 podcast도 제공한다. podcast 링크는 iTunes에서 찾을 수 있다. 좀 잼있어 보이는 강의가 몇개 있다. 영어를 못 들으니 영어 자막을 함께 제공해 줬으면 좋겠는데 영어 자막을 제공하지는 않는다.&lt;/p&gt;

&lt;h3&gt;수료&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://www.codeschool.com/courses/try-ios"&gt;tryios&lt;/a&gt;를 수료하면 3가지 보상을 준다. 일단 $5에 이르는 &lt;a href="http://www.codeschool.com/"&gt;CodeSchool&lt;/a&gt; Cash와 'iOS SDK Development'라는 책의 25% 할인권 그리고 &lt;a href="https://www.tinkerlearn.com/xray"&gt;TinkerLearn&lt;/a&gt;의 50% 할인권을 준다. 지원해주는 회사가 있으면 재밌게 공부도하고 수익 남는ㅋㅋㅋ:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2013/codeschool-tryios/reward.png" alt="reward"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.tinkerlearn.com/xray"&gt;TinkerLearn&lt;/a&gt;은 무료 Lesson만 다운받았는데 해당 Lesson과 관련된 pdf와 샘플 코드가 들어 있다. &lt;a href="http://www.codeschool.com/courses/try-ios"&gt;tryios&lt;/a&gt;를 끝낸 후 다음에 뭘해야 할지 고민할 필요도 없다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2013/codeschool-tryios/tinkerlearn.png" alt="tinkerlearn"/&gt;&lt;/p&gt;

&lt;h2&gt;그녀&lt;/h2&gt;

&lt;p&gt;그녀의 개발툴은 Illustrator와 Photoshop으로 프로그래밍은 잘 모른다. 디자이너도 코드를 읽을 줄 알아야 한다는 드립을 한 5년간 쳤는데, 드디어 일진보 했다.&lt;/p&gt;

&lt;p&gt;내가 그녀에게 친 뻥은 아래와 같다:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;인생은 학습이다. 뭘 배우는지 중요치 않다. 뭐든 배우는 거다.&lt;/li&gt;
&lt;li&gt;실무자가 의사 결정을 직접 해야 작업 속도가 빠르고 퀄리티도 높다. 소프트웨어 디자인을 한다면 소프트웨어에서 디자인을 어떻게 표현하는지 사람들이 어떻게 작업하는지 알아야 한다.&lt;/li&gt;
&lt;li&gt;결정권이 없는 실무자는 우울해진다. 동료를 위해 디자인과 관계된 실무를 익혀라.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;그녀에 대답은 대체로 아래와 같았다:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;너부터 디자인을 배워라.&lt;/li&gt;
&lt;li&gt;소프트웨어 싫다.&lt;/li&gt;
&lt;li&gt;동료가 없다.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;그렇게 줄다리기를 하다가 &lt;a href="http://www.codeschool.com/"&gt;CodeSchool&lt;/a&gt; 덕택에 함께 공부를 해보게 됐다.&lt;/p&gt;

&lt;p&gt;뭐든 상관 없지만, iOS를 배워보기로 했다. iOS, web, Android를 제안했는데 그녀는 iOS를 골랐다. iPhone의 완성도는 역시 너무 매력적이다.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.codeschool.com/courses/try-ios"&gt;tryios&lt;/a&gt;를 시작하기 전에 그녀는 Objective-C를 좀 먼저 읽었다. 분명 제대로 공부를 하고 있다면 질문이 많았을 텐데 질문이 많지 않은 거로 봐서 멘붕 상태에 있다는 걸 알 수 있었다. 그녀는 &lt;a href="http://www.codeschool.com/courses/try-ios"&gt;tryios&lt;/a&gt; 강의를 수료한 지금도 멘붕 상태다.&lt;/p&gt;

&lt;p&gt;들리지도 않는 동영상 교재를 끝까지 듣고, 처음보는 외계어로 가득찬 슬라이드를 끝까지 감내한 그녀에게 경의를 표한다.&lt;/p&gt;

&lt;p&gt;총 6 챕터로 구성된 강의는 매 챕터마다 3~4시간 가량 걸렸다. tryios는 아주 기본적인 내용만 다루기 때문에 하루 정도면 충분하다. 나는 좀 불친절한 성격이라 그녀가 묻는 질문에만 대답해서 오래걸렸다.&lt;/p&gt;

&lt;p&gt;내 느낌은 이렇다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2013/codeschool-tryios/haksan.png" alt="haksan"/&gt;&lt;/p&gt;

&lt;p&gt;내가 그녀라면 인내할 수 있었을까? 훌륭하다. 그녀는 이왕 결재한 거라고 하면서 &lt;a href="http://www.codeschool.com/courses/functional-html5-css3"&gt;Functional HTML5 &amp; CSS3&lt;/a&gt;와 &lt;a href="http://www.codeschool.com/courses/functional-html5-css3"&gt;CSS Cross-Country&lt;/a&gt; 강의도 시작했다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2013/codeschool-tryios/functional-html5-css3.png" alt="functional-html5-css3"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2013/codeschool-tryios/css-cross-country.png" alt="css-cross-country"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.codeschool.com/courses/try-ios"&gt;tryios&lt;/a&gt; 강의를 끝내고 받은 퀘스트 보상때문에 나도 이 두 강의를 시작했다. 찬찬히 &lt;a href="http://www.codeschool.com/"&gt;CodeSchool&lt;/a&gt;의 강의를 전부 해보려한다.&lt;/p&gt;

&lt;h2&gt;영어&lt;/h2&gt;

&lt;p&gt;영어라는 장벽이 없었으면 나도 그녀도 좀 더 쉽게 따라 했을 것 같다. 영어로 표현된 외계어로 가득찬 자료를 끝까지 참아줘서 고맙다. 그덕에 나도 많은 것을 배웠다.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.codeschool.com/"&gt;CodeSchool&lt;/a&gt; 강의 중에서는 영어자막을 제공하는 강의도 있다. &lt;a href="http://www.codeschool.com/courses/try-ios"&gt;tryios&lt;/a&gt;는 제공하지 않았는데 &lt;a href="http://www.codeschool.com/courses/functional-html5-css3"&gt;Functional HTML5 &amp; CSS3&lt;/a&gt;와 &lt;a href="http://www.codeschool.com/courses/functional-html5-css3"&gt;CSS Cross-Country&lt;/a&gt;는 영어자막이 있다.&lt;/p&gt;

&lt;p&gt;영어가 되는 사람들은 좋겠다. 쉽고 재밌는 학습 도구가 많아서 좋겠다. 글래머 금발이랑 코딩할 수 있어서 좋겠다. 앞으로는 영어를 좀 더 to-the-core하게 해서 그런 삶을 살아야 겠다.&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/github/dogfeet/~4/6gWSlCO6qO0" height="1" width="1"/&gt;</content><feedburner:origLink>http://dogfeet.github.io/articles/2013/he-she-codeschool-tryios.html</feedburner:origLink></entry><entry><title><![CDATA[ Git: GitHub secrets ]]></title><link href="http://feedproxy.google.com/~r/github/dogfeet/~3/nmvKQdp-UkI/git-github-secrets.html" /><updated>2012-12-29T00:00:00.000Z</updated><id>http://dogfeet.github.io/articles/2012/git-github-secrets.html</id><content type="html">
&lt;p&gt;이글은 &lt;a href="https://twitter.com/holman"&gt;@holman&lt;/a&gt;님이 싱가폴에서 열린 &lt;a href="http://reddotrubyconf.com/"&gt;RedDotRubyConf&lt;/a&gt;에서 발표한 &lt;a href="http://zachholman.com/talk/git-github-secrets"&gt;Git and GitHub Secrets&lt;/a&gt;에 설명을 달았다. 내용이 길어서 둘로 나눴는데 이 글은 &lt;code&gt;GitHub Secetets&lt;/code&gt; 부분을 정리한 글이다. &lt;a href="/articles/2012/git-secrets.html"&gt;Git Secrets&lt;/a&gt;은 다른 글에서 정리한다.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dogfeet.github.io/articles/2012/git-github-secrets/holman.png" alt="holman"/&gt;&lt;/p&gt;

&lt;p&gt;이 글을 정리하면서 그림을 많이 삽입하지 않았다. &lt;code&gt;GitHub Secrets&lt;/code&gt; 부분은 그림이 너무 많아서 생략했다. 이 글을 읽고서 슬라이드를 한번 보는게 좋을 것 같다.&lt;/p&gt;

&lt;h2&gt;숨겨진 기능&lt;/h2&gt;

&lt;p&gt;GitHub은 단순함을 추구한다. 그래서 GitHub에는 기능이 많은 데도 불구하고 굳이 화면에 보여주지 않는다.&lt;/p&gt;

&lt;h3&gt;.patch, .diff&lt;/h3&gt;

&lt;p&gt;커밋 URL 뒤에 .patch나 .diff 붙이면 해당 포멧의 파일이 나온다. 정확하게는 &amp;lsquo;Compare View, Pull Requests, Commit Pages&amp;rsquo; 화면에서 사용할 수 있다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dogfeet/dogit/commit/a1f156b6415439a8a84c3d2fa89ea975fb3a7ac2.diff"&gt;.diff 예제&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dogfeet/dogit/commit/a1f156b6415439a8a84c3d2fa89ea975fb3a7ac2.patch"&gt;.patch 예제&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;공백문자는 무시하고 diff를 보여준다.&lt;/h2&gt;

&lt;p&gt;diff URL끝에 &lt;code&gt;?w=1&lt;/code&gt;를 붙이면 공백문자를 무시한 결과를 보여준다.&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-github-secrets/ignore-whitespace.png" alt="ignore_whitespace"/&gt;&lt;/p&gt;

&lt;h3&gt;SVN 클라이언트도 지원.&lt;/h3&gt;

&lt;p&gt;&amp;lsquo;SVN/Git 서비스 레이어'가 있어서 SVN 요청을 Git 요청으로 변환해준다.&lt;/p&gt;

&lt;p&gt;당연히 Git 클라이언트를 사용할 수 있지만:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git &lt;span class="keyword"&gt;clone&lt;/span&gt; https:&lt;span class="comment"&gt;//github.com/dogfeet/dogit.git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SVN 클라이언트도 사용할 수 있다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="char"&gt;$ &lt;/span&gt;svn checkout &lt;span class="method"&gt;https:&lt;/span&gt;//github.com/dogfeet/dogit.git
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;SSH &amp; HTTP =&gt; HTTP &amp; SSH&lt;/h3&gt;

&lt;p&gt;GitHub은 기본 프로토콜을 SSH에서 HTTP로 바꿨다. 원래 HTTP 프로토콜을 사용하는 방식은 성능이 후져서 SSH를 권장했는데 이제 SmartHTTP 덕택에 HTTP도 효율적이다.&lt;/p&gt;

&lt;p&gt;원래 SSH를 먼저 보여줬었지만:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-github-secrets/ssh-http.png" alt="before"/&gt;&lt;/p&gt;

&lt;p&gt;지금은 HTTP를 먼저 보여준다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-github-secrets/http-ssh.png" alt="after"/&gt;&lt;/p&gt;

&lt;p&gt;이제 HTTP도 효율적이기 때문에 1) 회사 방화벽 뒤에서도 맘껏 GitHub을 즐길 수 있고 2) SSH Key를 사용하기 힘든 Windows 환경에서도 사용하기 쉬워졌다. SSH key없이도 암호를 메모리에 저장해서 사용할 수 있다. 이 방법은 git 1.7.10부터 사용할 수 있고 GitHub의 &lt;a href="https://help.github.com/articles/set-up-git"&gt;패스워드 캐싱하는 방법&lt;/a&gt;에 잘 설명돼 있다.&lt;/p&gt;

&lt;h4&gt;SmartHTTP&lt;/h4&gt;

&lt;p&gt;SmartHTTP는 git 1.6.6부터 지원한다. git 1.6.6은 2009년 말에 배포됐다.&lt;/p&gt;

&lt;p&gt;Git은 개체를 'Packfile'이라는 덩어리에 묶어서 관리한다. 이전 버전에서는 사용자가 'Packfile'에 들어 있는 개체 한 개가 필요해도 &lt;code&gt;Packfile&lt;/code&gt;을 통째로 전송해야 했다. 그래서 HTTP를 더미 프로토콜이라고 불렀다. 이제는 SmartHTTP가 있어서 'Packfile'에서 필요한 개체만 꺼내서 전송할 수 있다.&lt;/p&gt;

&lt;p&gt;progit 책을 집필할 때에는 SmartHTTP가 없었기 때문에 이를 설명하지 않았다. SmartHTTP에 대해서 자세히 알아보려면 progit 9장과 &lt;a href="http://git-scm.com/2010/03/04/smart-http.html"&gt;Smart HTTP Transport&lt;/a&gt;를 읽는게 좋다.&lt;/p&gt;

&lt;h3&gt;URL에서 &amp;rsquo;.git'은 생략해도 된다.&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git &lt;span class="keyword"&gt;clone&lt;/span&gt; https:&lt;span class="comment"&gt;//github.com/holman/boom.git&lt;/span&gt;
git &lt;span class="keyword"&gt;clone&lt;/span&gt; https:&lt;span class="comment"&gt;//github.com/holman/boom&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;GitHub HD&amp;trade;&lt;/h3&gt;

&lt;p&gt;GitHub 페이지의 아이콘이 HD 벡터 아이콘이라서 계속 확대해도 깨지지 않는다.&lt;/p&gt;

&lt;h3&gt;Auditing&lt;/h3&gt;

&lt;p&gt;우리말로 하자면 &amp;lsquo;감사로그&amp;rsquo; 쯤 되는 건데, GitHub에서 일어난 중요한 액션은 로그가 남는다:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/settings/security"&gt;https://github.com/settings/security&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Octocat&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://twitter.com/defunkt"&gt;@defunkt&lt;/a&gt;님이 에러페이지에 사용할 이미지를 찾다가 &lt;a href="http://www.istockphoto.com/"&gt;Istockphoto&lt;/a&gt;에서 싸게 산 이미지였는데 사람들이 좋아해서 지금은 GitHub의 마스코드가 됐다. 처음에는 아니였지만 지금은 GitHub이 저작권을 가지고 있다.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.quora.com/GitHub/What-is-the-story-behind-GitHub%E2%80%99s-octocat-mascot"&gt;Octocat 스토리&lt;/a&gt;는 Quora에서 참고.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://octodex.github.com/"&gt;http://octodex.github.com/&lt;/a&gt; 에 가면 Octocat 이미지가 많다.&lt;/p&gt;

&lt;h3&gt;git.io&lt;/h3&gt;

&lt;p&gt;GitHub용 URL Shortner이다. 작은 &lt;a href="git.io/nxVVig"&gt;쉘 스크립트&lt;/a&gt;로 구현돼 있고 다음과 같이 사용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ gitio &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;url&lt;/span&gt;&amp;gt;&lt;/span&gt; &lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;name&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="https://chrome.google.com/webstore/detail/gitio-url-shortener/baceaeopmlhkjbljoiinmbnnmpokgiml"&gt;Chrome Extension&lt;/a&gt;도 있다.&lt;/p&gt;

&lt;h3&gt;Linguist&lt;/h3&gt;

&lt;p&gt;저장소에 든 언어가 뭔지 찾아서 직접 그 저장소에서 개발된 파일만 추려서 &amp;lsquo;Syntax Highlighting'도 해준다. GitHub은 이 Linguist를 다음과 같은 걸 만들어 내는데 사용한다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-github-secrets/linguist.png" alt="linguist"/&gt;&lt;/p&gt;

&lt;p&gt;기능:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;'Language Detection&amp;rsquo; - 어떤 언어가 사용됐는지 찾는다.&lt;/li&gt;
&lt;li&gt;&amp;lsquo;Stats&amp;rsquo; - 언어 통계를 알려준다.&lt;/li&gt;
&lt;li&gt;&amp;lsquo;Syntax Highlighting&amp;rsquo; - Pygments를 사용한다.&lt;/li&gt;
&lt;li&gt;&amp;lsquo;Vendored Files&amp;rsquo; - 저장소에 들어 있는 파일 중 다른 프로젝트에서 가져온 파일. ex) jquery.js&lt;/li&gt;
&lt;li&gt;&amp;lsquo;Generated file detection&amp;rsquo; - 생성되는 파일을 알아서 제외한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;email reply&lt;/h3&gt;

&lt;p&gt;이메일로 comments에 답변을 달 수 있다.&lt;/p&gt;

&lt;h3&gt;gist&lt;/h3&gt;

&lt;p&gt;단순히 코드 snippet을 공유하는 도구가 아니다. 코멘트, 스크린샷, 코드를 공유를 할 수 있기 때문에 프로토타이핑 도구로 사용하기에도 좋다. 개발자와 디자이너 모두에게 유용하다.&lt;/p&gt;

&lt;p&gt;gist 자체가 git 저장소이기 때문에 clone할 수도 있다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="title"&gt;git&lt;/span&gt; clone &lt;span class="url"&gt;git://gist.github.com/2720312&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;간단하게 만들어서 프로토타이핑을 해보기에 아주 좋다.&lt;/p&gt;

&lt;h4&gt;microgems.&lt;/h4&gt;

&lt;p&gt;Ruby Gem으로도 사용할 수 있는 것 같다. 나는 Ruby를 몰라서 알 수 없다.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://jeffkreeftmeijer.com/2011/microgems-five-minute-rubygems/"&gt;http://jeffkreeftmeijer.com/2011/microgems-five-minute-rubygems/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Image View Mode&lt;/h3&gt;

&lt;p&gt;Gist에 이미지를 올리고 비교하면 비교해보기 좋게 나열해준다. 정말 쩐다. 데모 페이지에 가서 클릭해보자.&lt;/p&gt;

&lt;p&gt;이 기능은 &lt;a href="http://www.kaleidoscopeapp.com/"&gt;KaleidoScope&lt;/a&gt;같은 도구에서 있는 건데 GitHub도 된다. &lt;a href="http://www.sourcetreeapp.com/"&gt;SourceTree&lt;/a&gt;같은 데서도 가능하면 좋겠다.&lt;/p&gt;

&lt;p&gt;GitHub의 &lt;a href="https://github.com/blog/817-behold-image-view-modes"&gt;Behold: Image view modes&lt;/a&gt;에 잘 소개돼 있다.&lt;/p&gt;

&lt;h3&gt;Command Line GitHub - hub&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/defunkt/hub"&gt;hub&lt;/a&gt;라는 프로그램이 있다. Command Line에서 GitHub을 사용할 수 있는 명령이다. git + github 명령이라고 생각하면 된다. GitHub을 사용하면서 자동화한다면 꼭 필요한 툴이라고 생각된다. 나중에 따로 정리해야 겠다.&lt;/p&gt;

&lt;h3&gt;Keyboard Shortcuts&lt;/h3&gt;

&lt;p&gt;GitHub의 모든 페이지에서 &lt;code&gt;?&lt;/code&gt;를 누르면 그 페이지에서 사용할 수 있는 단축키를 보여준다.&lt;/p&gt;

&lt;h3&gt;Subscribing People&lt;/h3&gt;

&lt;p&gt;GitHub에서 글쓸때 &lt;code&gt;@pismute&lt;/code&gt;쓰면 해당 사용자에게 알림이 간다. &lt;code&gt;@org/team&lt;/code&gt;라는 팀 표현식도 있어서 팀 전체한테 노티를 줄수도 있다.&lt;/p&gt;

&lt;h3&gt;GitHub Flavored Markdown&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://github.github.com/github-flavored-markdown/"&gt;GFM&lt;/a&gt;에 대한 설명도 있다.&lt;/p&gt;

&lt;h3&gt;Auto-Closing Issues&lt;/h3&gt;

&lt;p&gt;커밋메시지에 &lt;code&gt;CLOSES/CLOSED/CLOSE #1&lt;/code&gt;나 &lt;code&gt;FIXES/FIXED/FIX #1&lt;/code&gt;라고 쓰면 해당 이슈가 자동으로 닫힌다.&lt;/p&gt;

&lt;h3&gt;Commit by Author&lt;/h3&gt;

&lt;p&gt;GitHub의 커밋 페이지에서 &lt;code&gt;?author=holman&lt;/code&gt; 처럼 파라미터를 넘기면 해당 사용자의 커밋만 볼 수 있다:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/progit/progit/commits/master?author=pismute"&gt;https://github.com/progit/progit/commits/master?author=pismute&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Branch-to-Branch&lt;/h3&gt;

&lt;p&gt;Pull Request는 브랜치 단위로 하는 거라는 얘기. 그래서 원 저장소가 아니라 Clone 저장소에도 Pull Request를 보낼 수 있다.&lt;/p&gt;

&lt;h3&gt;emoji!&lt;/h3&gt;

&lt;p&gt;GitHub에서 emoji 이모티콘을 사용할 수 있다. 사용할 수 있는 이모티콘은 &lt;a href="http://www.emoji-cheat-sheet.com/"&gt;http://www.emoji-cheat-sheet.com/&lt;/a&gt; 에서 참고.&lt;/p&gt;

&lt;h3&gt;Link Linking&lt;/h3&gt;

&lt;p&gt;GitHub 페이지에서 파일 보기 화면에서 URL 뒤에 &lt;code&gt;#L16&lt;/code&gt;을 붙이면 16라인이 노랗게 보인다. &lt;code&gt;#L16-32&lt;/code&gt;를 붙이면 16라인부터 32라인까지 노랗게 보인다. 다른 사람과 코드에 대해 수다떨 때 어떤 라인에 대해서 얘기하는 건지 콕 집어 줄 수 있다.&lt;/p&gt;

&lt;h3&gt;Advanced Compare View&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;github.com/user/repo/compare/{range}&lt;/code&gt;과 같은 형식의 URL을 사용하면 되고 &lt;code&gt;{range}&lt;/code&gt;부분에 다음과 같이 넣을 수 있다:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;master@{1.day.ago}...master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;master@{yesterday}...master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;master@{2012-02-25}...master&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;아래와 같이 사용한다:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dogfeet/dogit/compare/master@%7B60.day.ago%7D...master"&gt;https://github.com/dogfeet/dogit/compare/master@{60.day.ago}&amp;hellip;master&lt;/a&gt;&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/github/dogfeet/~4/nmvKQdp-UkI" height="1" width="1"/&gt;</content><feedburner:origLink>http://dogfeet.github.io/articles/2012/git-github-secrets.html</feedburner:origLink></entry><entry><title><![CDATA[ Git: git secrets ]]></title><link href="http://feedproxy.google.com/~r/github/dogfeet/~3/jdY6Ojpxb7c/git-secrets.html" /><updated>2012-12-19T00:00:00.000Z</updated><id>http://dogfeet.github.io/articles/2012/git-secrets.html</id><content type="html">
&lt;p&gt;이글은 &lt;a href="https://twitter.com/holman"&gt;@holman&lt;/a&gt;님이 싱가폴에서 열린 &lt;a href="http://reddotrubyconf.com/"&gt;RedDotRubyConf&lt;/a&gt;에서 발표한 &lt;a href="http://zachholman.com/talk/git-github-secrets"&gt;Git and GitHub Secrets&lt;/a&gt;에 설명을 달았다. 내용이 길어서 둘로 나눴는데 이 글은 &lt;code&gt;Git Secetets&lt;/code&gt;부분을 정리한 글이다. &lt;a href="/articles/2012/git-github-secrets.html"&gt;GitHub Secrets&lt;/a&gt;은 다른 글에 정리한다.&lt;/p&gt;

&lt;p&gt;Git은 명령과 옵션이 굉장히 많은데, 그 중에서 &lt;a href="https://twitter.com/holman"&gt;@holman&lt;/a&gt;님이 추천하는 쓸만한 것이라고 생각하면 되겠다.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dogfeet.github.io/articles/2012/git-secrets/holman.png" alt="holman"/&gt;&lt;/p&gt;

&lt;h2&gt;Git secret&lt;/h2&gt;

&lt;h3&gt;&amp;ndash;allow-empty&lt;/h3&gt;

&lt;p&gt;파일없이 커밋할 수 있다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;$&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;commit&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;m&lt;/span&gt; &lt;span class="comment"&gt;"LOOK&lt;/span&gt; &lt;span class="comment"&gt;AT&lt;/span&gt; &lt;span class="comment"&gt;ME&lt;/span&gt; &lt;span class="comment"&gt;TROLOLOL"&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;allow&lt;/span&gt;-&lt;span class="comment"&gt;empty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;git log&lt;/code&gt;를 하면 아무내용없이 그냥 커밋 개체만 달랑 생긴다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git log
&lt;span class="operator"&gt;&lt;span class="keyword"&gt;commit&lt;/span&gt; &lt;span class="number"&gt;6&lt;/span&gt;eb28f645174fba20d819f40da4ca822c7c67b2a
Author: Changwoo Park &amp;lt;pismute@gmail.com&amp;gt;
&lt;span class="keyword"&gt;Date&lt;/span&gt;:   Tue Nov &lt;span class="number"&gt;12&lt;/span&gt; &lt;span class="number"&gt;20&lt;/span&gt;:&lt;span class="number"&gt;32&lt;/span&gt;:&lt;span class="number"&gt;25&lt;/span&gt; &lt;span class="number"&gt;2012&lt;/span&gt; +&lt;span class="number"&gt;0900&lt;/span&gt;

    LOOK &lt;span class="keyword"&gt;AT&lt;/span&gt; ME TROLOLOL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;히스토리에 뭔가 표식을 남기고 싶을 때 유용하다.&lt;/p&gt;

&lt;h3&gt;Staging Hunk&lt;/h3&gt;

&lt;p&gt;파일에서 한 부분? 덩어리?를 Hunk라고 부른다. &lt;code&gt;git add -p&lt;/code&gt; 명령으로 파일을 통째로 Staging Area에 넣는게 아니라 일정 부분만(hunk만) 골라서 넣는다.&lt;/p&gt;

&lt;p&gt;자세한 설명은 &lt;a href="https://twitter.com/semtlnori"&gt;@semtlnori&lt;/a&gt;님의 &lt;a href="http://npcode.com/blog/archives/449"&gt;깔끔하게 커밋하기&lt;/a&gt;를 보자.&lt;/p&gt;

&lt;h3&gt;git show :/query&lt;/h3&gt;

&lt;p&gt;특정 질의가 들어간 커밋 중에서 가장 최근 커밋 하나를 찾아 준다. 로그를 분석할때 매우 유용하다. 커밋 메시지, 파일 이름, 파일 내용에서 찾는다.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git log&lt;/code&gt; 명령에도 있어서 &lt;code&gt;git log :/query&lt;/code&gt;라고 실행해도 된다. &lt;code&gt;:/query&lt;/code&gt; 만족하는 커밋을 골라서 보여준다.&lt;/p&gt;

&lt;h3&gt;go back&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;cd -&lt;/code&gt;라고 실행하면 이전 디렉토리로 되돌아 간다. &lt;code&gt;cd&lt;/code&gt;명령 처럼 &lt;code&gt;git checkout -&lt;/code&gt;라고 하면 이전 브랜치를 checkout한다.&lt;/p&gt;

&lt;h3&gt;merged branch&lt;/h3&gt;

&lt;p&gt;브랜치나 커밋이 다른 브랜치에 Merge됐는지 확인하는 명령들.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git branch --merged&lt;/code&gt; : 이미 다른 브랜치에 머지된 것만 보여준다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git branch --no-merged&lt;/code&gt; : 아직 다른 브랜치에 머지되지 않는 것만 보여준다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git branch --contains 838ad46&lt;/code&gt; : 특정 커밋이 포함된 브랜치만 보여준다.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Content Copy&lt;/h3&gt;

&lt;p&gt;브랜치를 변경하지 않고도 다른 브랜치에 들어 있는 파일을 복사해 올 수 있다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;$&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;checkout&lt;/span&gt; &lt;span class="comment"&gt;BRANCH&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt; &lt;span class="comment"&gt;path/to/file&lt;/span&gt;.&lt;span class="comment"&gt;rb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;path/to/file.rb&lt;/code&gt;에 파일이 복사된다.&lt;/p&gt;

&lt;p&gt;Reset과 Checkout은 비슷해보여서 구분하기 쉽지 않다. Checkout은 데이터베이스에서 뭔가를 꺼낼때 사용하는 명령이다. 옵션도 스냅샷과 파일이름 등 그와 관련된 옵션으로 구성돼있다. 반대로 Reset은 워킹 디렉토리, Staging Area, 브랜치 등을 스냅샷으로 Reset하는 명령이다.&lt;/p&gt;

&lt;p&gt;이렇게 구분하면 쉽다. 데이터베이스에서 뭔가를 꺼낼때는 Checkout을 사용하고 그외는 Reset을 사용한다.&lt;/p&gt;

&lt;h3&gt;Reachable Commits&lt;/h3&gt;

&lt;p&gt;특정 브랜치에만 있는 커밋이 보고 싶을 때는 다음과 같이 한다:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git log branchA ^branchB&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;branchA에는 있고 branchB에는 없는 커밋을 보여준다.&lt;/p&gt;

&lt;h3&gt;FINDING LOST COMMITS&lt;/h3&gt;

&lt;p&gt;어떤 브랜치에도 들어 있지 않은 커밋을 보여준다. git의 커밋은 개체는 실제로 전부 immutable이라서 커밋을 수정하면 새로운 커밋 개체가 등록된다. 잘 못 수정했으면 아래 명령으로 커밋을 찾아서 복구한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git fsck &lt;span class="comment"&gt;--lost-found&lt;/span&gt;
Checking object directories: 100% (256/256), done.
dangling &lt;span class="operator"&gt;&lt;span class="keyword"&gt;commit&lt;/span&gt; &lt;span class="number"&gt;4&lt;/span&gt;a7f2e89a480d3af0ccfdf71f76f4149f25fb0fb
dangling &lt;span class="keyword"&gt;commit&lt;/span&gt; d3ad9f17532109d12084646c306e9d7748c2f791
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;어떤 브랜치에도 속하지 않은 커밋이 두 개있다.&lt;/p&gt;

&lt;h2&gt;DIFFSTATS&lt;/h2&gt;

&lt;p&gt;델타(diff)를 다 보여주는 게 기본인데 통계만 볼 수도 있다:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git diff HEAD^ --stat&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-diff--stat.png" alt="git-diff--stat"/&gt;&lt;/p&gt;

&lt;h3&gt;BLAME&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git blame&lt;/code&gt;은 기본적으로 어떤 라인을 누가 고쳤는지 확인하는 명령이다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;blame이니까&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git blame&lt;/code&gt;은 기본적으로 어떤 라인을 &lt;strong&gt;어떤 새끼&lt;/strong&gt;가 고쳤는지 확인하는 명령이다.&lt;/p&gt;

&lt;h4&gt;git blame -w&lt;/h4&gt;

&lt;p&gt;정말 내용을 수정한 &lt;strong&gt;새끼&lt;/strong&gt;를 찾는다. 공백만 추가한 경우는 무시한다. &lt;code&gt;git diff HEAD~&lt;/code&gt; 명령으로 공백이 어디에 추가됐는지 보자:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-blame-w-diff.png" alt="git-blame-w-diff"/&gt;&lt;/p&gt;

&lt;p&gt;히스토리를 보면 마지막에 &amp;ldquo;BBB&amp;quot;가 공백을 추가했다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-blame-w-lg.png" alt="git-blame-w-lg"/&gt;&lt;/p&gt;

&lt;p&gt;다음은 &lt;code&gt;git blame -w&lt;/code&gt;의 결과다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-blame-w.png" alt="git-blame-w"/&gt;&lt;/p&gt;

&lt;p&gt;단순히 공백만 추가한 &amp;quot;BBB&amp;quot;는 무시된다. &lt;code&gt;-w&lt;/code&gt;을 옵션을 빼고 &lt;code&gt;git blame&lt;/code&gt;만 실행하면 공백만 추가한 &amp;quot;BBB&amp;quot;도 나온다.&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-blame.png" alt="git-blame"/&gt;&lt;/p&gt;

&lt;h4&gt;git blame -M&lt;/h4&gt;

&lt;p&gt;해당 라인을 실질적으로 마지막에 수정한 사람을 보여준다. 이 옵션을 주면 같은 파일 내에서 단순히 라인을 옮긴 사람이 아니라 마지막으로 내용을 수정한 사람이 표시된다:&lt;/p&gt;

&lt;p&gt;텍스트를 옮긴 후에 &lt;code&gt;git blame&lt;/code&gt; 명령을 실행하면 다음과 같이 나온다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-blame-m-before.png" alt="git-blame"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-M&lt;/code&gt; 옵션을 추가하면 단순히 옮긴 사람이 아니라 원래 그 코드를 추가한 사람을 보여준다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-blame-m.png" alt="git-blame"/&gt;&lt;/p&gt;

&lt;p&gt;원래 의도는 측근 &lt;code&gt;all.md&lt;/code&gt; 파일에서 친구 끼리, 가족 끼리 모아서 &lt;code&gt;-M&lt;/code&gt; 옵션을 설명할 계획이였다. Git이 정확히 어떤 알고리즘을 사용하는 건지 나중에 살펴봐야 겠다.&lt;/p&gt;

&lt;h4&gt;git blame -C&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;-M&lt;/code&gt;와 비슷하게 실제로 마지막에 수정한 사람을 보여준다. 한 파일 내에서의 이동만 감지하는 것이 아니라. 같은 커밋에서의 다른 파일간 이동도 감지한다.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;all.md&lt;/code&gt;라는 파일에서 친구는 &lt;code&gt;friends.md&lt;/code&gt;라는 파일로 옮기고 가족은 &lt;code&gt;family.md&lt;/code&gt; 파일로 옮겼다. 아래는 &lt;code&gt;git blame -f family.md&lt;/code&gt;의 결과다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-blame-c-f.png" alt="git-blame"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-f&lt;/code&gt;는 원래 파일이름을 보여주는 옵션이다. &lt;code&gt;git blame -fC family.md&lt;/code&gt;의 결과는 아래와 같다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-blame-c-cf.png" alt="git-blame"/&gt;&lt;/p&gt;

&lt;h4&gt;git blame -CC&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;-C&lt;/code&gt; 처럼 다른 파일에서 옮긴 것을 감지해주는데 해당 파일을 생성한 커밋내에서도 감지한다.&lt;/p&gt;

&lt;h4&gt;git blame -CCC&lt;/h4&gt;

&lt;p&gt;다른 파일에서 옮긴 것도 감지하는데 커밋을 가리지 않고 전체에서 찾는다.&lt;/p&gt;

&lt;h4&gt;MULTI-REMOTE FETCHES&lt;/h4&gt;

&lt;p&gt;원래는 하나씩 fetch해야 하지만 group을 만들어서 한번에 fetch할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="variable"&gt;$ &lt;/span&gt;git config remotes.mygroup &lt;span class="string"&gt;'remote1 remote2'&lt;/span&gt;
&lt;span class="variable"&gt;$ &lt;/span&gt;git fetch mygroup
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;A BETTER STATUS&lt;/h4&gt;

&lt;p&gt;status의 결과를 더 간략하게 볼 수 있다.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git status&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-status.png" alt="git-status"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git status -sb&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-status-sb.png" alt="git-status-sb"/&gt;&lt;/p&gt;

&lt;h4&gt;WORD DIFFING&lt;/h4&gt;

&lt;p&gt;라인 단위로 비교하는 것이 아니라 단어 단위로 비교해서 볼 수 있다.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git diff HEAD^&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-diff-head-1.png" alt="diff-head-1"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git diff HEAD^ --word-diff&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-diff-head-1--word-diff.png" alt="diff-head-1--word-diff"/&gt;&lt;/p&gt;

&lt;h4&gt;CONFIG: SPELLING&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;git comit&lt;/code&gt;이라고 실행하면 &lt;code&gt;commit&lt;/code&gt;이라고 할려고 했냐? 라고 물어봐 주는 게 기본설정이다. 명령을 실행할 때 오타를 내면 자동으로 인식해서 실행하게 할 수 있다. 다음과 같이 설정하면 된다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;$&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;config&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;global&lt;/span&gt; &lt;span class="comment"&gt;help&lt;/span&gt;.&lt;span class="comment"&gt;autocorrect&lt;/span&gt; &lt;span class="comment"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 옵션이 설정되면 &lt;code&gt;git comit&lt;/code&gt;이라고 실행하면 그냥 &lt;code&gt;git commit&lt;/code&gt;이 실행된다:&lt;/p&gt;

&lt;h4&gt;CONFIG: GIT RERERE(REUSE RECORDED RESOLUTION)&lt;/h4&gt;

&lt;p&gt;&lt;a href="/articles/2012/git-rerere.html"&gt;Git: rerere&lt;/a&gt;에서 확인한다.&lt;/p&gt;

&lt;h4&gt;CONFIG: COLOR!&lt;/h4&gt;

&lt;p&gt;다음과 같이 설정하면 결과가 칼라로 나온다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;$&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;config&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;global&lt;/span&gt; &lt;span class="comment"&gt;color&lt;/span&gt;.&lt;span class="comment"&gt;ui&lt;/span&gt; &lt;span class="comment"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ALIAS: GIT-AMEND&lt;/h4&gt;

&lt;p&gt;아래와 같이 alias를 등록하면 &lt;code&gt;git amend&lt;/code&gt;라고 실행해서 HEAD 커밋을 수정할 수 있다. &lt;code&gt;-C&lt;/code&gt; 옵션이 있기 때문에 커밋 메시지는 수정하지 않는다. 항상 커밋 메시지를 확인하고 싶으면 &lt;code&gt;-C HEAD&lt;/code&gt;를 빼면 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;$&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;config&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;global&lt;/span&gt; &lt;span class="comment"&gt;alias&lt;/span&gt;.&lt;span class="comment"&gt;amend&lt;/span&gt; &lt;span class="comment"&gt;"commit&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;amend&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;C&lt;/span&gt; &lt;span class="comment"&gt;HEAD"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ALIAS: GIT-UNDO&lt;/h4&gt;

&lt;p&gt;가장 최근 커밋을 되돌린다. &lt;code&gt;--soft&lt;/code&gt;이기 때문에 그 커밋의 개체는 Staged 상태로 남는다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;$&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;config&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;global&lt;/span&gt; &lt;span class="comment"&gt;alias&lt;/span&gt;.&lt;span class="comment"&gt;undo&lt;/span&gt; &lt;span class="comment"&gt;"reset&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;soft&lt;/span&gt; &lt;span class="comment"&gt;HEAD^"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ALIAS: GIT-COUNT&lt;/h4&gt;

&lt;p&gt;누가 얼마나 커밋했는지 보여준다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;$&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;config&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;global&lt;/span&gt; &lt;span class="comment"&gt;alias&lt;/span&gt;.&lt;span class="comment"&gt;count&lt;/span&gt; &lt;span class="comment"&gt;"shortlog&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;sn"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/articles/2012/git-secrets/git-shortlog-sn.png" alt="git-shortlog-sn"/&gt;&lt;/p&gt;

&lt;h4&gt;SCRIPT: GIT-CREDIT&lt;/h4&gt;

&lt;p&gt;가장 마지막 커밋 author 정보를 바꿀일은 종종 생긴다. 실수일 수도 있고 아닐 수도 있지만 뭐 어찌됐건 최근 커밋의 author를 마음대로 바꾸고 싶을 때가 있다. 다음과 같이 config에 등록한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="shebang"&gt;#!/bin/sh&lt;/span&gt;

git commit --amend --author &lt;span class="string"&gt;"&lt;span class="variable"&gt;$1&lt;/span&gt; &amp;lt;&lt;span class="variable"&gt;$2&lt;/span&gt;&amp;gt;"&lt;/span&gt; -C HEAD
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;git credit &amp;quot;Zach Holman&amp;quot; zach@example.com&lt;/code&gt;이라고 실행하면 최근 커밋의 author 정보가 변경된다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/holman"&gt;@holman&lt;/a&gt;님은 &lt;a href="https://twitter.com/holman"&gt;@holman&lt;/a&gt;님의 &lt;a href="https://github.com/holman/dotfiles/tree/master/bin"&gt;dotfile&lt;/a&gt; 프로젝트에 가면 &lt;a href="https://twitter.com/holman"&gt;@holman&lt;/a&gt;님이 사용하는 &lt;code&gt;git-credit&lt;/code&gt; 스크립트가 있다.&lt;/p&gt;

&lt;h4&gt;Octocat&lt;/h4&gt;

&lt;p&gt;이 슬라이드로 옥토캣은 다리가 4개고 꼬리가 1개라는 비밀을 알게 됐다. 맨날 보는 그림이지만 문어니까 그냥 다리가 8개라고 생각했었다.&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-secrets/pusheencat.png" alt="octocat"/&gt;&lt;/p&gt;

&lt;img src="http://feeds.feedburner.com/~r/github/dogfeet/~4/jdY6Ojpxb7c" height="1" width="1"/&gt;</content><feedburner:origLink>http://dogfeet.github.io/articles/2012/git-secrets.html</feedburner:origLink></entry><entry><title><![CDATA[ 잘 빠진 군체 알고리즘 - flock ]]></title><link href="http://feedproxy.google.com/~r/github/dogfeet/~3/8PyOH9CtquY/flocking-algorithm.html" /><updated>2012-12-02T15:00:00.000Z</updated><id>http://dogfeet.github.io/articles/2012/flocking-algorithm.html</id><content type="html">
&lt;style&gt;
canvas {display:block;}
.flock {margin-left: 10px;}
&lt;/style&gt;

&lt;p&gt;&lt;img src="http://dogfeet.github.io/articles/2012/flocking-algorithm/800px-Red-billed_quelea_flocking_at_waterhole.jpg" alt="flock"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://harry.me/2011/02/17/neat-algorithms---flocking/"&gt;harry&lt;/a&gt; 님의 사이트에서 보았던 플로킹 알고리즘(flockng algorithm)을 정리했다.(무려 2011년 2월 글이다.) 플로킹 알고리즘은 떼(flock)의 각 개체의 행동 모델이다. 각 개체는 보이드(boid)라고 부르며 이들은 세 가지 규칙을 이용해 움직인다. 이 알고리즘은 1986년에 Craig Reynolds가 낸 논문에서 처음 소개되었다. 원래 알고리즘은 &lt;a href="http://www.red3d.com/cwr/boids/"&gt;여기서&lt;/a&gt; 볼 수 있다. 그는 이 모델을 이용해 새 떼나 물고기 떼 등을 시뮬레이션했으며 배트맨 리턴즈의 박쥐 떼가 날아다니는 영상, 소셜 네트워크에서 의견의 흐름을 시뮬레이션해서 미래 의견을 예측하거나 분산 시스템에서도 이용되었다고 한다. 어디에서 쓰였는지는 링크를 확인하고 알고리즘이 어떻게 동작하는지 보자.&lt;/p&gt;

&lt;h2&gt;예제 먼저&lt;/h2&gt;

&lt;div class="flock" id="prettyDemo"&gt;&lt;/div&gt;

&lt;p&gt;옆의 버튼을 눌러서 한 보이드에 대한 자세한 정보와 그 범례를 볼 수 있다. : &lt;button class="awesome" id="decorateDemo"&gt;Undecorate&lt;/button&gt;&lt;/p&gt;

&lt;p&gt;구현은 harry 사이트의 것을 그대로 가져왔다. Coffee Script로 이루어져 있으며 HTML5의 canvas를 이용해서 애니메이션 데모를 보여준다. 따라서 애니메이션 데모를 보고 싶다면 canvas가 지원되는 브라우저로 들어오기를 바란다.  애니메이션은 &lt;a href="http://processingjs.org/"&gt;ProcessingJS&lt;/a&gt;를 이용해서 이루어진다. (ProcessingJS에 대한 지식은 그리 필요하진 않다.) 이 페이지의 모든 애니메이션 데모는 버튼으로 애니메이션 속도를 조정할 수 있고 애니메이션 화면을 클릭해서 일시 정지시킬 수 있다. 물론 다시 클릭하면 다시 애니메이션이 진행된다. 정지 되었을 때에는 각 보이드에 마우스를 올려서 그 보이드의 정보를 볼 수 있다. 정보를 보는 법은 이 글을 읽으면서 알 수 있으니 성급해하지 말자.&lt;/p&gt;

&lt;h2&gt;보이드 - Boid&lt;/h2&gt;

&lt;p&gt;보이드는 무리를 이루는 개체 하나하나를 부르는 이름이다. 여기저기에서 에이전트라고 하기도 하고 오브젝트라고도 하지만 원문에 보이드라고 되어있으니 여기서도 보이드라고 부르기로 하자. 보이드는 위치와 속도를 데이터로 가지고 있다. 그리고 위에서 말한 세 가지 행동 규칙을 이용해 가속도를 계산한다. 가속도는 현재 속도에 영향을 미치고 속도에 의해 다음 위치가 결정된다. 이런 일을 하는 메서드가 &lt;code&gt;step&lt;/code&gt;이다. 보이드가 너무 빨라지지 않도록 최고 속도를 정해놓고 그보다는 높아지지 않도록 조종하는 것도 중요하다. 다음 코드를 보자. &lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-coffeescript"&gt;&lt;span class="comment"&gt;# Ported almost directly from http://processingjs.org/learning/topic/flocking&lt;/span&gt;
&lt;span class="comment"&gt;# thanks a whole lot to Craig Reynolds and Daniel Shiffman&lt;/span&gt;

&lt;span class="class"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title"&gt;Boid&lt;/span&gt;&lt;/span&gt;
  location: &lt;span class="literal"&gt;false&lt;/span&gt;
  velocity: &lt;span class="literal"&gt;false&lt;/span&gt;

  constructor: (loc, processing) -&amp;amp;gt;
    &lt;span class="property"&gt;@velocity&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; Vector(Math.random()*&lt;span class="number"&gt;2&lt;/span&gt;-&lt;span class="number"&gt;1&lt;/span&gt;,Math.random()*&lt;span class="number"&gt;2&lt;/span&gt;-&lt;span class="number"&gt;1&lt;/span&gt;)
    &lt;span class="property"&gt;@location&lt;/span&gt; = loc.copy()
    &lt;span class="property"&gt;@p&lt;/span&gt; = processing

  &lt;span class="comment"&gt;# Called every frame. Calculates the acceleration using the flock method, &lt;/span&gt;
  &lt;span class="comment"&gt;# and moves the boid based on it.&lt;/span&gt;
  step: (neighbours) -&amp;amp;gt;
    acceleration = &lt;span class="keyword"&gt;this&lt;/span&gt;.flock(neighbours)
    &lt;span class="property"&gt;@velocity&lt;/span&gt;.add(acceleration).limit(MAX_SPEED) &lt;span class="comment"&gt;# Limit the maximum speed at which a boid can go&lt;/span&gt;
    &lt;span class="property"&gt;@location&lt;/span&gt;.add(&lt;span class="property"&gt;@velocity&lt;/span&gt;)
    &lt;span class="keyword"&gt;this&lt;/span&gt;._wrapIfNeeded()

  &lt;span class="comment"&gt;# Implements the flocking algorthim by collecting the three components &lt;/span&gt;
  &lt;span class="comment"&gt;# and returning a weighted sum.&lt;/span&gt;
  flock: (neighbours) -&amp;amp;gt;
    separation = &lt;span class="keyword"&gt;this&lt;/span&gt;.separate(neighbours).multiply(SEPARATION_WEIGHT)
    alignment = &lt;span class="keyword"&gt;this&lt;/span&gt;.align(neighbours).multiply(ALIGNMENT_WEIGHT)
    cohesion = &lt;span class="keyword"&gt;this&lt;/span&gt;.cohere(neighbours).multiply(COHESION_WEIGHT)
    &lt;span class="keyword"&gt;return&lt;/span&gt; separation.add(alignment).add(cohesion)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;제일 마지막 메서드인 &lt;code&gt;flock&lt;/code&gt;이 앞으로 설명할 세 가지 행동 규칙으로 가속도를 만들어내는 메서드이다. 이에 대한 자세한 이야기는 이 글의 끝에서 하도록 한다.&lt;/p&gt;

&lt;h2&gt;응집 - Cohesion&lt;/h2&gt;

&lt;div class="flock" id="cohesionDemo" style="float:right;"&gt;&lt;/div&gt;

&lt;p&gt;보이드는 자기 주변의 보이드의 곁으로 가려는 성질이 있다. 즉 무리지으려는 성질이라고 볼 수 있다. &amp;lsquo;자기 주변의 보이드'는 자신 주변 반경 n 픽셀 안에 있는 보이드로 정의되고 &lt;code&gt;NEIGHBOUR_RADIUS&lt;/code&gt;라는 상수가 그 주변을 결정하는 반경 값이다. 하나의 보이드는 주변 보이드들 사이의 무게 중심 쪽으로 방향을 튼다. &lt;/p&gt;

&lt;p&gt;옆의 예제를 보면 분홍색 보이드의 Cohesion 정보가 표시되고 있다. 녹색 원이 자신의 주변을 뜻하는 범위이고 그 안에 들어온 보이드들은 녹색으로 표시된다. 그리고 짙은 보라색 화살표가 주변 보이드들의 평균 위치 한 점으로 모인다. 분홍색 보이드는 분홍색 화살표를 이용해 &amp;lsquo;나 그쪽으로 회전 중입니다'라고 알리고 있다.&lt;/p&gt;

&lt;div style="clear:right"&gt;&lt;/div&gt;

&lt;h3&gt;코드&lt;/h3&gt;

&lt;p&gt;Cohesion은 &lt;code&gt;NEIGHBOUR_RADIUS&lt;/code&gt;안에 있는 모든 보이드의 위치의 평균이다. 코드는 &lt;code&gt;steer_to&lt;/code&gt;메서드를 거쳐 리턴한다. &lt;code&gt;stear_to&lt;/code&gt;는 현재 위치와 갈 곳을 계산해서 보이드의 방향을 자연스럽게 틀어주는 역할을 한다. 일종의 보정이라고 생각하면 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-coffeescript"&gt;&lt;span class="class"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title"&gt;Boid&lt;/span&gt;&lt;/span&gt;

  &lt;span class="comment"&gt;# 가속도를 계산할 때 cohesion요소를 계산하기 위해 호출한다.&lt;/span&gt;
  cohere: (neighbours) -&amp;amp;gt;
    sum = &lt;span class="keyword"&gt;new&lt;/span&gt; Vector
    count = &lt;span class="number"&gt;0&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; boid &lt;span class="keyword"&gt;in&lt;/span&gt; neighbours
      d = &lt;span class="property"&gt;@location&lt;/span&gt;.distance(boid.location)
      &lt;span class="keyword"&gt;if&lt;/span&gt; d &amp;amp;gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; d &amp;amp;lt; NEIGHBOUR_RADIUS
        sum.add(boid.location)
        count++

    &lt;span class="keyword"&gt;if&lt;/span&gt; count &amp;amp;gt; &lt;span class="number"&gt;0&lt;/span&gt;
      &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="keyword"&gt;this&lt;/span&gt;.steer_to sum.divide(count)
    &lt;span class="keyword"&gt;else&lt;/span&gt;
      &lt;span class="keyword"&gt;return&lt;/span&gt; sum &lt;span class="comment"&gt;# 아무런 영향도 주지 않기 위해 빈 벡터를 리턴한다.&lt;/span&gt;

  steer_to: (target) -&amp;amp;gt;
    desired = Vector.subtract(target, &lt;span class="property"&gt;@location&lt;/span&gt;) &lt;span class="comment"&gt;# 현재 위치에서 가려 하는 곳을 가리키는 벡터&lt;/span&gt;
    d = desired.magnitude()  &lt;span class="comment"&gt;# 현재 위치에서 목적지까지의 거리는 벡터의 크기이다.&lt;/span&gt;

    &lt;span class="comment"&gt;# 만약 거리가 0보다 크면 변경할 방향을 계산한다. (아니면 0을 리턴한다.)&lt;/span&gt;
    &lt;span class="keyword"&gt;if&lt;/span&gt; d &amp;amp;gt; &lt;span class="number"&gt;0&lt;/span&gt;
      desired.normalize()

      &lt;span class="comment"&gt;# 원하는 벡터의 크기를 계산하기 위한 두 옵션(1 -- 거리에 기초하여, 2 -- 최대 스피드)&lt;/span&gt;
      &lt;span class="keyword"&gt;if&lt;/span&gt; d &amp;amp;lt; &lt;span class="number"&gt;100.0&lt;/span&gt;
        desired.multiply(MAX_SPEED*(d/&lt;span class="number"&gt;100.0&lt;/span&gt;)) &lt;span class="comment"&gt;# 이 제동은 임의적으로 정했다.&lt;/span&gt;
      &lt;span class="keyword"&gt;else&lt;/span&gt;
        desired.multiply(MAX_SPEED)

      &lt;span class="comment"&gt;# Steering = Desired minus Velocity&lt;/span&gt;
      steer = desired.subtract(&lt;span class="property"&gt;@velocity&lt;/span&gt;)
      steer.limit(MAX_FORCE)  &lt;span class="comment"&gt;# 방향 전환 정도에 제한을 둔다.&lt;/span&gt;
    &lt;span class="keyword"&gt;else&lt;/span&gt;
      steer = &lt;span class="keyword"&gt;new&lt;/span&gt; Vector(&lt;span class="number"&gt;0&lt;/span&gt;,&lt;span class="number"&gt;0&lt;/span&gt;)

    &lt;span class="keyword"&gt;return&lt;/span&gt; steer
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;정렬 - Alignment&lt;/h2&gt;

&lt;div class="flock" id="alignmentDemo" style="float:right;"&gt;&lt;/div&gt;

&lt;p&gt;각 보이드는 주변의 보이드와 같은 방향을 향하려는 특성도 가지고 있다. 응집도와 비슷하게 &lt;code&gt;NEIGHBOUR_RADIUS&lt;/code&gt;의 내에 들어온 주변 보이드들의 속도의 평균을 향한다. 속도는 방향과 크기를 가지고 있으므로 평균을 구하면 방향뿐만이 아니라 크기까지 평균이 된다. 따라서 주변 보이드의 속력이 빠를 수록 정렬되려는 힘도 커진다.&lt;/p&gt;

&lt;p&gt;옆의 예제에서 보면 역시 분홍색 보이드의 정보가 보인다. 녹색 원 안에 녹색 보이드가 주변 보이드로 선정된 녀석들이고 주변 보이드의 속도는 녹색 화살표로 표시된다. 이 녹색 화살표의 평균이 분홍색 보이드의 연녹색 화살표이다. 검은색 화살표는 분홍색 보이드의 현재 속도이다. 분홍색 보이드는 다음 프레임에서 자신의 위치와 방향을 결정할 때 연녹색 화살표의 값을 이용한다. &lt;/p&gt;

&lt;div style="clear:right"&gt;&lt;/div&gt;

&lt;h3&gt;코드&lt;/h3&gt;

&lt;p&gt;이번 코드는 그리 길지 않다. 로직은 응집도 계산과 똑같다. 다만 위치의 평균이 아니라 속도의 평균인 점이 다르다. 물론 이번에도 최대값이 있어서 너무 커다란 값이 되지 않도록 조정한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-coffeescript"&gt;&lt;span class="class"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title"&gt;Boid&lt;/span&gt;&lt;/span&gt;

  &lt;span class="comment"&gt;# Alignment component for the frame's acceleration&lt;/span&gt;
  align: (neighbours) -&amp;amp;gt;
    mean = &lt;span class="keyword"&gt;new&lt;/span&gt; Vector
    count = &lt;span class="number"&gt;0&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; boid &lt;span class="keyword"&gt;in&lt;/span&gt; neighbours
      d = &lt;span class="property"&gt;@location&lt;/span&gt;.distance(boid.location)
      &lt;span class="keyword"&gt;if&lt;/span&gt; d &amp;amp;gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; d &amp;amp;lt; NEIGHBOUR_RADIUS
        mean.add(boid.velocity)
        count++

    mean.divide(count) &lt;span class="keyword"&gt;if&lt;/span&gt; count &amp;amp;gt; &lt;span class="number"&gt;0&lt;/span&gt;
    mean.limit(MAX_FORCE)
    &lt;span class="keyword"&gt;return&lt;/span&gt; mean
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;분리 - Separation&lt;/h2&gt;

&lt;div class="flock" id="separationDemo" style="float:right;"&gt;&lt;/div&gt;

&lt;p&gt;각 보이드들은 너무 가까워지지 않으려는 경향이 있다. 보이드는 일정 공간을 두어 그 안으로 다른 보이드가 들어오면 그 보이드의 반대편으로 힘이 작용하여 멀어진다. 그 개인적인 공간은 &lt;code&gt;DESIRED_SEPARATION&lt;/code&gt;이 결정하고 이 값은 &lt;code&gt;NEIGHBOUR_RADIUS&lt;/code&gt;보다 작아야 한다. 만약 이 값이 &lt;code&gt;NEIGHBOUR_RADIUS&lt;/code&gt;보다 크다면 이웃은 사라지고 모든 보이드를 배척하게 된다.&lt;/p&gt;

&lt;p&gt;이번 예제는 빨간 원이 하나 더 생겼다. 이것이 &lt;code&gt;DESIRED_SEPARATION&lt;/code&gt;값으로 결정된 생긴 개인 공간이고 이 안에 들어온 보이드는 빨간색으로 표시된다. 그리고 빨간 원 안으로 들어온 보이드에 의해 빨간 화살표로 멀어지려는 힘이 계산된다. 그 방향을 빨간 보이드의 반대 방향이 된다.&lt;/p&gt;

&lt;div style="clear:right"&gt;&lt;/div&gt;

&lt;h3&gt;코드&lt;/h3&gt;

&lt;p&gt;코드를 보면 주변 보이드들과의 거리를 검사해서 &lt;code&gt;DESIRED_SEPARATION&lt;/code&gt;보다 가까운 보이드와 거리를 정규화해서 평균을 낸다. 그 중간에 정규화된 벡터를 자신과 주변 보이드간의 거리에 반비례하게 크기를 변경한다. 이는 가까이 있을수록 더 빨리 멀어지고 싶어한다는 개념을 넣은 것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-coffeescript"&gt;&lt;span class="class"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title"&gt;Boid&lt;/span&gt;&lt;/span&gt;

  &lt;span class="comment"&gt;# Separation component for the frame's acceleration&lt;/span&gt;
  separate: (neighbours) -&amp;amp;gt;
    mean = &lt;span class="keyword"&gt;new&lt;/span&gt; Vector
    count = &lt;span class="number"&gt;0&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; boid &lt;span class="keyword"&gt;in&lt;/span&gt; neighbours
      d = &lt;span class="property"&gt;@location&lt;/span&gt;.distance(boid.location)
      &lt;span class="keyword"&gt;if&lt;/span&gt; d &amp;amp;gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="keyword"&gt;and&lt;/span&gt; d &amp;amp;lt; DESIRED_SEPARATION
        &lt;span class="comment"&gt;# Normalized, weighted by distance vector pointing away from the neighbour&lt;/span&gt;
        mean.add Vector.subtract(&lt;span class="property"&gt;@location&lt;/span&gt;,boid.location).normalize().divide(d)
        count++

    mean.divide(count) &lt;span class="keyword"&gt;if&lt;/span&gt; count &amp;amp;gt; &lt;span class="number"&gt;0&lt;/span&gt;
    mean
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;죄다 합쳐보자&lt;/h2&gt;

&lt;p&gt;위에서 계산했던 세 가지 행동 요소들을 이용해서 무리를 움직이게 하려면 아래와 같이 하면 된다. 보이드 클래스에 자신을 그리는 &lt;code&gt;render&lt;/code&gt;메서드를 넣고 이 보이드의 무리를 만들어서 움직일 &lt;code&gt;flock&lt;/code&gt;이라는 함수를 만들어 ProcessingJS의 인스턴스에 넘겨준다. &lt;code&gt;flock&lt;/code&gt;에서는 보이드를 만들어 각 보이드의 &lt;code&gt;step&lt;/code&gt;메서드와 &lt;code&gt;render&lt;/code&gt;메서드를 넣어준다.&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-coffeescript"&gt;&lt;span class="class"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title"&gt;Boid&lt;/span&gt;&lt;/span&gt;
  r: &lt;span class="number"&gt;2&lt;/span&gt; &lt;span class="comment"&gt;# "radius" of the triangle&lt;/span&gt;
  render: () -&amp;amp;gt;
    &lt;span class="comment"&gt;# Draw a triangle rotated in the direction of velocity&lt;/span&gt;
    theta = &lt;span class="property"&gt;@velocity&lt;/span&gt;.heading() + &lt;span class="property"&gt;@p&lt;/span&gt;.radians(&lt;span class="number"&gt;90&lt;/span&gt;)
    &lt;span class="property"&gt;@p&lt;/span&gt;.fill(&lt;span class="number"&gt;70&lt;/span&gt;)
    &lt;span class="property"&gt;@p&lt;/span&gt;.stroke(&lt;span class="number"&gt;255&lt;/span&gt;,&lt;span class="number"&gt;255&lt;/span&gt;,&lt;span class="number"&gt;0&lt;/span&gt;)
    &lt;span class="property"&gt;@p&lt;/span&gt;.pushMatrix()
    &lt;span class="property"&gt;@p&lt;/span&gt;.translate(&lt;span class="property"&gt;@location&lt;/span&gt;.x,&lt;span class="property"&gt;@location&lt;/span&gt;.y)
    &lt;span class="property"&gt;@p&lt;/span&gt;.rotate(theta)
    &lt;span class="property"&gt;@p&lt;/span&gt;.beginShape(&lt;span class="property"&gt;@p&lt;/span&gt;.TRIANGLES)
    &lt;span class="property"&gt;@p&lt;/span&gt;.vertex(&lt;span class="number"&gt;0&lt;/span&gt;, -&lt;span class="number"&gt;1&lt;/span&gt; * &lt;span class="property"&gt;@r&lt;/span&gt; *&lt;span class="number"&gt;2&lt;/span&gt;)
    &lt;span class="property"&gt;@p&lt;/span&gt;.vertex(-&lt;span class="number"&gt;1&lt;/span&gt; * &lt;span class="property"&gt;@r&lt;/span&gt;, &lt;span class="property"&gt;@r&lt;/span&gt; * &lt;span class="number"&gt;2&lt;/span&gt;)
    &lt;span class="property"&gt;@p&lt;/span&gt;.vertex(&lt;span class="property"&gt;@r&lt;/span&gt;, &lt;span class="property"&gt;@r&lt;/span&gt; * &lt;span class="number"&gt;2&lt;/span&gt;)
    &lt;span class="property"&gt;@p&lt;/span&gt;.endShape()
    &lt;span class="property"&gt;@p&lt;/span&gt;.popMatrix()

&lt;span class="comment"&gt;# flock function, passed the Processing instance by Processing itself&lt;/span&gt;
flock = (processing) -&amp;amp;gt;
  start = &lt;span class="keyword"&gt;new&lt;/span&gt; Vector(processing.width&lt;span class="regexp"&gt;/2,processing.height/&lt;/span&gt;&lt;span class="number"&gt;2&lt;/span&gt;)

  &lt;span class="comment"&gt;# Instantiate 100 boids who start in the middle of the map, have a maxmimum &lt;/span&gt;
  &lt;span class="comment"&gt;# speed of 2, maximum force of 0.05, and give them a reference to the &lt;/span&gt;
  &lt;span class="comment"&gt;# processing instance so they can render themselves.&lt;/span&gt;
  boids = &lt;span class="keyword"&gt;for&lt;/span&gt; i &lt;span class="keyword"&gt;in&lt;/span&gt; [&lt;span class="number"&gt;0.&lt;/span&gt;&lt;span class="number"&gt;.100&lt;/span&gt;]
    &lt;span class="keyword"&gt;new&lt;/span&gt; Boid(start, &lt;span class="number"&gt;2&lt;/span&gt;, &lt;span class="number"&gt;0.05&lt;/span&gt;, processing)

  processing.draw = -&amp;amp;gt;
    processing.background(&lt;span class="number"&gt;255&lt;/span&gt;)
    &lt;span class="keyword"&gt;for&lt;/span&gt; boid &lt;span class="keyword"&gt;in&lt;/span&gt; boids
      boid.step(boids)
      boid.render()
    &lt;span class="literal"&gt;true&lt;/span&gt;

canvas = $(&lt;span class="string"&gt;'&amp;amp;lt;canvas width="550" height="550"&amp;amp;gt;&amp;amp;lt;/canvas&amp;amp;gt;'&lt;/span&gt;).appendTo($(&lt;span class="string"&gt;'#flockingDemo'&lt;/span&gt;))[&lt;span class="number"&gt;0&lt;/span&gt;]
processingInstance = &lt;span class="keyword"&gt;new&lt;/span&gt; Processing(canvas, flock)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서 보이는 &lt;code&gt;flock&lt;/code&gt;함수는 보이드의 &lt;code&gt;flock&lt;/code&gt; 메서드와는 다르다. 위에 코드가 있지만 난 친절하니까 밑에 다시 코드를 적어주겠다.&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-coffeescript"&gt;flock: (neighbours) -&amp;amp;gt;
  separation = &lt;span class="keyword"&gt;this&lt;/span&gt;.separate(neighbours).multiply(SEPARATION_WEIGHT)
  alignment = &lt;span class="keyword"&gt;this&lt;/span&gt;.align(neighbours).multiply(ALIGNMENT_WEIGHT)
  cohesion = &lt;span class="keyword"&gt;this&lt;/span&gt;.cohere(neighbours).multiply(COHESION_WEIGHT)
  &lt;span class="keyword"&gt;return&lt;/span&gt; separation.add(alignment).add(cohesion)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자 이것이 보이드의 &lt;code&gt;flock&lt;/code&gt;메서드이다. 보이드의 것은 세 가지 행동 요소(분리, 정렬, 응집)를 이용해 가속도로 사용할 값을 계산하는 것이다. 각 요소 값을 계산하고 그것을 그대로 쓰는 것이 아니라 그 것이 가속도에 끼칠 영향도(weight)를 곱해준다. 그 값은 각각 &lt;code&gt;SEPARATION_WEIGHT&lt;/code&gt;, &lt;code&gt;ALIGNMENT_WEIGHT&lt;/code&gt;, &lt;code&gt;COHESION_WEIGHT&lt;/code&gt;이다. 보이드의 &lt;code&gt;flock&lt;/code&gt;메서드는 각 요소에 영향도를 곱해서 전부 더한다. 그게 끝이다.&lt;br/&gt;
전체 코드는 &lt;a href="https://github.com/hornairs/blog/tree/master/assets/coffeescripts/flocking"&gt;여기&lt;/a&gt;에서 구할 수 있다.&lt;/p&gt;

&lt;h2&gt;변칙&lt;/h2&gt;

&lt;h3&gt;다른 이웃&lt;/h3&gt;

&lt;p&gt;여기까지가 기본적인 플로킹 알고리즘이었다. 지금까지는 주변 보이드를 계산할 때 그냥 주변을 360도를 전부 검사했다. 하지만 만약 보이드가 인간이나 동물의 추상체라면 자신의 주변은 관찰 가능한 곳에 있는, 또는 눈에 보이는 보이드로 한정될 것이다. 따라서 주변 보이드를 계산할 때 자신의 뒤쪽은 배제할 수도 있다. 또는 정말 시야에 들어오는 보이드만을 이웃으로 규정할 수도 있다. 이런 이웃을 계산하는 방법에 따라 많은 변칙이 가능해진다.&lt;br/&gt;
공간에서 이웃은 위치상의 근접 개체이지만 다른 문제로 환원하면 논리적 근접을 다시 정의해야 한다. 예를 들어 만약 소셜 네트워크라면 이웃은 자신의 친구나 친구가 공유한 다른 친구로 될 것이다. 웹 사이트라면 직접 링크한 문서들을 이웃으로 정할 수도 있겠다.&lt;/p&gt;

&lt;h3&gt;영향도 수정&lt;/h3&gt;

&lt;p&gt;글에는 세 행동 요소들에 적용되는 영향도 값을 적어놓진 않았지만, 이 영향도 값들을 수정함으로써 보이드들이 다른 행동 양식을 보이도록 할 수도 있다.&lt;/p&gt;

&lt;h3&gt;장애물 피하기&lt;/h3&gt;

&lt;p&gt;위 데모가 진행되는 동안 마우스를 보이드로 가져다 대면 그들이 마우스 포인터를 휙휙 피하는 모습을 관찰할 수 있다. 아예 갈 수 없는 곳을 피하기도 하는데 이에 대한 설명은 글에서 하지 않았다. 공부를 더 해야 하지만 단순히 추측해보자면 특정 반경 안에 장애물이 감지되거나 장에물과 보이드가 충돌하면 속도를 줄이거나 멈추고 장애물이 없는 방향으로 속도를 올리는 것으로 생각된다. 장애물이 단순 벽일 때, 각이 있는 모서리 일 때, ㄷ자 형태의 벽일 때에 따라 장애물을 피해서 다시 빠져나갈 방법을 잘 설계해야 한다. 잘못하면 아마 벽에 무한으로 부히는 상황이 올지도 모른다.&lt;/p&gt;

&lt;h2&gt;마무리&lt;/h2&gt;

&lt;p&gt;이 글은 그냥 개인적으로 관심 있던 분야의 글이 뉴스 사이트에 올라왔고 호기심에 읽어본 글이 어쩌다 보니 이해되어 쓴 글이다. 이 모델을 어디에 사용할지는 아직 나도 모르겠지만 군체의 움직임이 예쁘게 모델링 되어있어 소개한다. 사실 예쁘게 모델링 되었다는게 정확한 모델링이라는 것과는 다른 말이지만 더 정교한 알고리즘의 기반 지식이라도 될까 기대해본다.&lt;/p&gt;

&lt;script type="text/javascript"&gt;
  var Harry = {};
&lt;/script&gt;

&lt;script src="/articles/2012/flocking-algorithm/js/processing.js" type="text/javascript"&gt;&lt;/script&gt;

&lt;script src="/articles/2012/flocking-algorithm/js/vector.js" type="text/javascript"&gt;&lt;/script&gt;

&lt;script src="/articles/2012/flocking-algorithm/js/boid.js" type="text/javascript"&gt;&lt;/script&gt;

&lt;script src="/articles/2012/flocking-algorithm/js/flock.js" type="text/javascript"&gt;&lt;/script&gt;

&lt;script src="/articles/2012/flocking-algorithm/js/flocking.js" type="text/javascript"&gt;&lt;/script&gt;

&lt;img src="http://feeds.feedburner.com/~r/github/dogfeet/~4/8PyOH9CtquY" height="1" width="1"/&gt;</content><feedburner:origLink>http://dogfeet.github.io/articles/2012/flocking-algorithm.html</feedburner:origLink></entry><entry><title><![CDATA[ Git: rerere ]]></title><link href="http://feedproxy.google.com/~r/github/dogfeet/~3/V6dUALEQbFU/git-rerere.html" /><updated>2012-11-22T00:00:00.000Z</updated><id>http://dogfeet.github.io/articles/2012/git-rerere.html</id><content type="html">
&lt;p&gt;왠지 &amp;lsquo;거꾸로 해도 이효리'가 떠오르는 이 이름, 명령어는 외우기는 쉽지만, 용법을 이해하는 데는 공을 좀 들여야 한다.&lt;/p&gt;

&lt;p&gt;어떤 Topic 브랜치는 오랫동안 Merge하지 않고 유지하기도 한다. 이런 Topic 브랜치를 Merge하면 Conflict가 날 확률이 높다. Conflict가 예상되는 브랜치를 Merge할 때 &lt;code&gt;git rerere&lt;/code&gt; 명령으로 난관을 극복하는 방법을 알아보자.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://dogfeet.github.io/articles/2012/git-rerere/overcome.jpg" alt="overcome"/&gt;&lt;/p&gt;

&lt;p&gt;이 글은 &lt;a href="http://git-scm.com/2010/03/08/rerere.html"&gt;Rerere your boat&amp;hellip;&lt;/a&gt;을 주로 참고 했다. 내용은 거의 같다.&lt;/p&gt;

&lt;h2&gt;rerere(Reuse Recorded Resolution)&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;rerere&lt;/code&gt;는 간단히 말하자면 Conflict를 해결한 Resolution을 저장해두고 같은 Conflict가 나면 저장한 Resolution을 재사용하는 명령이다.&lt;/p&gt;

&lt;p&gt;Conflict가 발생하면 우선 conflict를 해결한 다음에, 다시 Merge하기 전으로 돌아와서 다시 Merge하면 저장된 Resolution이 적용돼서 Conflict 없이 자동으로 Merge된다.&lt;/p&gt;

&lt;p&gt;어떻게 보면 말장난 같아 보일 수도 있다. Conflict를 Resolve하는 실험을 하고 실험에 성공하면 수동으로 그 실험을 재현해서 적용한다. 실패하면 다시 처음으로 돌아와 다시 시도한다. 그런데 이때 성공한 실험 내용을 기록해 뒀다가 자동으로 다시 적용하면 매우 편리할 것이다. &lt;strong&gt;&lt;code&gt;rerere&lt;/code&gt; 옵션을 켜면 Conflict를 Resolve하는데 성공하면 그 내용을 자동으로 저장해주고 같은 일을 다시 시도하면 git이 자동으로 재현해준다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;그러면 이 명령어 어떻게 동작하는지 예제와 함께 살펴보자.&lt;/p&gt;

&lt;h3&gt;설정&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;rerere&lt;/code&gt; 기능은 설정해야 사용할 수 있다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;%&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;config&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;global&lt;/span&gt; &lt;span class="comment"&gt;rerere&lt;/span&gt;.&lt;span class="comment"&gt;enabled&lt;/span&gt; &lt;span class="comment"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 저장소에 &lt;code&gt;.git/rr-cache&lt;/code&gt; 디렉토리를 만들어도 이 기능이 켜지지만, 그냥 &lt;code&gt;--global&lt;/code&gt;에 설정하자.&lt;/p&gt;

&lt;h3&gt;Hello World&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;hello.js&lt;/code&gt; 프로그램 하나인 프로젝트가 있다. master 브랜치의 &lt;code&gt;hello.js&lt;/code&gt; 프로그램은 아래와 같다:&lt;/p&gt;
&lt;pre&gt;&lt;code class="lang-javascript"&gt;#!&lt;span class="regexp"&gt;/usr/&lt;/span&gt;bin/env node

console.log( &lt;span class="string"&gt;'hello world'&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 아래와 같이 프로젝트를 진행한다. master 브랜치의 메시지를 'hola world'로 변경하고 i18n-world 브랜치의 메시지는 'hello mundo'로 변경한다:&lt;/p&gt;

&lt;p&gt;&lt;img src="/articles/2012/git-rerere/rerere1.png" alt="rerere1"/&gt;&lt;/p&gt;

&lt;p&gt;이 상태에서 Merge를 하면 Conflict가 난다. 이 예제의 Conflict는 너무 간단해서 Recorded Resolution이 필요하지 않지만 &lt;code&gt;rerere&lt;/code&gt;를 설명하기에는 더없이 좋은 예다.&lt;/p&gt;

&lt;h3&gt;Recored Resolution 만들기&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://twitter.com/chacon"&gt;@chacon&lt;/a&gt;님은 쓴 원래 글에서는 Conflict를 해결하는 브랜치에 바로 Merge하는 방법으로 Resolution을 만들었다. 그리고 Reset한 후에 다시 Merge해서 Resolution을 저장했다. 이 글에서는 detached HEAD를 이용하는 방법을 설명한다. 뭐 결과는 같지만 난 이 방법이 더 좋다.&lt;/p&gt;

&lt;p&gt;먼저 detached HEAD 상태로 만든다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% git checkout HEAD^0
Note: checking out 'HEAD^0'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and &lt;span class="operator"&gt;&lt;span class="keyword"&gt;commit&lt;/span&gt; them, &lt;span class="keyword"&gt;and&lt;/span&gt; you can discard &lt;span class="keyword"&gt;any&lt;/span&gt; commits you make &lt;span class="keyword"&gt;in&lt;/span&gt; this
state without impacting &lt;span class="keyword"&gt;any&lt;/span&gt; branches &lt;span class="keyword"&gt;by&lt;/span&gt; performing another checkout.

If you want &lt;span class="keyword"&gt;to&lt;/span&gt; &lt;span class="keyword"&gt;create&lt;/span&gt; a new branch &lt;span class="keyword"&gt;to&lt;/span&gt; retain commits you &lt;span class="keyword"&gt;create&lt;/span&gt;, you may
&lt;span class="keyword"&gt;do&lt;/span&gt; so (now &lt;span class="keyword"&gt;or&lt;/span&gt; later) &lt;span class="keyword"&gt;by&lt;/span&gt; &lt;span class="keyword"&gt;using&lt;/span&gt; -b &lt;span class="keyword"&gt;with&lt;/span&gt; the checkout command again. Example:

  git checkout -b new_branch_name

HEAD &lt;span class="keyword"&gt;is&lt;/span&gt; now &lt;span class="keyword"&gt;at&lt;/span&gt; &lt;span class="number"&gt;7&lt;/span&gt;d71bbe... hola world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;master 브랜치가 가리키는 &lt;code&gt;7d71bbe&lt;/code&gt;를 checkout 했기 때문에 워킹 디렉토리 내용은 master 브랜치와 같다. 단지 'detached HEAD&amp;rsquo; 상태인 것만 다르다. 그래서 여기서 커밋을 하면 &amp;lsquo;detached HEAD&amp;rsquo; 상태로 커밋된다. master 브랜치는 움직이지 않는다.&lt;/p&gt;

&lt;p&gt;그러면 여기서 Merge한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% git merge i18n-world
Auto-merging hello.js
CONFLICT (content): Merge conflict &lt;span class="keyword"&gt;in&lt;/span&gt; hello.js
Recorded preimage &lt;span class="keyword"&gt;for&lt;/span&gt; 'hello.js'
Automatic merge failed; fix conflicts &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="keyword"&gt;then&lt;/span&gt; commit &lt;span class="keyword"&gt;the&lt;/span&gt; &lt;span class="constant"&gt;result&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;보통 Conflict 날 때의 상황과 다르게 &amp;ldquo;Recorded preimage for &amp;lsquo;hello.js&amp;rsquo;&amp;rdquo; 라는 메시지를 추가로 보여준다. &lt;code&gt;rerere&lt;/code&gt;를 켰기 때문에 생겼다.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git status&lt;/code&gt;는 Conflict가 있다고 아래와 같이 알려준다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% git status
&lt;span class="preprocessor"&gt;# Not currently on any branch.&lt;/span&gt;
&lt;span class="preprocessor"&gt;# Unmerged paths:&lt;/span&gt;
&lt;span class="preprocessor"&gt;#   (use "git add/rm &amp;lt;file&amp;gt;..." as appropriate to mark resolution)&lt;/span&gt;
&lt;span class="preprocessor"&gt;#&lt;/span&gt;
&lt;span class="preprocessor"&gt;#       both modified:      hello.js&lt;/span&gt;
&lt;span class="preprocessor"&gt;#&lt;/span&gt;
no changes added to commit (use &lt;span class="string"&gt;"git add"&lt;/span&gt; and/or &lt;span class="string"&gt;"git commit -a"&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;git diff&lt;/code&gt;라고 실행하면 어느 부분에서 Conflict가 난 것인지 보여준다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;diff&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;cc&lt;/span&gt; &lt;span class="comment"&gt;hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="comment"&gt;index&lt;/span&gt; &lt;span class="comment"&gt;68d2f27&lt;/span&gt;,&lt;span class="comment"&gt;2c3b5e5&lt;/span&gt;.&lt;span class="string"&gt;.&lt;/span&gt;&lt;span class="comment"&gt;0000000&lt;/span&gt;
&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt; &lt;span class="comment"&gt;a/hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt; &lt;span class="comment"&gt;b/hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="comment"&gt;@@@&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;4&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;4&lt;/span&gt; &lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;8&lt;/span&gt; &lt;span class="comment"&gt;@@@&lt;/span&gt;
  &lt;span class="comment"&gt;#!/usr/bin/env&lt;/span&gt; &lt;span class="comment"&gt;node&lt;/span&gt;

&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; &lt;span class="comment"&gt;ours&lt;/span&gt;
 &lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="comment"&gt;console&lt;/span&gt;.&lt;span class="comment"&gt;log(&lt;/span&gt; &lt;span class="comment"&gt;'hola&lt;/span&gt; &lt;span class="comment"&gt;world')&lt;/span&gt;
&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="comment"&gt;=======&lt;/span&gt;
&lt;span class="literal"&gt;+&lt;/span&gt; &lt;span class="comment"&gt;console&lt;/span&gt;.&lt;span class="comment"&gt;log(&lt;/span&gt; &lt;span class="comment"&gt;'hello&lt;/span&gt; &lt;span class="comment"&gt;mundo')&lt;/span&gt;
&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; &lt;span class="comment"&gt;theirs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 &amp;lsquo;hello.js&amp;rsquo; 파일을 편집해서 &amp;lsquo;hola mundo'로 Conflict를 해결하고 저장한다. 아직 Resolve를 Mark하지 않은 상태에서 &lt;code&gt;git diff&lt;/code&gt;를 실행하면 아래와 같이 나온다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;diff&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;cc&lt;/span&gt; &lt;span class="comment"&gt;hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="comment"&gt;index&lt;/span&gt; &lt;span class="comment"&gt;68d2f27&lt;/span&gt;,&lt;span class="comment"&gt;2c3b5e5&lt;/span&gt;.&lt;span class="string"&gt;.&lt;/span&gt;&lt;span class="comment"&gt;0000000&lt;/span&gt;
&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt; &lt;span class="comment"&gt;a/hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt; &lt;span class="comment"&gt;b/hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="comment"&gt;@@@&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;4&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;4&lt;/span&gt; &lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;4&lt;/span&gt; &lt;span class="comment"&gt;@@@&lt;/span&gt;
  &lt;span class="comment"&gt;#!/usr/bin/env&lt;/span&gt; &lt;span class="comment"&gt;node&lt;/span&gt;

&lt;span class="literal"&gt;-&lt;/span&gt; &lt;span class="comment"&gt;console&lt;/span&gt;.&lt;span class="comment"&gt;log(&lt;/span&gt; &lt;span class="comment"&gt;'hola&lt;/span&gt; &lt;span class="comment"&gt;world')&lt;/span&gt;
 &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;console&lt;/span&gt;.&lt;span class="comment"&gt;log(&lt;/span&gt; &lt;span class="comment"&gt;'hello&lt;/span&gt; &lt;span class="comment"&gt;mundo')&lt;/span&gt;
&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="comment"&gt;console&lt;/span&gt;.&lt;span class="comment"&gt;log(&lt;/span&gt; &lt;span class="comment"&gt;'hola&lt;/span&gt; &lt;span class="comment"&gt;mundo')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 명령은 'hola world'가 'hello mundo'와 Merge돼서 'hola mundo'가 되는 거라고 보여준다. 그런데 웬걸 &lt;code&gt;git add&lt;/code&gt; 명령으로 Resolution을 Mark하면 &lt;code&gt;git diff&lt;/code&gt; 명령은 더는 이런 메시지를 보여주지 않는다. 대신 &lt;code&gt;git rerere diff&lt;/code&gt;를 사용해야 한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="header"&gt;--- a/hello.js&lt;/span&gt;
&lt;span class="header"&gt;+++ b/hello.js&lt;/span&gt;
&lt;span class="chunk"&gt;@@ -1,8 +1,4 @@&lt;/span&gt;
 #!/usr/bin/env node

&lt;span class="deletion"&gt;-&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;
&lt;span class="deletion"&gt;-console.log( 'hello mundo')&lt;/span&gt;
&lt;span class="deletion"&gt;-=======&lt;/span&gt;
&lt;span class="deletion"&gt;-console.log( 'hola world')&lt;/span&gt;
&lt;span class="deletion"&gt;-&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="addition"&gt;+console.log( 'hola mundo')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Resolution은 다 만들었고 이제 커밋한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% git commit -m '&lt;span class="keyword"&gt;sample&lt;/span&gt; resolution'
Recorded resolution &lt;span class="keyword"&gt;for&lt;/span&gt; 'hello.js'.
[detached HEAD f35bf55] &lt;span class="keyword"&gt;sample&lt;/span&gt; resolution
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;ldquo;Recorded resolution for 'hello.js&amp;rsquo;&amp;quot;라는 메시지는 Resolution이 저장됐음을 보여주는 것이고 &amp;quot;detached HEAD&amp;quot;는 detached HEAD 상태에서 커밋했기 때문에 보여주는 것이다.&lt;/p&gt;

&lt;p&gt;이제 Resolution은 다 만들었다. Conflict를 해결하는 실험을 성공적으로 마친 것이다. 이 실험 결과를 실전에 적용해보자.&lt;/p&gt;

&lt;h2&gt;rerere&lt;/h2&gt;

&lt;p&gt;i18n-world를 master로 Merge하기 전에 i18n-world를 Rebase한다. 먼저 i18n-world를 Checkout한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% git co i18n-world
Warning: you are leaving 1 &lt;span class="operator"&gt;&lt;span class="keyword"&gt;commit&lt;/span&gt; behind, &lt;span class="keyword"&gt;not&lt;/span&gt; connected &lt;span class="keyword"&gt;to&lt;/span&gt;
&lt;span class="keyword"&gt;any&lt;/span&gt; &lt;span class="keyword"&gt;of&lt;/span&gt; your branches:

  f35bf55 sample resolution

If you want &lt;span class="keyword"&gt;to&lt;/span&gt; keep them &lt;span class="keyword"&gt;by&lt;/span&gt; creating a new branch, this may be a good &lt;span class="keyword"&gt;time&lt;/span&gt;
&lt;span class="keyword"&gt;to&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; so &lt;span class="keyword"&gt;with&lt;/span&gt;:

 git branch new_branch_name f35bf550d886286e5e75569fb9597c664cd7743d

Switched &lt;span class="keyword"&gt;to&lt;/span&gt; branch &lt;span class="string"&gt;'i18n-world'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;detached HEAD에서 벗어난다는 경고 메시지를 보여준다. 그리고 Rebase한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% git rebase master
First, rewinding head &lt;span class="keyword"&gt;to&lt;/span&gt; replay your work &lt;span class="function_start"&gt;&lt;span class="keyword"&gt;on&lt;/span&gt; &lt;span class="title"&gt;top&lt;/span&gt;&lt;/span&gt; &lt;span class="keyword"&gt;of&lt;/span&gt; &lt;span class="keyword"&gt;it&lt;/span&gt;...
Applying: hello mundo
Using index info &lt;span class="keyword"&gt;to&lt;/span&gt; reconstruct a base tree...
Falling &lt;span class="keyword"&gt;back&lt;/span&gt; &lt;span class="keyword"&gt;to&lt;/span&gt; patching base &lt;span class="keyword"&gt;and&lt;/span&gt; &lt;span class="number"&gt;3&lt;/span&gt;-way merge...
Auto-merging hello.js
CONFLICT (content): Merge conflict &lt;span class="keyword"&gt;in&lt;/span&gt; hello.js
Resolved 'hello.js' using previous resolution.
Failed &lt;span class="keyword"&gt;to&lt;/span&gt; merge &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="keyword"&gt;the&lt;/span&gt; changes.
Patch failed &lt;span class="keyword"&gt;at&lt;/span&gt; &lt;span class="number"&gt;0001&lt;/span&gt; hello mundo

When you have resolved this problem &lt;span class="command"&gt;run&lt;/span&gt; &lt;span class="string"&gt;"git rebase --continue"&lt;/span&gt;.
If you would prefer &lt;span class="keyword"&gt;to&lt;/span&gt; skip this patch, instead &lt;span class="command"&gt;run&lt;/span&gt; &lt;span class="string"&gt;"git rebase --skip"&lt;/span&gt;.
To check out &lt;span class="keyword"&gt;the&lt;/span&gt; original branch &lt;span class="keyword"&gt;and&lt;/span&gt; stop rebasing &lt;span class="command"&gt;run&lt;/span&gt; &lt;span class="string"&gt;"git rebase --abort"&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;quot;Resolved &amp;lsquo;hello.js&amp;rsquo; using previous resolution&amp;rdquo; 메시지가 추가돼 있다. 편집기로 hello.js를 열어보면 좀 전에 만들었던 Resolution대로 파일이 Resolve됐음을 알 수 있다. &lt;code&gt;git diff&lt;/code&gt; 명령으로 차이를 확인할 수 있다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;%&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;diff&lt;/span&gt;
&lt;span class="comment"&gt;diff&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;cc&lt;/span&gt; &lt;span class="comment"&gt;hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="comment"&gt;index&lt;/span&gt; &lt;span class="comment"&gt;68d2f27&lt;/span&gt;,&lt;span class="comment"&gt;2c3b5e5&lt;/span&gt;.&lt;span class="string"&gt;.&lt;/span&gt;&lt;span class="comment"&gt;0000000&lt;/span&gt;
&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt; &lt;span class="comment"&gt;a/hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt; &lt;span class="comment"&gt;b/hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="comment"&gt;@@@&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;4&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;4&lt;/span&gt; &lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;4&lt;/span&gt; &lt;span class="comment"&gt;@@@&lt;/span&gt;
  &lt;span class="comment"&gt;#!/usr/bin/env&lt;/span&gt; &lt;span class="comment"&gt;node&lt;/span&gt;

&lt;span class="literal"&gt;-&lt;/span&gt; &lt;span class="comment"&gt;console&lt;/span&gt;.&lt;span class="comment"&gt;log(&lt;/span&gt; &lt;span class="comment"&gt;'hola&lt;/span&gt; &lt;span class="comment"&gt;world')&lt;/span&gt;
 &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;console&lt;/span&gt;.&lt;span class="comment"&gt;log(&lt;/span&gt; &lt;span class="comment"&gt;'hello&lt;/span&gt; &lt;span class="comment"&gt;mundo')&lt;/span&gt;
&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="comment"&gt;console&lt;/span&gt;.&lt;span class="comment"&gt;log(&lt;/span&gt; &lt;span class="comment"&gt;'hola&lt;/span&gt; &lt;span class="comment"&gt;mundo')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/articles/2012/git-rerere/rerere3.png" alt="rerere1"/&gt;&lt;/p&gt;

&lt;p&gt;그러면 이 상태에서 Resolution을 Mark하고 &lt;code&gt;git rebase --continue&lt;/code&gt;를 실행하면 Rebase가 완료된다. 아래와 같이 실행한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="preprocessor"&gt;# git add .&lt;/span&gt;
&lt;span class="preprocessor"&gt;# git rebase --continue&lt;/span&gt;
Applying: hello mundo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rerere&lt;/code&gt;를 이용한 Merge를 마쳤다. &amp;lsquo;detached HEAD&amp;rsquo; 상태를 만들어서 Conflict를 해결하는 실험을 하고 Resolution을 만들어 놓는다. 그다음에 다시 Merge를 하면 만들어 놓은 Resolution이 재사용된다. 그래서 명령어 이름이 &amp;lsquo;rerere(REuse REcorded REsolution)'이다.&lt;/p&gt;

&lt;h3&gt;Resolution을 재사용하지 않기&lt;/h3&gt;

&lt;p&gt;Resolution을 Mark하기 전으로 돌아가 보자. &lt;code&gt;git rebase master&lt;/code&gt;를 실행하면 자동으로 저장된 Resolution이 적용된다. 그 상태로 돌아가서 &lt;code&gt;git diff&lt;/code&gt;를 실행하면 결과는 아래와 같다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="comment"&gt;%&lt;/span&gt; &lt;span class="comment"&gt;git&lt;/span&gt; &lt;span class="comment"&gt;diff&lt;/span&gt;
&lt;span class="comment"&gt;diff&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;cc&lt;/span&gt; &lt;span class="comment"&gt;hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="comment"&gt;index&lt;/span&gt; &lt;span class="comment"&gt;68d2f27&lt;/span&gt;,&lt;span class="comment"&gt;2c3b5e5&lt;/span&gt;.&lt;span class="string"&gt;.&lt;/span&gt;&lt;span class="comment"&gt;0000000&lt;/span&gt;
&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="literal"&gt;-&lt;/span&gt; &lt;span class="comment"&gt;a/hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt; &lt;span class="comment"&gt;b/hello&lt;/span&gt;.&lt;span class="comment"&gt;js&lt;/span&gt;
&lt;span class="comment"&gt;@@@&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;4&lt;/span&gt; &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;4&lt;/span&gt; &lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="comment"&gt;1&lt;/span&gt;,&lt;span class="comment"&gt;4&lt;/span&gt; &lt;span class="comment"&gt;@@@&lt;/span&gt;
  &lt;span class="comment"&gt;#!/usr/bin/env&lt;/span&gt; &lt;span class="comment"&gt;node&lt;/span&gt;

&lt;span class="literal"&gt;-&lt;/span&gt; &lt;span class="comment"&gt;console&lt;/span&gt;.&lt;span class="comment"&gt;log(&lt;/span&gt; &lt;span class="comment"&gt;'hola&lt;/span&gt; &lt;span class="comment"&gt;world')&lt;/span&gt;
 &lt;span class="literal"&gt;-&lt;/span&gt;&lt;span class="comment"&gt;console&lt;/span&gt;.&lt;span class="comment"&gt;log(&lt;/span&gt; &lt;span class="comment"&gt;'hello&lt;/span&gt; &lt;span class="comment"&gt;mundo')&lt;/span&gt;
&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="literal"&gt;+&lt;/span&gt;&lt;span class="comment"&gt;console&lt;/span&gt;.&lt;span class="comment"&gt;log(&lt;/span&gt; &lt;span class="comment"&gt;'hola&lt;/span&gt; &lt;span class="comment"&gt;mundo')&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서 git이 자동으로 적용해준 Resolution이 마음에 들지 않으면 다시 Conflict 파일을 생성할 수 있다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% git checkout --conflict=merge hello.j
% cat hello.js
#!&lt;span class="regexp"&gt;/usr/&lt;/span&gt;bin/env node

&amp;lt;&lt;span class="xml"&gt;&lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="attribute"&gt;ours&lt;/span&gt;
&lt;span class="attribute"&gt;console.log&lt;/span&gt;( '&lt;span class="attribute"&gt;hola&lt;/span&gt; &lt;span class="attribute"&gt;world&lt;/span&gt;')
=&lt;span class="value"&gt;======&lt;/span&gt;
&lt;span class="attribute"&gt;console.log&lt;/span&gt;( '&lt;span class="attribute"&gt;hello&lt;/span&gt; &lt;span class="attribute"&gt;mundo&lt;/span&gt;')
&amp;gt;&lt;/span&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; theirs
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;--conflict&lt;/code&gt; 옵션은 Conflict를 해결할 때 사용하면 유용하다. &lt;code&gt;merge&lt;/code&gt; 대신 &lt;code&gt;diff3&lt;/code&gt;를 사용하면 base Commit의 것도 알 수 있다. Checkout명령은 &lt;code&gt;.git&lt;/code&gt; 데이터베이스에 들어 있는 내용을 워킹 디렉토리로 복사하는 명령이다. 이 명령을 실행하면 충돌이 표시된 hello.js파일이 워킹 디렉토리에 생성된다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% git checkout --conflict=diff3 hello.js
% cat hello.js
#!&lt;span class="regexp"&gt;/usr/&lt;/span&gt;bin/env node

&amp;lt;&lt;span class="xml"&gt;&lt;span class="tag"&gt;&amp;lt;&lt;span class="title"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="attribute"&gt;ours&lt;/span&gt;
&lt;span class="attribute"&gt;console.log&lt;/span&gt;( '&lt;span class="attribute"&gt;hola&lt;/span&gt; &lt;span class="attribute"&gt;world&lt;/span&gt;')
||||||| &lt;span class="attribute"&gt;base&lt;/span&gt;
&lt;span class="attribute"&gt;console.log&lt;/span&gt;( '&lt;span class="attribute"&gt;hello&lt;/span&gt; &lt;span class="attribute"&gt;world&lt;/span&gt;')
=&lt;span class="value"&gt;======&lt;/span&gt;
&lt;span class="attribute"&gt;console.log&lt;/span&gt;( '&lt;span class="attribute"&gt;hello&lt;/span&gt; &lt;span class="attribute"&gt;mundo&lt;/span&gt;')
&amp;gt;&lt;/span&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; theirs
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 원하는 데로 편집하고 &lt;code&gt;git add .;git rebase --continue&lt;/code&gt; 명령을 실행하면 Rebase는 완료된다. 하지만, 저장해둔 Resolution을 다시 적용하고 싶어지면 아래와 같이 복원한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% git rerere
Resolved &lt;span class="string"&gt;'hello.js'&lt;/span&gt; &lt;span class="keyword"&gt;using&lt;/span&gt; previous resolution.
% cat hello.js
&lt;span class="preprocessor"&gt;#!/usr/bin/env node&lt;/span&gt;

console.log( &lt;span class="string"&gt;'hola mundo'&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Conflict를 다시 해결했으니 계속 진행해서 Rebate를 완료한다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class="preprocessor"&gt;# git add .&lt;/span&gt;
&lt;span class="preprocessor"&gt;# git rebase --continue&lt;/span&gt;
Applying: hello mundo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이상으로 &lt;code&gt;rerere&lt;/code&gt; 명령에 대해 알아보았다.&lt;/p&gt;

&lt;h2&gt;참고&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-rerere.html"&gt;git-rerere&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://git-scm.com/2010/03/08/rerere.html"&gt;Rerere your boat&amp;hellip;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://gitster.livejournal.com/41795.html"&gt;Fun with rerere&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;img src="http://feeds.feedburner.com/~r/github/dogfeet/~4/V6dUALEQbFU" height="1" width="1"/&gt;</content><feedburner:origLink>http://dogfeet.github.io/articles/2012/git-rerere.html</feedburner:origLink></entry></feed>
