2011/10/21

Git == SVN 愛用者的快速入門

我想,很多人是 SVN  的愛用者,若有像我一樣要換到 git 的人,可以參考本文的原始文章: Git - SVN 快速入門。底下是翻譯

如果你只是想追蹤某人的專案的話,下面這樣就夠了:
git clone url
git pull
svn checkout url
svn update

在進一步閱讀前,你應該知道的事

  • Repositories.
    在 subversion 中,每個專案有一個單一的放置源碼的地方,英文叫 Repository,在 SVN 中它用來管理源碼、管理變動記錄、或是讓你上傳源碼
    但是在 Git 卻不一樣,它是分散式的,每個專案的複製品有它自己的專案倉庫,在做與 SVN 一樣的管理時,並不需要連線,你可以在本地端做完。也就是說,在本地端的分支進展,是可以與所謂的遠端分開的不同分支。雖然名詞上一樣,但是觀念上卻完全不同。
  • URL.在 SVN 中,URL 指的就是前面的 Repository的位址或其目錄樹位址,所以你必須對 Repository 做好目錄管理,譬如,最常見的 trunk/, branches/ 及tags/ 等。
    但是在 Git 中,URL 就只是 Repository 的位址而已,也就是說,你無法透過 URL 來指定分支什麼的。換句話說,Git 的倉庫要就整個被提取而 SVN 則可以從倉庫中提取個別的物品。看似 SVN 比較合理點,但是反過來思考二個問題,當對方的倉庫沒開門時,你就提取不到東西,而且若你想開分行時,將會占用另一個倉庫。我這樣說或許有人會說,Git 只是一開始就弄一個新倉庫,還不是一樣要占用一個倉庫?其實觀念上完全不同,你在本地端不管是 Git 或 SVN 本來也都一樣要占用完全的備份,而 SVN 則在新分支時又要重新占用一個新的備份。
  • Revisions.
    SVN 在版本的控管上是用 Revision, 而且會自動遞增。這在理論上或許很方便,實務上對 Repository 有人懶得管,或是分支的管理上,Revision 都反而增加了困擾。當然可以用 Tag 來稍微解決問題,但是常用 SVN 的人就知道,這確實是個很大的困擾。尤其到後期,你根本無法知道哪個 Revision 是你要的。若混用了 Repository 及 branch, 光是要記 Revision 就是一場災難。
    而 Git 則用 HEAD 來代表當前的版本,你可以搭配 HEAD^, HEAD^^ = HEAD~2 來表示更早的版本。而且 Git 在分支的管理上更是輕便,隨時可以切換分支,這一點是 SVN 比不上的。
  • Commits.
    首先一點是,SVN 的 commit 是丟到伺服器上,就是真的『提交』。而 Git 的 commit 則是丟到本地端的 Repository,跟 SVN 的 commit 相對應的反而是 push。
    第二點,Git 把每次的提交動作,分出 author, 及 committer, 也就是作者與提交者,兩個是不同的。底下的命令可以設定成全域值:
    git config --global user.name "Your Name Comes Here"
    git config --global user.email you@yourdomain.example.com
    
  • Commands.
    Git 有一個很特殊的用法,就是一般的用法是 "git command“ 你也可以用 “git-command” 的型式,這樣在 bash 下很容易用 tab 來找出你可以用的命令.
  • Colors.
    Git 很貼心的可以將命令的輸出著色,因為有些人討厭顏色,其實我也是,所以預設是關掉的。可以試著用下面的命令來打開:
    git config --global color.diff auto
    git config --global color.status auto
    git config --global color.branch auto
    
  • Visualize.
    Git 也有圖形界面可以用,最簡單的就是 gitk

Commiting

除了前面算是緒言的部份外,首先要介紹的是,如何讓你的專案交由 Git 來追蹤,並且看看在那之後每日必做的功課。先切換工作目錄到你的專案去吧,然後.....

git init
git add .
git commit
svnadmin create repo
svn import file://repo
git init 初始化 repository, 而 git add . 則會將目錄下所有東西加到 repository 中。事實上,與其相對應的 svn 是不存在的,因為 svn 沒有本地端的 repository 的觀念,所以上面的例子是用 svnadmin  來實作的,而這通常是在伺服器端。要注意的是,git commit 之後,才算真正的進入到 repository 中,不過貌似有個比較正式的名詞叫 staged.

git diffsvn diff | less
比對不同版本的差異。這邊講的版本,其實是每次的 commit 視為一個版本,跟實務上的版本是不一樣的。
git diff rev pathsvn diff -rrev path
Git embeds special information in the diffs about adds, removals and mode changes:
git applypatch -p0
That will apply the patch while telling Git about and performing those "meta-changes".
There is a more concise representation of changes available:
git statussvn status
This will show the concise changes summary as well as list any files that you haven't either ignored or told Git about. In addition, it will also show at the top which branch you are in.
While we are at the status command, over time plenty of the "Untracked files" will get in there, denoting files not tracked by Git. Wait a moment if you want to add them, run git clean if you want to get rid of all of them, or add them to the .gitignore file if you want to keep them around untracked (works the same as the svn:ignore property in SVN).
To restore a file from the last revision:
git checkout pathsvn revert path
You can restore everything or just specified files.
So, just like in SVN, you need to tell Git when you add, move or remove any files:
git add file
git rm file
git mv file
svn add file
svn rm file
svn mv file
You can also recursively add/remove whole directories and so on; Git's cool!
So, it's about time we commit our changes. Big surprise about the command:
git commit -asvn commit
to commit all the changes or, as with Subversion, you can limit the commit only to specified files and so on. A few words on the commit message: it is customary to have a short commit summary as the first line of the message, because various tools listing commits frequently show only the first line of the message. You can specify the commit message using the -m parameter as you are used, but you can pass several -m arguments and they will create separate paragraphs in the commit message:
If you don't pass any -m parameter or pass the -e parameter, your favorite $EDITOR will get run and you can compose your commit message there, just as with Subversion. In addition, the list of files to be committed is shown.
And as a bonus, if you pass it the -v parameter it will show the whole patch being committed in the editor so that you can do a quick last-time review.
By the way, if you screwed up committing, there's not much you can do with Subversion, except using some enigmatic svnadmin subcommands. Git does it better - you can amend your latest commit (re-edit the metadata as well as update the tree) using git commit --amend, or toss your latest commit away completely using git reset HEAD^, this will not change the working tree.

Browsing

Now that we have committed some stuff, you might want to review your history:
git log
git blame file
svn log | less
svn blame file
The log command works quite similar in SVN and Git; again, git log is quite powerful, please look through its options to see some of the stuff it can do.
The blame command is more powerful as it can detect the movement of lines, even with file copies and renames. But there is a big chance that you probably want to do something different! Usually, when using annotate you are looking for the origin of some piece of code, and the so-calledpickaxe of Git is much more comfortable tool for that job (git log -Sstring shows the commits which add or remove any file data matchingstring).
You can see the contents of a file, the listing of a directory or a commit with:
git show rev:path/to/file
git show rev:path/to/directory
git show rev
svn cat url
svn list url
svn log -rrev url
svn diff -crev url

Tagging and branching

