Home

Advertisement

November 26th, 2009


12:06 pm - Fun with merges and purposes of branches
When merging branches in git, all branches are taken as equals. This stems from the consistent view towards history git maintains at the philosophical level, and the philosophy, if not the mechanism and implementation, should be applicable to any SCM that supports merges.

This entry is about that philosophy and the implications from it, as it is important to understand what you are doing when you make merges.

First, the philosophy part.
  • A branch has a purpose. The most obvious is a topic branch you use for developing a new feature. A topic branch 'add-frotz' would be about adding a new 'frotz' feature and shouldn't do anything else. In a well organized project, 'master' branch (or 'trunk' if you are coming from subversion) has the sole purpose of containing proven-to-be-good changes to produce the next release. Your per-customer branch is to contain the changes suitable for the next customer code dump and nothing else. It typically consists of bugfixes and selected features the particular customer asked (and paid for).
  • The act of making a commit with one or more parents and advancing the tip of a branch with that commit is to make this statement: I have considered what all of these parent commits represent, and in my belief the state I am committing is more suitable for the purpose of this branch than any of them.
As the consequence of the latter point, when you merge a topic branch 'add-frotz' to your 'master' branch, you are obviously incorporating the new 'frotz' feature, but more importantly, you are stating that it is desirable to have that 'frotz' feature in the 'master' branch. This is because...
  • The commit at the tip of 'add-frotz' (one of the parents of the resulting merge) didn't contain any of the other good changes happened on the 'master' branch', so compared to that commit, the resulting merge commit is more suitable for the purpose of the 'master' branch.
  • The commit at the tip of 'master' (the other parent of the resulting merge) didn't contain the new 'frotz' feature, and your wanting to have that feature in the next release makes the resulting merge more suitable for the purpose of the 'master' branch.
If the latter were not true, you wouldn't be merging 'add-frotz' to 'master'.

This is why you do not merge an integration branch 'master' into a topic branch 'add-frotz' at random points. Merging in that direction declares that all the other unrelated changes done on 'master' in preparation for the next release somehow bring 'add-frotz' closer to the goal of the 'frotz' topic, namely, realizing the new 'frotz' feature---that is usually not true.

Similarly, you in general do not merge 'master' into a branch for supporting a particular customer, especially if the purpose of 'master' is to prepare for the next release for the general public, and if the purpose of 'customer' is to support the customer by maintaining the last release with only bugfixes and customer specific features. For the purpose of a typical customer branch, random new features meant for general public that would appear in the next release are not wanted. This is even more true if the purpose of your 'master' branch is to hold any random thing that happened recently in the development community that might be interesting to somebody, which is unfortunately the way 'master' is used in many less well-organized projects.

So instead, a branch meant for a particular customer would typically merge directly from topic branches, to get specific set of bugfixes, and selected new features.

Sometimes it is valid to merge an integration branch like 'master' into a topic branch like 'add-frotz', and it is not inconsistent with the philosophical background I explained above. If you started working on adding a new feature 'frotz' back when the 'master' release was 1.0, and later the codebase of 'master' has substantially changed and your 'add-frotz' branch does not cleanly merge anymore into 'master' that is preparing for 2.0 release, you could choose to change the purpose of your 'add-frotz' branch from "add frotz feature" to a more specific "add frotz feature in a way that it merges cleanly to the 2.0 codedbase". It is valid only because the codebase between 1.0 release made on 'master' and the current one working toward producing 2.0 release has significantly changed, and a generlc "add frotz feature" cannot make sense anymore without saying to which codebase it is adding the feature.

In order for the act of merging 'master' into 'add-frotz' to declare that the resulting merge is more suitable than both the tip of 'master' and 'add-frotz' for the purpose of 'add-frotz' branch, however, the purpose of 'add-frotz' branch has to change from "add frotz (to the version the branch forked from)" to "add frotz to the recent master cleanly".

