2017年7月29日 星期六

設定 ssh config 讓人生簡單一些

ssh 是工作上的重要工具之一,平時要連進其他電腦、傳送檔案、甚至是上 ptt 都可以用 ssh 達成,這篇文整理一些 ssh 的設定,可以讓 ssh 的使用更簡單一些。

第一個是免打密碼的設定,這個如果有用 github 的話一定很清楚,簡單來說我們可以把電腦的公開金鑰放一份到遠端,登入的時候 ssh 就會不問密碼自動驗證。
步驟如下:
在家目錄下的 .ssh 目錄中,鍵入 ssh-keygen 指令,預設是使用 rsa,如果覺得 rsa 不夠安全可能會被心算解開(誤,可以用 -t dsa | ecdsa | ed25519 | rsa 選擇要用哪種公開金鑰加密法,用 -b 來選擇生成的金鑰長度(不過我下了 ssh-keygen -b 16384 然後它金鑰生不出來…)。
金鑰生成後,會詢問檔案要存在哪,預設就是 .ssh 資料夾;另外會問 pass phrase,我們都用上 public key 就是不想打密碼,除非有安全性考量否則留空即可。
Enter file in which to save the key (/home/garbage/.ssh/id_rsa): /tmp/id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
會產生兩個檔案,id_[algorithm] id_[algorithm].pub,從檔名看 pub 自然是公開金鑰了。
接著將 .pub 檔案的內容,複製到伺服器上 .ssh/authorized_keys 裡面,如此一來 ssh 就能免密碼登入了

詳細請參考
https://blog.gtwang.org/linux/linux-ssh-public-key-authentication/

第二個是設定 ssh config,這有點像 ssh 專用的 /etc/hosts,可以幫常連的機器設定別名,甚至是連線時要用什麼動作,以下來看看:
首先,假設我們要連線一台電腦,remotemachine.com 或者是 ip,帳號名稱是 yolo,開的 port 為 9453,要連線要打入這樣的指令:
ssh yolo@remotemachine.com -p 9453
當然如果照這篇文章安裝了 fzf ,某種程度上能大幅緩解連一台機器要打很多字的問題--連機器的指令不會變,用fzf 找出來就是了,但每次都用 fzf 來找還是會花一些時間,這時可以改用 ssh config
在家目錄的 ~/.ssh/config 檔案中,加上以下內容:
Host MyMachine
 HostName remotemachine.com
 User yolo
 Port 9453
ssh MyMachine 就會直接連上 yolo@remotemachine.com -p 9453
這些規則不是寫死的,ssh 的設定優先次序是命令列、.ssh/config、/etc/ssh/ssh_config,設定後照樣可以用 another@MyMachine 來使用其他使用者登入,或者用 -p 來改變連線的埠。
設定檔就是許多 Host 為開頭的區塊,內容為針對該 Host 的設定,上面展示的就是設定預設主機、使用者跟埠,還有許多其他設定可用,像是認證檔的位置、選擇加密方式,詳細可以看 man ssh_config

下面是我查到 ssh config 的契機,平常工作的地方開了一個新的工作室,但平常測試的主機A放在工作室A,兩個工作室的網段不一樣,沒辦法透過 ip 直接連線;為了連線所以在 router 上面鑽一個洞,開一個 ip 會直接進到工作室A的另一台主機B,到主機B就進到內網,可以直連測試主機A。
如果要打指令,大概會長得像下面這樣,等於是透過machineB ,開一個 pseudo-tty,再執行 ssh進到machineA:
ssh -t userB@machineB.ip -p 9453 ssh userA@machineA.ip
要簡單一點找到這個 stackoverflow,關鍵字是ssh proxy,在 .ssh/config 裡面加上這些設定
Host machineB
  Hostname machineB.ip
  User userB

Host machineA
  User userA
  ProxyCommand ssh -q machineB nc -q0 machineA.ip 22
ProxyCommand 指定要連到這台 Host 時要下的指令為何,這裡會用 ssh quiet mode ,再執行 netcat 接入machineA,這樣只要用 ssh machineA 就能完全上述工作啦
當然這樣每次連線都要打兩次密碼,不想打密碼,照上面在機器上放入自己的公鑰即可。

2017年7月3日 星期一

使用 git-svn 和 svn 遠端協同開發

最近因為跟人協作,共同開了一個版本控制資料夾。
對方使用的版本控制是 svn ,而我則是用 git,重新學 svn 實在太麻煩了,有沒有一個好的解決方案呢?經過強者我同學 AZ 大神跟 qcl 大神的推薦,決定使用 git-svn 來解決。

git-svn 是 git 提供的一個…橋接工具?可以在遠端保持 svn 的狀態,本地則用 git 的管理,享受 git 那些branch 開很大開不用錢,git stash之類,種種我們再熟悉不過的使用方式;另外用了 git 也不用每次都跟遠端目錄同步,可以在自己家裡亂搞,最後有網路時再一次同步。
畢竟在 git 出世前,svn 才是世界上版本控制的霸主,有不少早期知名的 project ,例如LLVM, apache software;用了 git svn ,不需重新熟習 svn 也能用 git 參與這些 project 的開發。

第一步,在拉下 svn repository 的時候,直接使用 git svn 的指令,所有跟 svn 相關的指令都是 git svn xxx:
git svn clone http://SERVER/svn/trunk/ TARGET_DIR
這樣就會把整個svn給抓下來,它同等於執行 git svn init 跟 git svn fetch。
要注意的是因為 git 設計的邏輯就是「所有的機器裡面都有完全一樣的內容」,所以 git svn clone 的時候,它會把遠端的內容逐個載下來,如果遠端 svn 很大的話,這個動作可能會花上非常久的時間。
抓下來的 repository會產生一個叫 git-svn 的 remote ,這個 remote 只有用 git svn 的時候會動到;要注意一點,因為 svn 只能維持一條線性的歷史,同時也沒辦法修改歷史,所以在使用 git-svn 的repository 裡面,不要和其他的 git 遠端同時使用,保持所有使用者都用一個 svn 遠端,git svn 設計上也假定你只有一個遠端。

再來我們就能做些修改,一樣就是git add, git commit,這時提交的內容只會在本地中,可以用:
git svn dcommit
把內容送到 svn 遠端去。
git 在推送到遠端 svn 的時候,會把一個一個 commit 取出,並提交到 svn,然後最重要的,它會依照svn 的提交結果,重新在 git repository 裡面 commit 這些結果,整個 dcommit 的結果,最終效果更像是 git rebase,這跟一般的 git push 完全不同。
commit aea3964417e62759dadf9e1769d927623e0f5a1b
Author: yodalee <garbage@mail.com>
Date:   Fri Jun 30 16:31:30 2017 +0800

    add debug message to every function call

commit 2531676d1d1b81f898e9965c0e46f28e92e02c82
Author: yoda <yoda@59464745-af19-4556-b8ec-ef3a2794439b>
Date:   Fri Jun 30 08:00:23 2017 +0000

    fix description in sensor function

    git-svn-id: http://SERVER/svn/trunk@2345 59464745-af19-4556-b8ec-ef3a2794439b
上面的 git log ,包含一個已經推到遠端的 commit 跟一個還未推送的 commit,推送到 svn 上的 commit 會出現 git-svn-id 的遠端資訊,同時它的作者資訊跟雜湊值也會變化,這也是為何不建議同一個 repository 中同時使用 git跟svn的遠端,git-svn 修改雜湊值會讓 git 遠端天下大亂。
就算要有 SVN 跟 git 兩個遠端也要先向svn dcommit ,得到最終雜湊值後,再推送到 git 遠端上。

svn 身為版本控制,也是允許其他人共同協作,只要有協作就會有衝突要解決,如果發生衝突,svn dcommit 會無法推送到遠端。
為了解決該問題,可以執行 git svn rebase ,它和 git pull 很像,首先它會用 git svn fetch ,把 svn 遠端上的內容拉下來,沿著 git-svn 往前長,之後再用 git rebase ,把現在 git HEAD 指向的目標,rebase 到 git-svn 上。
如果沒有更新的內容,在 git svn rebase 時會看到:
Current branch master is up to date.
此時就能放心進行 git svn dcommit

這裡會牽涉到一些 git 跟 svn 設計不同的地方,在 git 裡面,假設 remote/master 跟本地的 master 有所不同,在 push master 的時候即會發生衝突,git 會要求你解決衝突後才能 push。
svn 在這點上,只有檔案有所衝突的時候才會要求,所以當遠端修改 A 檔案,本地修改 B 檔案,在 dcommit 的時候是完全沒有問題--只是遠端專案會進到一個 A, B 檔案都修改過,而本地檔案卻沒看到 A 檔被修改的狀態。
直接引用 git 文件的話:「如果做出的修改無法相容但沒有產生衝突,則可能造成一些很難確診的難題。」所以,誠心建議還是在 dcommit 前都 svn rebase 一下,確保跟遠端保持隨時同步。

其實有了 dcommit 跟 rebase,大概也就差不多了,有關 svn branch 的部分我就不太想看了,畢竟 git branch 比較強大;唯一要注意的,大概就是要送到 svn 伺服器之前,儘量用 rebase ,把 git 的各 branch 收整成一條線性,再進行 dcommit 。另外有個小技巧是,git svn dcommit/rebase 在操作的時候不允許任何 uncommit 的內容,所以在 svn 操作的前後,可以利用 git stash push/pop ,把未commit 的內容塞進stash,svn 操作結束後再取出來。

參考資料:
Git 與 Subversion