Subversion marks certain checkpoints in history through copies, the copy is usually placed in a directory named tags. Git tags are much more powerful. The Git tag can have an arbitrary description attached (the first line is special as in the commit case), some people actually store the whole release announcements in the tag descriptions. The identity of the person who tagged is stored (again following the same rules as identity of the committer). You can tag other objects than commits (but that is conceptually rather low-level operation). And the tag can be cryptographically PGP signed to verify the identity (by Git's nature of working, that signature also confirms the validity of the associated revision, its history and tree). So, let's do it:
git tag -a namesvn copy http://example.com/svn/trunk http://example.com/svn/tags/name
To list tags and to show the tag message:
git tag -l
git show tag
svn list http://example.com/svn/tags/
svn log --limit 1 http://example.com/svn/tags/tag
Like Subversion, Git can do branches (surprise surprise!). In Subversion, you basically copy your project to a subdirectory. In Git, you tell it, well, to create a branch.
git branch branch
git checkout branch
svn copy http://example.com/svn/trunk http://example.com/svn/branches/branch
svn switch http://example.com/svn/branches/branch
The first command creates a branch, the second command switches your tree to a certain branch. You can pass an extra argument togit branch to base your new branch on a different revision than the latest one.
You can list your branches conveniently using the aforementioned git-branch command without arguments the listing of branches. The current one is denoted by an "*".
git branchsvn list http://example.com/svn/branches/
To move your tree to some older revision, use:
git checkout rev
git checkout prevbranch
svn update -r rev
svn update
or you could create a temporary branch. In Git you can make commits on top of the older revision and use it as another branch.

Merging

Git supports merging between branches much better than Subversion - history of both branches is preserved over the merges and repeated merges of the same branches are supported out-of-the-box. Make sure you are on one of the to-be-merged branches and merge the other one now:
git merge branchsvn merge -r 20:HEAD http://example.com/svn/branches/branch
(assuming the branch was created in revision 20 and you are inside a working copy of trunk)
If changes were made on only one of the branches since the last merge, they are simply replayed on your other branch (so-called fast-forward merge). If changes were made on both branches, they are merged intelligently (so-called three-way merge): if any changes conflicted, git mergewill report them and let you resolve them, updating the rest of the tree already to the result state; you can git commit when you resolve the conflicts. If no changes conflicted, a commit is made automatically with a convenient log message (or you can dogit merge --no-commit branch to review the merge result and then do the commit yourself).
Aside from merging, sometimes you want to just pick one commit from a different branch. To apply the changes in revision rev and commit them to the current branch use:
git cherry-pick revsvn merge -c rev url

Going Remote

So far, we have neglected that Git is a distributed version control system. It is time for us to set the record straight - let's grab some stuff from remote sites.
If you are working on someone else's project, you usually want to clone its repository instead of starting your own. We've already mentioned that at the top of this document:
git clone urlsvn checkout url
Now you have the default branch (normally master), but in addition you got all the remote branches and tags. In clone's default setup, the default local branch tracks the origin remote, which represents the default branch in the remote repository.
Remote branch, you ask? Well, so far we have worked only with local branches. Remote branches are a mirror image of branches in remote repositories and you don't ever switch to them directly or write to them. Let me repeat - you never mess with remote branches. If you want to switch to a remote branch, you need to create a corresponding local branch which will "track" the remote branch:
git checkout -b branchorigin/branchsvn switch url
You can add more remote branches to a cloned repository, as well as just an initialized one, using git remote add remote url. The command git remote lists all the remotes repositories and git remote show remote shows the branches in a remote repository.
Now, how do you get any new changes from a remote repository? You fetch them: git fetch. At this point they are in your repository and you can examine them using git log origin (git log HEAD..origin to see just the changes you don't have in your branch), diff them, and obviously, merge them - just do git merge origin. Note that if you don't specify a branch to fetch, it will conveniently default to the tracking remote.
Since you frequently just fetch + merge the tracking remote branch, there is a command to automate that:
git pullsvn update

Sharing the Work

Your local repository can be used by others to pull changes, but normally you would have a private repository and a public repository. The public repository is where everybody pulls and you... do the opposite? Push your changes? Yes! We do git push remote which will push all the local branches with a corresponding remote branch - note that this works generally only over SSH (or HTTP but with special webserver setup). It is highly recommended to setup a SSH key and an SSH agent mechanism so that you don't have to type in a password all the time.
One important thing is that you should push only to remote branches that are not currently checked out on the other side (for the same reasons you never switch to a remote branch locally)! Otherwise the working copy at the remote branch will get out of date and confusion will ensue. The best way to avoid that is to push only to remote repositories with no working copy at all - so called bare repositories which are commonly used for public access or developers' meeting point - just for exchange of history where a checked out copy would be a waste of space anyway. You can create such a repository. See Setting up a public repository for details.
Git can work with the same workflow as Subversion, with a group of developers using a single repository for exchange of their work. The only change is that their changes aren't submitted automatically but they have to push (however, you can setup a post-commit hook that will push for you every time you commit; that loses the flexibility to fix up a screwed commit, though). The developers must have either an entry in htaccess (for HTTP DAV) or a UNIX account (for SSH). You can restrict their shell account only to Git pushing/fetching by using the git-shell login shell.
You can also exchange patches by mail. Git has very good support for patches incoming by mail. You can apply them by feeding mailboxes with patch mails to git am. If you want to send patches use git format-patch and possibly git send-email. To maintain a set of patches it is best to use the StGIT tool (see the StGIT Crash Course).
If you have any questions or problems which are not obvious from the documentation, please contact us at the Git mailing list atgit@vger.kernel.org. We hope you enjoy using Git!

Git refs 參考文獻


Git Cheat Sheet

如您所見,轉貼一下有用的資訊而已

2011/10/20

git 基本概念 basic intro

主要是從這一篇簡報來寫的,因為我的網路慢,開簡報很麻煩,因此寫下來,直接看原簡報當然會清楚多了,我的這篇只是給自己看的。


Git 的目標或是特色有幾項:快、簡單(呃....)、非線性、分散式、可處理超大專案。

Git 對分支(Branch)的操作極快速,對複雜的合作開發模式也可以勝任。不過因為複雜開發模式也真的太複雜了,建議看看Git Flow 開發流程或是英文版的 A successful Git branching model

基本操作很簡單:
git init  /  git init --bare
git clone / git clone --bare
git add / git rm
git commit / git commit --amend
git status / git log / git diff
git checkout / git checkout -- file
// git clone 才像 svn checkout, 而 git checkout 是用來切換分支的命令
git pull origin == git fetch origin + git merge origin/master
































關於 rebase 與 merge, 其實我並沒有搞懂,但是這不妨礙我把它抄下來

指令 git pull --rebase 

rebase 的意思是
1. 把本地 repo. 從上次 pull 之後的變更暫存起來
2. 回覆到上次 pull 時的情況
3. 套用遠端的變更
4. 最後再套用剛暫存下來的本地變更。
所以如果將已經 push 出去的變動再來一次 rebase 的話,到底會怎樣?

git for windows 3 - 分散式之 client 端篇

在講下去之前,請見這篇git教學


ssh 需要公開金鑰與私鑰,公鑰是給別人的,放在 git server,而私鑰則是放自己家目錄中的 .ssh/ 目錄下



1. 產生 ssh key, 可以用下面的命令產生,此時你的 windows cmd 視窗應該可以執行 ssh-keygen 才對

這一步驟若是你有在別台機器上已經產生的 ssh key 的話,也是可以拿來用的,當然是要「信任的」。因此,若是你有別台安裝 Ubuntu 的電腦的話,也可以考慮使用該台電腦的 key, 這樣可以做到免密碼登入。

2. 把公開金鑰給 server
3. git server 的設定,請見這篇『簡易 git server 架設 或是這篇


git for windows 2 - 分散式之 server 端篇


在說明之前,先提一篇必看的文章,8 ways to share your git repository

先講一下基本觀念,因為要跟別人共用,所以要透過網路傳輸的協定,基本上有 git, ssh, http, https 等.....各有優缺,這邊以上面的教學來操作 ssh 的部份。

git 本身並沒有分 client 或是 server, 這邊講的 server 其實是指「連線功能」本身的,就好像要用 http protocol 的話,總要安裝 web server, ssh protocol 的話,就要安裝 ssh server. 而要用 git 的話,其實它也是用 ssh, 所以一樣要安裝 ssh server.

在 ubuntu 下的話,可以透過安裝 openssh-server 即可達標,windows 下我則建議使用 cygwin,當然其他套件也是非常多選擇啦,不過我並沒有打算在 windows 裝,所以這邊講的也都是指 ubuntu 下的。

$ sudo apt-get install openssh-server

接下來(至少)有兩種使用情境,只是要先提一下另外兩篇文章,只是因為與本文有點關係而已,跟我這邊要講的情境關係是有但是不大:請參考 Git Book, Setting a public repository 及 Git Book, Setting a private repository

在講server 端的操作的使用情境時,可以先了解一個 git 特有的概念,叫『bare git repository』,不管在 clone 或是 init 時,都可以加上 --bare。按照英文的意思,是叫「裸露」,也就是說並沒有內容,也就是沒有 project files, 只有 git repository 資料結構而已。請參考 push to only bare repository 或是 git bare v.s. none bare repository


server 端的操作的第一種情境是建立一個空的 repository ,此時可以用 git init --bare。這個情境通常是用來建立一個 public repository給自己用,也就是,如果你想在自己的機器上建立一個 git public repository 來給別人抓(pull)源碼的情況。

我知道這樣講也很模糊,這也是 git 讓人覺得複雜的地方,先用圖來說明, 在 git book, distributed workflows裡面有個圖:


                        you push
  your personal repo -----------------> your public repo
    ^                                     |
    |                                     |
    | you pull                            | they pull
    |                                     |
    |                                     |
    |               they push             V
  their public repo <------------------- their repo
左上角,也就是我之前貼的那篇 git for windows 單機版
而右上角,也就是第一個情境 git init --bare

同樣的,所謂的第二個情境,則是從遠端抓下來。由上圖可以很容易發現,就是把別人的 public repo 抓下來而已。用的是 git pull 或 git clone --bare 重新建立一個自己的 public repo

2011/10/19

GIT for windows - 單機版

一直以來都在用 svn 來做檔案管理,因為算是都自己在用,就算是團隊也是自己下面的,它方便之處在於使用與管理可以很簡單,不便之處則在於需要管理時反而不便。這邊講的管理,是指專案的控管上。還有一個 Git 的優點,就是它是分散式的,在團隊開發的情境中,若有人的檔案系統毀了也沒差,隨時可以回覆。

 首先介紹一些不錯的教學文章:
  寫給大家的 Git 教學 要想入門時有個清楚觀念的話,一定要看這一篇簡報
  Git community book  這篇是一定要看的書,不止是基礎那麼簡單而已,算是講的很清楚參考書+工具書
  Git for Windows with Central Server

  Git教學:初學者使用心得分享(Windows) 
  上一篇比較簡單,相對講的也只有一部份,完整請見下面三個:
  Git for Windows Part I
  Git for Windows Part II
  Git for Windows Part III

----PART I: Local User----
其實大部份版本控制的基本觀念都差不多,當然有其差異處,這邊就不比較了,直接講安裝。照著上面第一篇教學安裝就夠了,不過這邊提醒一下:
1. 我是先安裝 tortoisegit, 它只是 git 的 GUI 而已,別人的教學上寫的是反過來先安裝 msysgit, 想來兩種順序都可以。
2. 要搭配真正的 git 命令 for windows, 我是照文件寫的,安裝 msysgit,我相信有在用 cygwin 的人也可以用它
3. 安裝 msysgit 時,它會問 ssl client -- plink.exe, 有幾個選擇,一個是 putty, 一個是因為我自己也有安裝 tortoiseSVN, 或是前面的 tortoiseGIT 也有附這個東西

底下直接把我安裝時的照片貼上來

1. 安裝 msysgit, 注意我只勾選 Git Bash Here, 因為我的 GUI 用的是 tortoiseGIT
2. 環境變數,因為我沒在用 windows 內建的 find, sort 等,因此選擇用 msysgit 附的

3. 選擇 plink.exe,這是給要跟遠端的 git server 連線用的。若是個人自己要在自己的電腦上玩,這個就不是很重要。因為我有安裝 putty, 也可以選 tortoiseSVN 或是 tortoiseGIT 附的

4. 選擇 ending, 也就是 Windows 的換行與 Unix 的換行是不一樣的,若不瞭解的話,就選預設的就好了,否則出問題時很難解決。

5. 安裝好了之後,就開始新的專案,很簡單,第一個步驟是開啟一個「倉庫」放源碼。這邊有兩個方法,一個是產生一個 bare, 也就是一個 .git 檔,另一個是把某個目錄變成 git 的 repository, 我用的後面這個方法


6. 加檔案進去。若已經有很多源碼檔,可以用「加進整個目錄」或是只加一個檔案,底下的圖示範的是只加一個檔。就是在該檔按滑鼠右鍵,然後.....

  再選擇 TortoiseGit --> Add 即可加入。值得注意的是,此時並沒「push」到 server 的版本控制中,還需要 commit....後面會說


7. commit 也一樣簡單。不過也有兩種方式,或是說,有一種是不加『簽名」,一種加簽名,其實結果都差不多。在第一次使用 commit 時,TortoiseGit 會要求你設定 name & email, 就自己設一個吧。這一點也是跟 SVN 不一樣的地方,git 的使用者其實是很鬆散的,但是也是要自己負起責任的方式。



8. 取出。這部份有兩種,一個是在之前建立 repository 的目錄直接操作即可。另一個是你也可以到另一個目錄下取出之後來玩,這種需求比較少,可以自己玩玩看 git clone。

值得一提的是,TortoiseGIT 也支援從 svn server 取出 source code, 畫面如下



2011/10/12

plop boot manager

從光碟或是隨身碟開機,有些機型不見得能支援,有的有支援卻也不見得適用自己手邊的裝置。這邊介紹一個 boot manager 叫 plop, 功能強大,可以輕鬆解決這個問題,底下直接列其特性:

  • USB boot without BIOS support (UHCI, OHCI and EHCI)
  • CD/DVD boot without BIOS support (IDE)
  • PCMCIA CardBus support to enable boot from USB PC-Cards
  • Floppy boot
  • Different profiles for operating systems
  • Define up to 16 partitions
  • No extra partition for the boot manager
  • Hidden boot, maybe you have a rescue system installed and the user should not see that there is another system installed
  • Boot countdown
  • Hide partitions
  • Password protection for the computer and the boot manager setup
  • Backup of partition table data
  • Textmode user interface 80x50
  • Graphic user interface 640x480, 800x600, 1024x786, 1280x1024
  • MBR partition table edit
  • Start of the boot manager from harddisk, floppy, USB, CD, DVD
  • Starting from Windows boot menu
  • Starting from LILO, GRUB, Syslinux, Isolinux, Pxelinux (network)
  • It can be used as PCI option ROM
  • Access the whole USB hard disk (up to 2TB) even when the bios has a 128 GiB limit
  • You can run the boot manager over the network
  • Start the networkcard bootrom from the boot manager to boot from the network

2011/09/15

送信給加西亞 Send Message to Garcia

還記得我寫過這篇『態度決定一切摘要』嗎?現在回想,這篇是2004年寫的,因為當年陳水扁競選連任成功。裡面有一篇章叫「送信給加西亞」,當年我沒查到故事來源,此事我以為就從此沒下文了,誰知,今天開完會後,有個同仁竟然幫我查到了,底下錄下原文章,因為寫的比我要表達的還好:




在一切有關古巴的事情中,有一個常常從我記憶中冒出來讓我難以忘懷。
美西戰爭爆發時,美國必須立即與古巴的起義軍首領加西亞取得聯繫。加西亞在古巴廣闊的山脈裡──沒有人確切地知道他在哪裡,也沒有任何郵件或電報能夠送到他手上。而美國總統麥金菜又必須盡快地得到他的合作。
怎麼辦呢?
有人對總統說:”如果有人能夠找到加西亞的話,那麼這個人就是羅文”。於是總統把羅文找來,交給他一封寫信加西亞的信,關於那個名叫羅文的人,如何拿了信,用油紙袋包裝好,打封,放在胸口藏好;如何經過4天的船路到達古巴,再經過3個星期,徒步穿過一個危機四伏的國家,終於把那封信送給加西亞──這些細節不是我想說的。我要強調的重點是:美國總統把一封寫給加西亞的信交給羅文:而羅文接過信之後,並沒有問:”他在甚麼地方?”
像羅文這樣的人,我們應該為他塑造銅像,放在所有的大學裡,以表彰他的精神。年輕人所需要的不僅僅是從書本上學習來的知識,也不僅僅是他人的種種教誨,而是要加強一種敬業精神:忠於上級的托付,迅速地探取行動,全心全意地完成任務──”把信送給加西亞”。
加西亞將軍已經不在人世,但現在還有其他的”加西亞”。沒有人能夠經營好這樣的企業──在那裡雖然有眾多人手,但是令人驚訝的是,其中充滿了許多碌碌無為的人,這些人要麼沒有能力,要麼不情願去集中精力做好一件事。
懶懶散散﹑漠不關心﹑馬馬虎虎的做事態度,似乎已成常態;沒有人能夠成功,除非苦口心﹑威逼利誘地強迫他人幫忙。或者,除非奇跡出現,上派一名天使提供幫助,不然沒有人能把事情辦成。
不信的話,你可以做個試驗;此刻,你坐在辦公室裡──周圍有6名職員。把其中任何一名叫過來,對他說:”請幫我查一查百科全書,把克里吉奧的生平做成一篇摘錄。”
他會靜靜地說:”好的,先生。”
然後他會去執行嗎?
他敢說他絕對不會,他會用死魚般的眼睛盯著你,然後滿臉狐疑他提出一個或數個問題:他是誰呀?
哪套百科全書?
百科全書放在哪兒?
這是我的工作嗎?
為甚麼不叫喬治去做呢?
他死了嗎?
急不急?
需不需要我拿書過來,你自己查?
你為甚麼要查他?
我敢以十比一的賭注你打賭,在你回答了他提出的所有問題,解釋了怎樣去查那些資料,解釋了怎樣去查那些資料,以及你為甚麼要查的理由之後,那個職員會走開,吩咐另外一個職員去幫他查那些資料,然後,會回來告訴你,沒有這個人。當然,我可能會輸了這個賭注,但是根據平均概率法則,我不會輸。
現在,如果你夠聰明,你不會對你的助理”解釋”,克里吉奧編在甚麼,而不是甚麼類,你會滿臉笑容地說:”沒關係。”然後自己去查。
這種自主行動的無能,這種道德上的愚行,這種意志上的脆弱,這種惰性的風氣,這就是十來社會被帶到崩潰境地的根源。
如果人們都不能為了自己而自主行動,人們又怎麼可能心甘情願地為他人服務呢?
乍看起來,所有的公司都有許多可以委以任務的人選,但是事實真是如此嗎?你刊登廣告招聘一名速記員,應聘者中,十有八九不會拼也不會寫,他們甚至認為這些都無所謂。
這種人能夠寫一封給加西亞的信嗎?
“你看那個職員。”一家大工廠的主管對我說。
“我看到了,他怎麼樣?”
“他是個很好的會計,不過如果我讓他去城裡辦個小差事,他可能會完成任務,但很可能在途中走進酒吧,而到了鬧巿區,他還可能根本忘記了他的差事。”
這種人你能把給加西亞送信的任務交給他嗎?
近來,我們聽到了許多人對”那些薪水微薄而又無出頭之日的工人”以及”那些為了尋找一份舒適的工作而顏繁跳糟的人”表示同情,同時把那些僱主詛咒一通。
這是從來沒有人提到,那些年齡正在不斷變老僱主們白費了多少時間和精力去促使那些不求上進的懶蟲們勤奮起來;也沒有人提到,有些僱主持久而耐心地想感動些當他一轉身就投機取巧,敷衍了事的員工。
在每個商場和企業,都有一些常規性的整頓工作。僱主們經常送走那些不能對公司有所助益的員工,同時也接納一些新的成員。不論務怎麼忙碌,這種淘汰工作都要進行。只有當經濟形勢不景氣的時候,就業機會不多的時候,整頓才會有明顯的績效──那些不能勝任﹑沒有才能的人,都被擯棄於公司大門之外,只有最能干的人,才會被留下來。這是一個優勝劣汰的機制。僱主為了自己的利益,只會保留那些最佳的職員──那些能把信送給加西亞的人。
我認識一個有真才實學的人,他沒有獨自創業的能力,並且對他人也沒有絲毫的價值,因為他總是偏執地懷疑他僱主在壓榨他,或有壓榨他的傾向。他沒有能力指揮,也不願意被他人指揮。如果你要他去把信送給加西亞,他的回答很可能是:”你自己去吧﹗”
當然,我知道像這種道德不健全的人比那些肢體不健全的人更不值得同情;但是我們對那些用畢生精力去經營一個偉大企業的人也應該予以同情:下班的鈴聲不能夠停止他們的工作,他們因為努力維持那些漠不關心﹑偷懶被動﹑不知感激的員工的工作而白髮日增。那些員工從來不願想一想如果沒有僱主們付出的心血,他們是否將挨餓或者無家可歸?
我是否說得太嚴重了?可能如此。但是,就算整個世界變成貧民窟之時,我也要為成功者說幾句同情的話──這些人在成功機極小的情況下,承受巨大的壓力,導引眾人的力量,終於獲得了成功;但他從成功中所得到的是一片空虛,除了食物和衣服,其他甚麼也沒有。
我曾經為了衣食而他人工作,也曾經當過一些僱員的老板,我深知其中兩方面的種種甘苦。貧窮沒有甚麼優越之處,也不值得贊美;衣衫襤褸更不值得驕傲;並非所有的僱主都是探取高壓手段極力壓榨員工,並且我敢說大多數僱主都更富有美德。
我欽佩的是那些不論老板在還是不在都會努力工作的人,我也敬佩那些能夠把信交給加西亞的人,迅速地接受任務,不會提出任何愚蠢的問題,更不會隨手把信扔到水坑里,而是全力以地把信送到。這些人永遠不會被解僱,也永遠不會為加薪而罷工。

======= 分割線 ============
後來我又查到了一篇,也許是正牌的原文翻譯,不過這故事就如同我的那位同事說的,也許是美國家戶喻曉的故事吧。



2011/09/07

隨身碟多重開機又一章 Advanced for multi-OS on usb disk

寫過一篇 grub on usb disk, 再加上上一篇的從隨身碟開機, 後面這篇其實我沒表達的很完整,主要要講的是如何從隨身碟放進 liveCD 的 iso 檔,然後做多重開機。而前一篇則示範如何將 usb disk 放進 grub.....不過以現在的 Ubuntu 來說,你可以直接從 LiveDVD 安裝到 usb disk 也可以做到。

不過,grub on usb disk 這篇文章還示範了一個很有用的使用情境,你可以用 dd 來產生一個 usb disk image file, 然後再搭配從隨身碟開機,這樣可以得到另一個好處:

不必隨身碟,也可以用 VirtualBox 來虛擬隨身碟的多重開機,然後必要的時候,把它 dd 到隨身碟裡,你的隨身碟就有最新的多重開機環境。而且在製作多重開機隨身碟時,若使用這個方法,因為 image file 是在硬碟中,所以速度上會快很多。

底下以 16G 為例


1. $ dd if=/dev/zero of=16G.dd bs=262144 count=1 seek=61696
2. $ sudo losetup /dev/loop0 16G.dd
3. fdisk .... mkfs.....,用法上就跟硬碟/隨身碟一樣:
    $ sudo fdisk /dev/loop0
4. $ sudo VBoxManage internalcommands createrawvmdk -filename ~/.VirtualBox/usb-16G.vmdk -rawdisk /dev/loop0
......

快速產生特定大小的檔案 using dd fast create specified size file

dd 可以做很多事,這一點我想不必多說,它不止可以「複製」硬碟/隨身碟,還可以做轉換,網路上有人把它稱為瑞士刀。如果只會 dd if=/dev/zero of=my-dd-file bs=1M count=10 這樣的語法的話,那就遜掉了。以現代做嵌入式應用來說,dd 是個不可不學的指令,若你想要 remaster 自己的系統,似乎也離不開 dd.

這邊要提醒一個用法,可以「一瞬間」就產生特定大小。像我要產生一個 16G 大小的 image file,  以我手邊的隨身碟來看,原始大小是 16173498368, 我用的命令是 dd if=/dev/zero of=16G.dd bs=262144 count=1 seek=61696


其中 bs 是 block size, 這個大家都知道,因為一般的隨身碟或硬碟的 block size 都是 512, dd 的預設也是這麼大。上述 bs 也可以設計成 512, 這個是一次讀寫的量,因為記憶體都夠大,寫大一點照理會加速讀寫,所以我就設計成 512*512=262144,而 seek=61696 是計算來的,它等於 16173498368/512/512

這個命令的執行速度是
$ dd if=/dev/zero of=16G-2.dd bs=262144 count=1 seek=61696
1+0 records in
1+0 records out
262144 bytes (262 kB) copied, 0.00109749 s, 239 MB/s

2011/09/06

從隨身碟開機 raw disk image at virtualbox

這篇文章要寫的,主要是我把 Linux 安裝在我的隨身碟中,裝了幾個不同的系統,其中主要的就是 ubuntu, 加上幾個用 iso 檔的系統,例如 GeeXboX 2.0, TinyCore 等等,這當然能把作業系統隨身帶著走,不過有時找不到機器,那就可以考慮用 Virtualbox 來開機同樣的文章還可以參考這篇

先貼一下上面關於 GeeXboX 2.0, TinyCore 的 grub2 設定檔,這邊要提醒的是,Grub2 與 Grub1 的設定檔差很大。這個設定檔,按照 Ubuntu 的設定,只需要放在 /boot/grub/custom.cfg 即可「自動」生效。

menuentry "GeeXboX 2.0 alpha2" --class os {
  set isofile="/livecd/geexbox-2.0-alpha2-en.i386.eglibc.iso"
  loopback loop $isofile                          # 這個 loopback 設定,在硬碟中是 (hd1,msdos1)$isofile
                                                           # 請參考自己的環境適當修改
  linux (loop)/GEEXBOX/boot/vmlinuz root=/dev/ram0 rw rdinit=linuxrc boot=cdrom lang=en vga=788 video=vesafb:ywrap,mtrr hdtv quiet loglevel=3
  initrd (loop)/GEEXBOX/boot/initrd.gz
}
menuentry "Tinycore" --class os {
  set isofile="/livecd/tinycore.iso"
  loopback loop $isofile  # 同上
  linux (loop)/boot/bzImage
  initrd (loop)/boot/tinycore.gz
}
上面只是範例而已,要自己測試修改,其中 isofile 的目徑請務必自己修改。

上面 Virtualbox 來開機的教學是用 Windows(感謝微軟),相當簡單,若是想在 Linux Host 裡面用的話,反而要注意一件事,就是權限問題,若用 root 跑 virtualbox  當然不會有問題,若要用一般帳號的話,則要記得把隨身碟的 device 權限改成普通帳號可以存取的。至於命令很類似,參考文章改一下即可。

當然在 Linux 下命令與 windows 不太一樣,把兩者並列供大家參考:



-- Windows --
VBoxManage internalcommands createrawvmdk -filename "%USERPROFILE%"\.VirtualBox\usb.vmdk -rawdisk \\.\PhysicalDrive#
上面的 PhysicalDrive# 是透過 diskmgmt.msc 這個命令來查找的,主要就是隨身碟的「磁碟機編號」


-- Linux -- 
VBoxManage internalcommands createrawvmdk -filename ~/.VirtualBox/usb.vmdk -rawdisk /dev/sdb
上面的 /dev/sdb 相信大家都會,可以用 fdisk -l 命令來查隨身碟的編號

2011/08/15

套件管理 比較 package management

有一份文件叫『套件管理工具比較』,閱讀原文件比較易讀,作為備份,我轉載如下,以後有空也許也會分享心得:

比較一下現在市面上幾種套件管理工具
(debian - apt , gentoo - emerge , freebsd - prots)

其它參考: APT 用法、emerge 用法

深入內容中有 emerge, apt, ports 的語法比較(也可以說是語法教學吧)


以下轉載自: Gentoo/Debian/FreeBSD套件管理memo

以關鍵字搜尋軟體
Gentoo: emerge -s
輸出會有這個 pkg 的簡介 , 關鍵字都可以用正規表示式
Debian: apt-cache search
FreeBSD: make search name= 或 ports_glob -- need portupgrade

安裝軟體
Gentoo: emerge
Debian: apt-get install
FreeBSD: portinstall

移除軟體
Gentoo: emerge --unmerge 或 emerge -C
Debian: apt-get remove
FreeBSD: pkg_deinstall

升級單一軟體
Gentoo: emerge -u
Debian: apt-get install
FreeBSD: portupgrade

察看升級全系統會動到哪些套件:
Gentoo: emerge -puDv world
Debian: apt-get -u dist-upgrade
FreeBSD: portupgrade -arnv

升級全系統
Gentoo: emerge -u --deep world
Debian: apt-get dist-upgrade
FreeBSD: make world ; portupgrade -ar

查詢系統裝了哪些軟體
Gentoo: emerge -pe world 或 qpkg -I -v
Debian: dpkg --get-selections
FreeBSD: pkg_info

查詢某檔案屬於哪個軟體
Gentoo: qpkg -f
(need gentoolkit)
Debian: dpkg -S
FreeBSD: pkg_info -W

查詢某個套件安裝了什麼檔案
Gentoo: qpkg -l
Debian: dpkg -L
FreeBSD: pkg_info -L

查詢有沒有裝這個關鍵字的套件
Gentoo: qpkg -I | grep (qpkg再加上 -v 看版本號碼)
Debian: dpkg -qa | grep
FreeBSD: pkg_info | grep

找哪些 pkg "簡介" 裡含有你要找的關鍵字:
Gentoo: emerge -S <關鍵字>
輸出方式和前者一樣, 關鍵字都可以用正規表示式
Debian: apt-cache search
FreeBSD: make search key=

不管相依性強制移除某套件
Gentoo: emerge -C
Debian:
FreeBSD: pkg_delete -f

全系統重新編譯
Gentoo: emerge -e world
Debian: ?
FreeBSD: make world ; portupgrade -arf

========
補充:
qpkg -f
ps: emerge gentoolkit first.

--
gentoo 裡:
查詢某個套件安裝了什麼檔案:
qpkg -l
查詢有沒有裝這個關鍵字的套件:
qpkg -I (再加上 -v 看版本號碼)
不管相依性強制移除某套件:
emerge -C

--
仔細看一下 man emerge
要找一個 pkg 的 "名稱":
emerge -s <關鍵字>
輸出會有這個 pkg 的簡介

找哪些 pkg "簡介" 裡含有你要找的關鍵字:
emerge -S <關鍵字>
輸出方式和前者一樣

更厲害的是上面的關鍵字都可以用正規表示式!!

--
emerge -f (fetchonly)
好處在於當你emerge 兩個package以上的時候,可以先把source給抓回來,不過需要再emerge

emerge -p (pretend)
看要裝些什麼東西,

小弟認為這兩個指令對撥接使用者很重要,尤其 -f .一來可以大略知道下載時間,二來可以隨時中斷,然後在續傳(感謝偉大的wget)
--
升級單一軟體
Gentoo: emerge -u
Debian: apt-get install
FreeBSD: portupgrade

查詢某個套件安裝了什麼檔案
Gentoo: qpkg -l
credit to paar@gentoo.org.tw
Debian: dpkg -L
FreeBSD: pkg_info -L

全系統重新編譯
Gentoo: emerge -e world
Debian: apt-get update;apt-get upgrade
FreeBSD: make world ; portupgrade -arf

2011/08/11

Google Nexus S

先前把我的手機換成 Nexus S,......關於網路的使用,我一直是使用 usb....結果今天試著用 Wifi 分享,哪知....Wifi 的速度幾乎是 usb 的10倍!!!!而且不太會停頓........ 雖然說,用 usb 要多轉兩次,這也差太多了吧

2011/08/05

svn tag/branch

剛剛寫了一篇 svn on ubuntu,現在來談談對 release 的版本控制要怎麼進行。

版本控制有很多事要做,release 算是重要的一環。最簡單的就是自己在 commit 時加到註解中,然後日後自行讀 log 來判斷 release version

上面這種做法好處就是簡單,但是真正要做版本控制就有點複雜,因為必須從一堆 log 去找出來,當時間過去很久之後,這並不是一件簡單的事。因此比較正規的作法是用 svn copy 來完成。不過為了這個功能,一般書上會講一件事,就是目錄的擺放。我舉前面提到的 /home/svn/wade 來說....

首先,假如是用 log 來決定 release version 的話,大可以把所有 source code 都直接擺放 /home/svn/wade 下即可。但是若要用 svn copy 來做版本控制的話,依建議是.....
假設已經 checkout wade project 了,那麼在該工作目錄下執行底下兩個命令:
svn mkdir trunk
svn mkdir tags

然後,把所有 source code 都擺放在 trunk 目錄下,也就是真正的工作目錄是 /home/svn/wade/trunk
當你要 release source code 時,再用下面的命令:
svn copy http://localhost/svn/wade/trunk http://localhost/svn/wade/tags/release-1.0 -m "first release"

簡單講,svn 的 tag/branch 就是用 copy 來實現,而且是把工作目錄複製一份新的......當做了上面的命令之後,可以再接著用下面的命令來 release source code

svn export http://localhost/svn/wade/tags/release-1.0

svn on ubuntu

之前寫過幾篇跟 SVN 有關的文章,例如 svn server on win-xp ,或是 SVN 版本控管, 不過都是在 winXP 或是 Win7 上的,現在來寫一下怎樣在 Ubuntu 上安裝 svn。最後那篇值得沒用過的人參考一下。

首先,這樣的文章很容易找,例如 Ubuntu Help。不過我試了試會出點小問題,底下是我自己的小心得

一、照 Ubuntu Help 教的,先安裝套件:
sudo apt-get install subversion libapache2-svn
二、先決定自己的 repository 的佈置邏輯,以我為例,我是打算放在 /home/svn/PROJ 下,每個 PROJ 有一個 Repository, 也有得人懶得管理那麼複雜,所有 Projects 都放同一個 repository....決定好之後進行第三步,但是這邊舉例的兩種邏輯其實管理起來差異很大,我再補充補充:
1. 前一種在設定上比較麻煩,但是每個 Project 的 revision number(就是俗稱的版本號碼)是分開各自計算,好處就是可以對每個 Project 做版本控管,也可以做存取權限的控管,也就是可以分別控制誰可以存取
2. 後一種在設定上非常簡單,設過一次就不必再管理,但是將來的 Project 其實是在同一個repository 之下,無法分開控管
三、不管哪一種方式,總要先建立我要放 repository 的 home directory, 因此命令是:
$ sudo mkdir /home/svn
四、我弄了一個 shell script 叫 new-repository.sh....
#!/bin/bash
help()
{
  echo Usage: $0 PROJECT_NAME
  exit 0
}

[ "x$1" = "x" ] && help
SVN_HOME=/home/svn
cd $SVN_HOME
mkdir -p $1
svnadmin create $1
chown -R www-data:subversion $1
cd /home/svn/$1/conf
rm -f passwd svnserve.conf
ln -s $SVN_HOME/.conf/passwd .
ln -s $SVN_HOME/.conf/svnserve.conf .
五、要新弄一個 project 的話,只要簡單的下命令,不過請暫緩這個步驟,請先看第六步再回頭執行這一步:
$ sudo new-repository.sh MY-NEW-PROJECT
譬如
$ sudo new-repository.sh wade
六、從上面的 script 可以看到,事實上我也是讓每個 Project 用相同的存取設定,若您真的要分別管理的話,只要讓該 repository 裡面的 conf/passwd 或是 conf/svnserve.conf 不同即可, 這兩個檔的範例如下:
[users]
wade = iamwade
[general]
anon-access = read
auth-access = write
password-db = passwd
七、當您執行過第五步之後,就會產生一個適當的 repository, 那麼接下來就是要啟動 svn server....但是,竟然沒有 ubuntu 下的 /etc/init.d/svn 或是 /etc/init.d/svnserve....先看看怎麼啟動它:
$ sudo svnserve -d -r /home

$ sudo svnserve -d -r /home/svn
以上兩個方法,將來在存取時的路徑會不一樣,我是採用前一個
八、為了方便啟動/停止此項服務,我自己弄了個 /etc/init.d/svn, 內容如下,存好之後當然要讓它可執行($ sudo chmod +x /etc/init.d/svn):
#! /bin/sh
set -e

test -x /usr/bin/svnserve || exit 0
( /usr/bin/svnserve 2>&1 | grep -q exactly ) 2>/dev/null || exit 0

umask 022
SVN_OPTS="-d -r /home"

. /lib/lsb/init-functions

case "$1" in
  start)
        log_daemon_msg "Starting subversion server" "svnserve"
        if start-stop-daemon --start --quiet --oknodo --pidfile /var/run/svnserve.pid --exec /usr/bin/svnserve -- $SVN_OPTS; then
            pidof svnserve > /var/run/svnserve.pid
            log_end_msg 0
        else
            log_end_msg 1
        fi
        ;;
  stop)
        log_daemon_msg "Stopping subversion server" "svnserve"
        if start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/svnserve.pid; then
            log_end_msg 0
        else
            log_end_msg 1
        fi
        ;;

  restart)
        log_daemon_msg "Restarting subversion server" "svnserve"
        start-stop-daemon --stop --quiet --oknodo --retry 30 --pidfile /var/run/svnserve.pid
        if start-stop-daemon --start --quiet --oknodo --pidfile /var/run/svnserve.pid --exec /usr/bin/svnserve -- $SVN_OPTS; then
            log_end_msg 0
        else
            log_end_msg 1
        fi
        ;;

  status)
        status_of_proc -p /var/run/svnserve.pid /usr/bin/svnserve svnserve && exit 0 || exit $?
        ;;

  *)
        log_action_msg "Usage: /etc/init.d/svn {start|stop|restart|status}"
        exit 1