The implication of this merge is to also make the resulting branch unsuitable for the original purpose of the branch; you cannot merge the 'add-frotz' topic to older 1.0 codebase anymore. It is by choice made by you when you decided to merge 'master' to it.

Of course, you could choose to do something like this to keep two branches with separate purposes when this happens:


$ git checkout add-frotz
$ git branch add-frotz-to-1.0
$ git merge master


You create a new branch 'add-frotz-to-1.0' with the purpose of building the 'frotz' feature on top of the older 1.0 codebase. After that is done, you repurpose 'add-frotz' branch by merging 'master' into it, at which point, the new purpose of the branch becomes adding 'frotz' cleanly to the newer 'master' for 2.0 release.
Tags:

(1 comment | Leave a comment)

November 24th, 2009


06:11 pm - ふたすりめが届いた。
入門Gitのふたすり目の見本本が届いた。ここのところ、各書店で品薄、とのうわさを聞いていたので、まずは増刷してもらって一安心である。初版本のあちこちの誤植も直してもらってある。

わざわざ日本からEMSで送ってもらうと、中身よりも送料の方が余分にかかるので、出版社さんには恐縮である。Printed Matter扱いにすると安かったりしないのかな、とも思う。



サポートページ、というほどではないですが、http://www.kernel.org/~junio/ に、現在の正誤表を置いてあります。
Tags:

(Leave a comment)

November 17th, 2009


06:13 pm - Fun with rerere

When you start using the topic branch workflow, you would merge topics often into a throw-away testing branch, and from time to time, end up performing the same conflict resolution over and over again. Git has a mechanism called rerere to help you in such a situation.

Even people who use rerere often do not realize one interesting aspect of the command, and this article is about showing off that part.

Let's pretend that you start from this base version.

$ mkdir /var/tmp/practice-rerere
$ cd /var/tmp/practice-rerere
$ git init
$ cat >hello.c <<\EOF
#include <stdio.h>
#include <string.h>

/*
 * Say the message
 */
void hello(char *message)
{
        printf("%s\n", message);
}

/*
 * Show greetings
 */
void greetings(char *message)
{
        hello(message);
        printf("Your message is %d bytes long\n",
               (int) strlen(message));
}

/*
 * Main program
 */
int main(int ac, char **av)
{
        greetings(av[1]);
        return 0;
}
EOF
A very simple "hello world" program. You can try it like this if you want to.
$ cc -o hello ./hello.c && ./hello "hello world"
hello world
Your message is 11 bytes long
Commit it as the initial version.
$ git add hello.c
$ git commit -m initial
Now, let's create a topic to work on upcasing the first word of the message.
$ git checkout -b upcase-first
$ git apply <<\EOF && git commit -a -m "upcase first word"
diff --git a/hello.c b/hello.c
index e0dbe10..7fcb4f3 100644
--- a/hello.c
+++ b/hello.c
@@ -1,11 +1,16 @@
+#include <ctype.h>
 #include <stdio.h>
 #include <string.h>

 /*
  * Say the message
  */
-void hello(char *message)
+void hello(char *message, int upcase_first)
 {
+       char *cp;
+       for (cp = message; *cp && !isspace(*cp); cp++)
+               *cp = toupper(*cp);
+
        printf("%s\n", message);
 }

@@ -14,7 +19,7 @@ void hello(char *message)
  */
 void greetings(char *message)
 {
-       hello(message);
+       hello(message, 1);
        printf("Your message is %d bytes long\n",
               (int) strlen(message));
 }
EOF
$ cc -o hello ./hello.c && ./hello "hello world"
HELLO world
Your message is 11 bytes long
While you are working on this change, let's pretend that somebody committed a change on a 'const-fix' branch to tighten constness.
$ git checkout -b const-fix master
$ git apply <<\EOF && git commit -a -m "constness fix"
diff --git a/hello.c b/hello.c
index e0dbe10..9970faa 100644
--- a/hello.c
+++ b/hello.c
@@ -4,7 +4,7 @@
 /*
  * Say the message
  */