esac

exit 0
九、弄好上面的檔之後,就可以用一般的方式來管理服務,譬如 /etc/init.d/svn start, 或是 /etc/init.d/svn stop 或是 /etc/init.d/status
十、以上,您已經可以用 svn co svn://localhost/svn/wade 來取回 wade,當然此時是空的,或是您可以用 svn import /some/where/of/wade svn://localhost/svn/wade。要 remote 使用只要把 localhost 取代成 IP 即可。順便講一下前面第七步講啟動時的兩個方法,我是用前者啟動,若要改成後者,則存取路徑範例是 svn co svn://localhost/wade 。不知道有沒有比較出不同?
十一、若是要用 http:// 也行,我舉我的設定如下,請修改 /etc/apache2/sites-available/default 或是 /etc/apache2/mods-available/dav_svn.conf,後者就整個內容相同即可,前者則要放在<VirtualHost>與</VirtualHost>之間,內容如下:
<Location /svn>
  DAV svn
  SVNParentPath /home/svn
  SVNListParentPath On
  AuthType Basic
  AuthName "wade's repository"
  AuthUserFile /home/svn/.conf/htpasswd
  Require valid-user
</Location>
十二、存好上面的檔之後,只需要 /etc/init.d/apache2 restart 即可生效
十三、對 http:// 的控制,上面並沒有分開控制存取權限,請對 Location 複製去改就可以,譬如:
<Location /svn/wade>
  DAV svn
  SVNPath /home/svn/wade
  # SVNListParentPath On
  AuthType Basic
  AuthName "wade's repository"
  AuthUserFile /home/svn/.conf/htpasswd
  Require valid-user
</Location>

2011/08/01

lftpget, wget, curl?

本文沒要談標題那麼大範圍。在取得 chromium os 的 source code 時,因為公司網路的關係,在抓 dev-java/icedtea6-bin-1.6.2-r2 時,一直出現 timeout 的現象,害我一直抓不完。曾經試過 wget, lftpget 兩套來抓,也都抓不完。

後來發現 curl 有些參數還頗優秀,如下:
--retry 50 <-- 指定個 50 次,總會抓成功了吧?沒想到沒用,因為.....
--retry-max-time 0 <-- 最大總嘗試時間.....呃,0 是無限大,這樣總行了吧?還是沒用,因為....retry 間隔是隨次數而加倍遞增,所以總嘗試時間會很久,最後會超過「整個操作的最大時間」,因此可以搭配
--retry-delay 20 <-- 這個可以限制嘗試間隔時間,不過「整個操作的最大時間」也要指明一下,如下
--max-time 2000 <-- 整個操作的最大時間,我想,這樣總夠了吧....

2011/07/01

google android nexus S adb howto

之前的中華電信 CHT8000 被我不小心掉進馬桶.....這事有點小故事,懶得多說,總之,最後我送修說要收我一萬多塊換板子,我當然不換,後來又堅持了一陣子,卻因為無法打電話,在接到副總來電無法對話之後,我只好忍痛買一支 google android nexus S。這支 Nexus S 好用是非常好用,而且非常省電,可是......我無法破解,在剛買第二天晚上我就試著要 root 它,失敗後竟然連開機都不行!!!天啊,只好花三百元送修還原。