-void hello(char *message)
+void hello(const char *message)
 {
        printf("%s\n", message);
 }
@@ -12,7 +12,7 @@ void hello(char *message)
 /*
  * Show greetings
  */
-void greetings(char *message)
+void greetings(const char *message)
 {
        hello(message);
        printf("Your message is %d bytes long\n",
@@ -22,7 +22,7 @@ void greetings(char *message)
 /*
  * Main program
  */
-int main(int ac, char **av)
+int main(int ac, const char **av)
 {
        greetings(av[1]);
        return 0;
EOF
Now, you would want to try your topic with this new branch to make sure they play well together. Don't merge 'const-fix' into your topic for this, though. Your 'upcase-first' branch is about upcasing the first word in the message, and should only contain commits that are relevant to that goal. Instead, you would try merging on a throw-away branch. Recent git allows you to "detach HEAD", so let's make use of that.
$ git checkout upcase-first^0
Note: moving to 'upcase-first^0' which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b <new_branch_name>
HEAD is now at 362a9fb... upcase first
This temporarily checks out the named commit (in this case, the commit at the tip of your upcase-first branch). Since the purpose of this article is to show the power of rerere, we will do a little magic here to enable it.
$ mkdir .git/rr-cache
Don't worry too much about it for now. This needs to be done only once in your repository. Now to the merge.
$ git merge const-fix
Auto-merging hello.c
CONFLICT (content): Merge conflict in hello.c
Recorded preimage for 'hello.c'
Automatic merge failed; fix conflicts and then commit the result.
This results in a merge conflict. Let's examine what happened.
$ cat hello.c
#include <ctype.h>
#include <stdio.h>
#include <string.h>

/*
 * Say the message
 */
<<<<<<< HEAD
void hello(char *message, int upcase_first)
=======
void hello(const char *message)
>>>>>>> const-fix
{
        char *cp;
        for (cp = message; *cp && !isspace(*cp); cp++)
                *cp = toupper(*cp);

        printf("%s\n", message);
}

/*
 * Show greetings
...
There is a constness change on the 'const-fix' side, while your side added an "upcase_first" argument to the function hello(). The conflicted part is shown enclosed in
<<<<<<< HEAD
void hello(char *message, int upcase_first) --- your changes
=======
void hello(const char *message) --- their changes
>>>>>>> const-fix
and you would resolve it to read like this.
void hello(const char *message, int upcase_first)
But that is not enough. The way you implemented your upcase-first is to use a pointer cp that walks the message and upcase the first word in place. Now you are not allowed to overwrite message, so a different solution is necessary. Your new hello() function may look like this:
void hello(const char *message, int upcase_first)
{
        const char *cp;
        for (cp = message; *cp && !isspace(*cp); cp++)
                putchar(toupper(*cp));
        message = cp;

        printf("%s\n", message);
}
After editing hello.c like that, test the result.
$ cc -o hello ./hello.c && ./hello "hello world"
HELLO world
Your message is 11 bytes long
You are satisfied that your changes, even though they have conflicts with somebody else's changes, still work well. You can go back and continue working on your topic, but before doing so, tell git that you are done, and the easiest way to do so is to make a throw-away commit.
$ git commit -a -m 'test resolution'
Recorded resolution for 'hello.c'.
[detached HEAD 2fe010b] test resolution
Notice it says "Recorded resolution"? Now let's go back and keep working on the upcase-first topic.
$ git checkout upcase-first
Previous HEAD position was 2fe010b... test resolution
Switched to branch 'upcase-first'
$ git apply <<\EOF && git commit -a -m "add comment"
diff --git a/hello.c b/hello.c
index 9970faa..53abee2 100644
--- a/hello.c
+++ b/hello.c
@@ -20,7 +20,7 @@ void greetings(const char *message)
 }

 /*
- * Main program
+ * Main program - give greetings with the first word upcased.
  */
 int main(int ac, char **av)
 {
EOF
$ cc -o hello ./hello.c && ./hello "hello world"
HELLO world
Your message is 11 bytes long
Now let's try the "test merge with const-fix" again.
$ git checkout HEAD^0
$ git merge const-fix
Auto-merging hello.c
CONFLICT (content): Merge conflict in hello.c
Resolved 'hello.c' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.
This again results in a merge conflict, but notice that the message says "Resolved 'hello.c' using previous resolution." If you look at hello.c, you actually do not see any conflict markers. Instead, the hello() function is already updated with the change you made earlier, even though you are not merging exactly the versions as you tried earlier. If you are curious, here is how to check what conflict you got:
$ git checkout --conflict=merge hello.c
$ cat hello.c
Try it. You will notice that:
  • The branches produced the conflict exactly the same way. The hello() function has different sets of arguments.
  • The body of the hello() function did not have any conflict; it merged cleanly at the textual level, but it is wrong as the merge result.
Running "git rerere" explicitly at this point will again resolve the conflict for you.
$ git rerere
Resolved 'hello.c' using previous resolution.
Things to notice:
  • Rerere remembers how you chose to resolve the conflicted regions;
  • Rerere also remembers how you touched up outside the conflicted regions to adjust to semantic changes;
  • Rerere can reuse previous resolution even though you were merging two branches with different contents than the one you resolved earlier.
Even people who have been using rerere for a long time often fail to notice the last point.




Tags:

(2 comments | Leave a comment)

November 5th, 2009


02:27 pm - 「入門Git」には満足
大手の通販店では品切れとか、「コレクター本」と称してサイン本でさえない本(著者サイン本は世の中に2冊だけ存在して、そのうちの1冊は Linus君が持っている)を定価より高価く売っているのをみるが、きちんと探すと、まちの本屋さんには、まだ在庫をお持ちで店頭で買えたり、通販部からすぐ出荷となるところがいくつもある。
ここのところ、ブログ上で書評を書いて下さっている方をちらほらと見かける。kernel.orgに置いたこの本の正誤表ページからもリンクを付けて紹介しているが、ボクは全般的に満足している。

おおかたが好意的な書評だから満足している、というわけではない。もちろん、けなされるよりはホメられる方がうれしいのは間違いないけれど、それよりも、これらの書評からは、評してくれた方々が、ボクがこの本で伝えたかったメッセージをきちんと読み取ってくれたことがうかがえるからだ。

以前ここにも書いたことがあるが、この本には「入門」という名前が付いていても、何も考えないで切りはりして使うレシピ本、という「Git for dummies」的な「入門」にはしたくなかった。

マニュアルやレシピ本を読んで使いこなせるための下地となる、分散型版管理システムの世界観(というと大げさだが)を読者が頭の中に確立できるようにすることが、入門本の一番重要な役割だと思うので、他の入門本とは重点を置いて書いた内容も味付けも随分ちがう。そういう意味で、読者によって好ききらいがはっきり分かれる本になるだろうことは初めからわかっていた。

もちろん、書いた側はそういう視点からの入門書が必要で有用だと思ってくれる読者が多数いるだろうことを期待して書いたわけだが、こういうアプローチに不安がなかったと言えば嘘になる。実際に本が世に出てみて、こうしてブログ上の書評の形で「まずはモノの考え方、git的な世界観」というボクの入門書に対するスタンスが評価されていることを確認できたことに満足している。




Tags:

(Leave a comment)

October 28th, 2009


10:21 am - 入門Gitは増刷の運び
あちこちで品切れの字を見ますが、増刷することになったようです。

サポートページ、というほどではないですが、http://www.kernel.org/~junio/ に、現在の正誤表を置いてあります。




Tags:

(Leave a comment)

October 9th, 2009


10:33 pm - Rockbox came to my Fuze
After about two years, the battery of my Clip died.  But I have a Fuze that was a present from a fellow git user Felix.  While browsing the Sandisk Forum site, I noticed that Rockbox has arrived.  Yes, it is still only for v1 hardware, and it is labelled as unstable, but it is at least not unusable.



Here are some possible reasons why you might consider trying Rockbox on your Fuze, if you have v1 hardware (i.e. your original firmware version is 1.X.Y, not 2.X.Y):
  • Customizable UI.  I do not care too much about UI, but at least I wanted to have a customizable wallpaper.  The original firmware offers "wallpaper" option, but it only changes the background color; it does not allow you to use your own image.
  • Changing playback speed without affecting pitch.  I mostly use my MP3 players to listen to Podcasts while driving to work, and it is often nice to play at a faster speed.  The original firmware allows you to play faster, but at higher pitch (some people call this "chipmunk mode"), but Rockbox has an option to play faster at the original pitch, which is much nicer.
  • The emulator works in Wine.  This is primarily a praise for Wine guys, but I was quite impressed that the Fuze emulator to try custom UI out worked out of the box in Wine (it even plays sound ;-).
Here are some bad things.  I am hoping things will improve:
  • No USB connection support yet.  Plugging the unit over USB will charge and let the original firmware act as a removable disk as usual (i.e. MSC mode), so it is not a showstopper.
  • No recording support yet.  Every day, after I park my car, I record where I parked in the parking structure (which is 6 stories tall and often packed, as it is not just the office parking but primarily is used as airport parking); I easily can forget where I parked in the morning without this.
  • No embedded album art.  You can extract album art as a separate image file and use it, but then it would become cumbersome to remove it.
  • Booting back into the original firmware seems to reset the clock.  This is quite annoying, as connecting with USB to either upload music and podcast tracks, or to charge the unit, will boot into the original firmware.
Also, there are a few glitches, even though they are not showstoppers:
  • You should be able to set the default playback speed per type of contents (e.g. play Audiobooks and Podcasts at 125% speed).  I want to listen to podcasts at faster speed, but want to play music at the original speed.
  • The UI to set clock seems to be buggy. When set to October 7th from the UI, it starts ticking on October 8th for whatever reason.
  • You can set the display to turn off after inactivity.  When the display goes dark this way, you can set the first UI action to only activate the display without doing anything else.  This is very nice (and is the only mode supported by the original firmware) especially because in Rockbox UI, there is no single safe button to press that wouldn't do anything, when you cannot tell what mode the unit is in.  One glitch of this is that when the unit is on hold (i.e. with Fuze, the power switch can be slided in the opposite direction to put the unit on hold), no action other than the unhold action itself wakes up the display.  This is somewhat irritating.
  • The source is in Subversion, although git-svn seems to cope with the trunk version just fine.
  • The emulator in Wine does not seem to get long-click (in Windows it reacts).
Spent some time practicing to build a custom UI, which was fun (but was not productive use of time).



 



(Leave a comment)

October 4th, 2009


03:26 pm - Back from Japan
Shawn, Peff and Dscho have been doing wonderful job reviewing the patches sent to the git mailing list while I was away, and to me it seems the week was more productive without me ;-)  Picked up the histories they prepared for me to pull from, and pushed the result out.

Linus says he received an autographed copy of my book I sent him from Japan.

 
Tags:

(Leave a comment)

October 2nd, 2009


08:35 pm - 小飼さんってセレブなんだね
日本に帰っていた次いでに、技術評論社さんの会議室で小飼弾さんと対談してきた。年末頃のWeb+DB誌に出るらしい。

次に出るWeb+DB誌の書評欄用に「入門Git」が一冊編集部に届いていたのを見て、何だか欲しそうにしてたので、すぐに秀和システムに連絡して小飼さんのとこにも送ってもらったら、御自身のブログに書評を書いてくれた。「入門Git書評」とGoogleで引いたら引っかかってくる。読んでいてこちらが恥ずかしい位にホメてもらっている。

主にPerlハッカーとしてしかボクは知らなかったけれど、この人は恐ろしく影響力がある。出版社からは「小飼さんが書いてくれたとたんにamazon.co.jpでの順位もすごいことになってます」と連絡が来たので見てみると、総合で100位前後だ。確か前に見たときは1600位だか1700位くらいだった。

総合で100位前後というのがどれ位大変なことなのかピンと来なかったので、最近ボクのお兄さんが書いた(ウソだよ)「韓国の美をたどる旅」を見てみると、これが総合で300位前後だ。この本は前から気になってる(これは本当)し、負けたくない(これも本当だが、勝てるわけがない。今のところは勝ってるけどね)。


 



Tags:

(Leave a comment)

September 28th, 2009


08:30 pm - 入門Gitにはコマンドとかオプションのリストがない?

最近書いた、「入門Git」の最後の方にマニュアル的なリストをつけることも考えなかったわけではありません。でも、お金払った本にマニュアルに書いてあることのコピーがえんえんと載ってたら、なんだか損した気になるでしょ?

入門書である本書のひとつの目的は、マニュアルを効果的に使えるための地固めをすることです。gitメーリングリスト等での新規ユーザの挙動を見ていると、必要な基礎概念を理解する前にいきなりマニュアルを見ようとして、それでかえって混乱していることが往々にしてあります。例えばrevertというコマンド名を思い付いてgit revertのマニュアルを見る。「いままでエディタで色々編集してきたの、みんなでたらめな変更だから捨てたい、どうしたらいいの」という状況に使えるオプションを見たくて、オプションのリストの部分にいきなり行ってもそんな記述がない。

もともとrevertはそういう用途に使うコマンドではないので、そんなオプションがないのは当たり前で、正解は他のコマンドを使うこと、これはrevertマニュアルの先頭にも書いてあるのですが、マニュアルページは基本的に字引きのようなもので、基礎がわかった上でのあちこちつまみ食い、という使い方が効率的にできることを主たる目的とした構成になっていますから、先頭の記述部分をとばしてオプションのリストだけ見て分かるひとにはそれでよいのですが、新規ユーザが全体の構成を理解せずにいきなりマニュアルから入ってもなかなか効果的な習得は望めません。

マニュアルを効果的に使えるための地固めをする、という目的の入門文書は別にチュートリアルがあるのですが、あまり使われていないようなのを残念に思っていました。日本では先月翻訳がでたSwicegoodの本を最初に、この1-2年の間にようやくまとまったGit本が出始めましたが、これらの本がそういう地固めに役立ってくれるよう希望しています。

ボクの本は、リファレンス本というよりも、リファレンスを見る前に、基本的な考え方の背景の理解に役立てられる本というのが必要だという思いから、そういう内容の方に紙数を割きました。まぁ言えば、言い方は悪いけれど、リファレンス本はマニュアルを読める人なら誰にでも訳して書けるので、わざわざボクが書くまでもないですから。

日本に来て本屋さんで売られているのを見ると、ちょっぴり嬉しいです。



Tags:

(Leave a comment)

September 18th, 2009


04:43 pm - キャッチフレーズ

日本に来た序でに某書店に置くPOPを書け、なんて言われて、キャッチフレーズを考えはじめた。でもなかなかいいのが浮かばぬ。

子供向き
初心者向き中上級者向きウラ話ぜーんぶだめ。POPコピーとしてもダメだし、本のタイトルとしてもおよそどれも売れそうにない。

裏ワザなんてあったらこっちが知りたい位だし。


 


(Leave a comment)

gitster's journal

> Recent Entries
> Archive
> Friends
> User Info
> previous 10 entries

Links
Pages at the k.org
Gifts
貢ぎ物
> previous 10 entries
> Go to Top
LiveJournal.com

Advertisement