好吧,以上小故事本來也不算什麼,誰知在這段送修的一週內,我又只好用回那台 CHT8000,竟然可以通話!!!!老天啊,這不是在玩我嗎?!

現在拿回 Nexus S 後,我不敢再 root 它,但是好歹也要給我能 adb 一下吧!!什麼叫 adb? 什麼叫 root? 不知道的人就不要看這篇算了。

參考一下這篇文章有提到怎樣讓你在 Ubuntu 下可以 adb....底下摘要一下:

$ cat /etc/udev/rules.d/51-android.rules
SUBSYSTEM=="usb", SYSFS{idVendor}=="18d1", MODE="0666"
$ sudo restart udev


接下來再插進 Nexus S 到 Ubuntu, 當然,你得在設定裡面打開 debug 選項,至於這項怎麼做我也不想多說,找一下就有了

2011/06/11

CDPATH 惹的禍

你曾經困擾於『/bin/bash: line 0: cd: libcore/$ANDROID_ROOT/libcore: No such file or directory』這樣的訊息嗎?我相信很少人會遇到。不過我會希望在下 cd command 時能方便的省略絕對路徑,所以我會設定 export CDPATH=".:..:$HOME", 而這樣的設定卻會帶來上述的 error message....

2011/06/10

android database management -2

前一篇所說的,sqlite3 還可以在命令列操作,

# sqlite3 -help                                         
Usage: sqlite3 [OPTIONS] FILENAME [SQL]
FILENAME is the name of an SQLite database. A new database is created
if the file does not previously exist.
OPTIONS include:
   -help                show this message
   -init filename       read/process named file
   -echo                print commands before execution
   -[no]header          turn headers on or off
   -bail                stop after hitting an error
   -interactive         force interactive I/O
   -batch               force batch I/O
   -column              set output mode to 'column'
   -csv                 set output mode to 'csv'
   -html                set output mode to HTML
   -line                set output mode to 'line'
   -list                set output mode to 'list'
   -separator 'x'       set output field separator (|)
   -stats               print memory stats before each finalize
   -nullvalue 'text'    set text string for NULL values
   -version             show SQLite version
裡面很多選項都跟上一篇寫的 sqlite3 命令有相對應。這邊講一下怎樣在 command line 寫成 script....

# sqlite3 /data/data/com.android.providers.settings/databases/settings.db "select * from system;"

上面的 sql 語法本來也可以在 sqlite3 命令提示下操作,寫在命令列則可以自動化。
# echo "select * from system;" | sqlite3 /data/data/com.android.providers.settings/databases/settings.db

用常見的 stdin 轉向也有同樣的功效,那麼,我們也可以寫在檔案中,再用 cat YourSQL.sql | sqlite3 YOUR_DATABASE 的方式
要特別跟大家講的是,有個 -init 選項,這個是進 sqlite3 的初始化, 也就是說....
1. 把你要的 sql 存檔,譬如把 select * from system; 存在 s.sql 中,然後用
2. sqlite3 -init s.sql /data/data/com.android.providers.settings/databases/settings.db 來執行的話,最後會停在 sqlite3 的命令提示符號下,所以與下面這方式是不同的...
3. cat s.sql | sqlite3 /data/data/com.android.providers.settings/databases/settings.db

2011/06/09

android database management -1

參考資訊: Android sqlite3 簡介, sqlite3 datatype, sqlite 語法

Android 用 Sqlite3 的格式來儲存資料,甚至系統的 setting, preference 都是如此,在進一步研究 sqlite3 之前,先列一下所有目前模擬機上的 database file(android 3.1):
/data/data/com.android.providers.contacts/databases/contacts2.db
/data/data/com.android.email/databases/EmailProvider.db
/data/data/com.android.email/databases/EmailProviderBody.db
/data/data/com.android.browser/app_databases/Databases.db
/data/data/com.android.browser/app_databases/https_mail.google.com_0/0000000000000001.db
/data/data/com.android.browser/app_appcache/ApplicationCache.db
/data/data/com.android.browser/databases/autofill.db
/data/data/com.android.browser/databases/webview.db
/data/data/com.android.browser/databases/browser2.db
/data/data/com.android.browser/databases/webviewCache.db
/data/data/com.android.browser/app_geolocation/CachedGeoposition.db
/data/data/com.android.browser/app_icons/WebpageIcons.db
/data/data/com.android.providers.downloads/databases/downloads.db
/data/data/com.android.quicksearchbox/databases/qsb-log.db
/data/data/com.android.inputmethod.latin/databases/userbigram_dict.db
/data/data/com.android.inputmethod.latin/databases/auto_dict.db
/data/data/com.android.providers.media/databases/internal.db
/data/data/com.android.providers.media/databases/external-80b2b02.db
/data/data/com.android.providers.settings/databases/settings.db
/data/data/com.android.providers.userdictionary/databases/user_dict.db
/data/data/com.android.launcher/databases/launcher.db
/data/data/com.android.providers.telephony/databases/mmssms.db
/data/data/com.android.providers.telephony/databases/telephony.db
/data/data/com.google.android.gsf/databases/gservices.db
/data/data/com.google.android.gsf/databases/googlesettings.db
/data/data/com.google.android.gsf/databases/subscribedfeeds.db
/data/data/com.google.android.gsf/databases/talk.db
/data/data/com.android.deskclock/databases/alarms.db
/data/system/accounts.db

sqlite3 是一個 android device 上的內建命令,因此要使用的話就得進 device shell, 譬如 adb shell。執行 sqlite3 的畫面如下:
# which sqlite3
/system/xbin/sqlite3
# sqlite3                                                       
SQLite version 3.7.4
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite>

上面提供了幾點資訊:
1. sqlite3 放在 /system/xbin, 這是系統路徑
2. 版本是 3.7.4
3. sqlite3 的提示符號是 sqlite>
4. 每個命令要用分號 ; 來結束
5. 要取得說明請下 .help 命令

有些隱含的訊息像,命令都是以 . 開頭,這一點下過 .help 命令後就知道了,另外要離開 sqlite3 的話,可以下 .quit 命令,或是 ctrl-D

這篇先列所有命令先,也就是 .help 畫面:

sqlite> .help
.backup ?DB? FILE      Backup DB (default "main") to FILE
.bail ON|OFF           Stop after hitting an error.  Default OFF
.databases             List names and files of attached databases
.dump ?TABLE? ...      Dump the database in an SQL text format
                         If TABLE specified, only dump tables matching
                         LIKE pattern TABLE.
.echo ON|OFF           Turn command echo on or off
.exit                  Exit this program
.explain ?ON|OFF?      Turn output mode suitable for EXPLAIN on or off.
                         With no args, it turns EXPLAIN on.
.header(s) ON|OFF      Turn display of headers on or off
.help                  Show this message
.import FILE TABLE     Import data from FILE into TABLE
.indices ?TABLE?       Show names of all indices
                         If TABLE specified, only show indices for tables
                         matching LIKE pattern TABLE.
.load FILE ?ENTRY?     Load an extension library
.log FILE|off          Turn logging on or off.  FILE can be stderr/stdout
.mode MODE ?TABLE?     Set output mode where MODE is one of:
                         csv      Comma-separated values
                         column   Left-aligned columns.  (See .width)

                         html     HTML <table> code
                         insert   SQL insert statements for TABLE
                         line     One value per line
                         list     Values delimited by .separator string
                         tabs     Tab-separated values
                         tcl      TCL list elements
.nullvalue STRING      Print STRING in place of NULL values
.output FILENAME       Send output to FILENAME
.output stdout         Send output to the screen
.prompt MAIN CONTINUE  Replace the standard prompts
.quit                  Exit this program
.read FILENAME         Execute SQL in FILENAME
.restore ?DB? FILE     Restore content of DB (default "main") from FILE
.schema ?TABLE?        Show the CREATE statements
                         If TABLE specified, only show tables matching
                         LIKE pattern TABLE.
.separator STRING      Change separator used by output mode and .import
.show                  Show the current values for various settings
.stats ON|OFF          Turn stats on or off
.tables ?TABLE?        List names of tables
                         If TABLE specified, only list tables matching
                         LIKE pattern TABLE.
.timeout MS            Try opening locked tables for MS milliseconds
.width NUM1 NUM2 ...   Set column widths for "column" mode
.timer ON|OFF          Turn the CPU timer measurement on or off

2011/06/08

Android screenshot

要對 Android device screenshot 的話,最簡單的方法是透過 ddms, ddms 連上 device 後,就有個命令叫 screenshot 直接取回 png 格式。這方法可以在 Ubuntu/Windows 上進行,算是最方便的。

不過,若我們要做到像「每3秒取回一張 screenshot」要怎麼辦?首先,可以參考 Android source code 中的
sdk/screenshot/src/com/android/screenshot/Screenshot.java
它的做法基本上跟 ddms 的做法是完全一模一樣的,只是這個可以產生一個可執行檔,放在  out/host/linux-x86/bin/screenshot2.jar

上述 screenshot2 有搭配一個同名的 shell script 程式,純粹用來設定執行 out/host/linux-x86/framework/screenshot2.jar 的執行環境, 因為是 jar file, 理論上也可以在 windows 下執行才對,但是我沒測試過在 windows 下執行,想必也很複雜,因為還要裝 DDMS library。

還有一個辦法是透過直接抓取 /dev/graphics/fb0 來轉換,Android 是採用 framebuffer 來做 display device, 不過直接抓這個 framebuffer 的話,其格式是 raw image, 或是說,我們必須先研究出 android device framebuffer format, 這一點根據我的研究是每個產品都可以不一樣,每個 android version 也不見得相同。But….至少在我的 android 3.1 模擬機中,是 16 bpp, 這一點,最好是看上面提到的 sdk/screenshot/src/com/android/screenshot/Screenshot.java 中會有線索。

上述的提到的直接存取 framebuffer 的做法上,可以參考下面的方法:
$ adb pull /dev/graphics/fb0 fb0.raw   #
這一步可以抓回 device 上的  screenshot raw image
$ ffmpeg -vframes 1 -vcodec rawvideo -f rawvideo -pix_fmt rgb565le -s 1280x800 -i fb0.raw -f image2 -vcodec png fb0.png
# 上面 ffmpeg 的參數可以參考 Screenshot.java 給的線索,當然 code 本身沒有,自己印出來看就知道了

 
上述命令必須在有安裝 ffmpeg 的環境才能執行,因此是可以在 Ubuntu/Windows 下執行的。比較神奇的是,明明 file size 1280x800x4, 我原本以為是 32bpp, 結果是 16bpp

參考資訊: http://stackoverflow.com/questions/2807070/screenshot-of-the-nexus-one-from-adb

2011/04/19

sshfs on window7, dokan 簡介

繼上次寫過『sshfs』之後,一直沒在看這段,不過目前又有用到,上次寫的太簡單,這邊就多寫一些,當然若要用 winScp 也是不錯的選擇。

首先,到 http://dokan-dev.net/en/download/ 去抓檔案,必須抓的檔案有三個:
1. 抓 Dokan library, 目前是 dokan-0.6.0 2011/01/10,是個執行檔,執行它
2. 抓 dokan-sshfs-0.2.0,是個 msi, 沒錯,你沒看錯,是 0.2.0, 不知道為什麼作者要這樣放,執行它
3. 抓 0.2.0 的補丁。解壓它,然後蓋掉上面 0.2.0 安裝路徑下的檔案,共有5個

最後,就是執行上面覆蓋後的 DokanSSHFS.exe , 我是安裝在 C:\Program Files (x86)\Dokan\DokanSSHFS\

或請參考 sshfs for windows 一文

2011/03/21

Android ApiDemos

這篇目前就當成註記。

Android 有個很重要的 Sample code 叫 ApiDemos, 這個的重要性不止是它內附了非常多的 API demo 用法,還是 CTS 的測試項目之一。

有兩篇不錯的文章註記在這兒:
http://hunter2014.javaeye.com/blog/778160
http://cheng-min-i-taiwan.blogspot.com/2010/10/apidemos.html

2011/02/21

Help for adb on windows

求救!!!求救!!!,不知道有沒有朋友可以教一下,怎樣在 windows 編譯 adb? 因為工作上的需求,需要在 windows 修改屬於自己的 adb.....

關於這個問題,我有找到How to build Android Windows SDK,這篇講的不錯,不過我找到的第一篇是構建Windows版的Android SDK,只是兩篇都沒有教人怎樣成功編譯 adb on windows.....嗚呼

2011/02/14

libusb 初探4-device IO

usb 作為一個通用 IO bus,當然就具備同步與非同步,當然非同步相對複雜,我們就看同步IO的就好了

同步 IO 有二種(其實還有第三種叫 stream, 似乎沒被實作):
libusb_bulk_transfer() : Perform a USB bulk transfer.
及 libusb_interrupt_transfer() : Perform a USB interrupt transfer.

而要跟 device 溝通,很重要的一個函數叫 libusb_control_transfer()
Function Documentation

int libusb_control_transfer ( libusb_device_handle *  dev_handle,
    uint8_t  bmRequestType,
    uint8_t  bRequest,
    uint16_t  wValue,
    uint16_t  wIndex,
    unsigned char *  data,
    uint16_t  wLength,
    unsigned int  timeout  
)   
Perform a USB control transfer.

The direction of the transfer is inferred from the bmRequestType field of the setup packet.

The wValue, wIndex and wLength fields values should be given in host-endian byte order.

Parameters:
    dev_handle  a handle for the device to communicate with
    bmRequestType  the request type field for the setup packet
    bRequest  the request field for the setup packet
    wValue  the value field for the setup packet
    wIndex  the index field for the setup packet
    data  a suitably-sized data buffer for either input or output (depending on direction bits within bmRequestType)
    wLength  the length field for the setup packet. The data buffer should be at least this size.
    timeout  timeout (in millseconds) that this function should wait before giving up due to no response being received. For an unlimited timeout, use value 0.

Returns:
  on success, the number of bytes actually transferred
  LIBUSB_ERROR_TIMEOUT if the transfer timed out
  LIBUSB_ERROR_PIPE if the control request was not supported by the device
  LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
  another LIBUSB_ERROR code on other failures
上面有句話不知道有沒注意到: The wValue, wIndex and wLength fields values should be given in host-endian byte order

底下來寫一點心得:

.首先就是找到 usb device 有兩種方法,一種是透過 cnt = libusb_get_device_list(NULL, &devs) 這樣的呼叫,另一種則是透過 devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a);

.在 libusb_get_device_list() 的第一個參數 libusb_context,這是讓你可以同時控制兩個 libusb sessions, 一般情況不必管這個值的話就給 NULL 即可

.底下有一段利用 libusb_control_transfer() 來跟 device 溝通的範例:
#define CTRL_IN                 (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN)
#define USB_RQ                  0x04

static int print_f0_data(void)
{
        unsigned char data[0x10];
        int r;
        unsigned int i;

        r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data,
                sizeof(data), 0);
        if (r < 0) {
                fprintf(stderr, "F0 error %d\n", r);
                return r;
        }
        if ((unsigned int) r < sizeof(data)) {
                fprintf(stderr, "short read (%d)\n", r);
                return -1;
        }

        printf("F0 data:");
        for (i = 0; i < sizeof(data); i++)
                printf("%02x ", data[i]);
        printf("\n");
        return 0;
}
其中libusb_control_transfer() 裡的第二個參數決定 request 的型態,以上例是 LIBUSB_REQUEST_TYPE_VENDOR, 這個是非 STANDARD, 因此視 application(device) 而定。 .這邊再舉個例子:
int config;
    uint8_t tmp = 0;
    r = libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN,
           LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
    if (r == 1) {
          config = tmp;
    }
.最後再來看一個例子
void check_device(libusb_device *dev)
{
  ...
  int r = libusb_get_device_descriptor(dev, &desc);  // 先取得 device descriptor,因為內藏豐富資訊
  ...
  if (!is_adb_interface (desc.idVendor, desc.idProduct,
                         ADB_CLASS, ADB_SUBCLASS, ADB_PROTOCOL))
  { .... }

  /* 底下兩行再取得 device handler 的 bus, address 資訊, 以便給 libusb_open() 使用 */
  uh.dev_bus = libusb_get_bus_number(dev);
  uh.dev_addr = libusb_get_device_address(dev);

  /* 底下四行取得 device 的 active config descriptor,必須其 interface 有值才合法
     因為每個 usb device 是可以 config 成多個不同 device type,
     在此只需要取回現行的 config 即可(即 active config)
  */
  r = libusb_get_active_config_descriptor(dev, &config);
  if (config->interface != NULL) {
        found = check_usb_interfaces(config, &desc, &uh); // 檢查是否 support android adb
  }

  /* 底下開始要進一步與 device 溝通取回更多資訊 */

  r = libusb_open(dev, &uh.devh); // 類似 libusb_open_device_with_vid_pid()
  uh.dev = dev;

  if (found >= 0) {
        ....
        // 在對 endpoint 進行 I/O 之前,一定要 claim interface
        uh.interface = found;
        r = libusb_claim_interface(uh.devh, uh.interface);
        ....
        if (desc.iSerialNumber) {
            // reading serial
            uint16_t    buffer[128] = {0};
            uint16_t    languages[128] = {0};
            int languageCount = 0;

            memset(languages, 0, sizeof(languages));
            r = libusb_control_transfer(uh.devh,
                LIBUSB_ENDPOINT_IN |  LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
                LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_STRING << 8,
                0, (uint8_t *)languages, sizeof(languages), 0);
            ....
            languageCount = (r - 2) / 2;
            for (i = 1; i <= languageCount; ++i) {
                memset(buffer, 0, sizeof(buffer));

                /* 底下透過 request type 為 LIBUSB_REQUEST_TYPE_STANDARD 來指定
                   LIBUSB_REQUEST_GET_DESCRIPTOR 的方式取回 android device ID string
                   其傳回值型態由 LIBUSB_DT_STRING 指定,其值為 iSerialNumber
                */
                r = libusb_control_transfer(uh.devh,
                    LIBUSB_ENDPOINT_IN |  LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
                    LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc.iSerialNumber,
                    languages[i], (uint8_t *)buffer, sizeof(buffer), 0);
                .... // 處理存在 buffer 中的 serial 資訊,也就是 android device ID
            }
            if (register_device(&uh, serial) == 0) {
                ..... // fail
            }
            else { // success
                libusb_ref_device(dev);
            }
        }
  }
  ......
}

libusb 初探3-usb device descriptor

完整說明請見 usb device descriptor,底下僅列出資料結構:

Data Fields

uint8_t bLength
Size of this descriptor (in bytes).
uint8_t bDescriptorType
Descriptor type.
uint16_t bcdUSB
USB specification release number in binary-coded decimal.
uint8_t bDeviceClass
USB-IF class code for the device.
uint8_t bDeviceSubClass
USB-IF subclass code for the device, qualified by the bDeviceClass value.
uint8_t bDeviceProtocol
USB-IF protocol code for the device, qualified by the bDeviceClass and bDeviceSubClass values.
uint8_t bMaxPacketSize0
Maximum packet size for endpoint 0.
uint16_t idVendor
USB-IF vendor ID.
uint16_t idProduct
USB-IF product ID.
uint16_t bcdDevice
Device release number in binary-coded decimal.
uint8_t iManufacturer
Index of string descriptor describing manufacturer.
uint8_t iProduct
Index of string descriptor describing product.
uint8_t iSerialNumber
Index of string descriptor containing device serial number.
uint8_t bNumConfigurations
Number of possible configurations.