2019年10月14日 星期一

[Git 文章收集] Differences between git merge and git rebase

Source From Here
Preface
Merging and rebasing are the two most popular way to applying changes from one branch into another one. They both give you the same result at the end, so let’s talk about differences. Briefly:
git merge apply all unique commits from branch A into branch B in one commit with final result
git merge doesn’t rewrite commit history, just adds one new commit
git rebase gets all unique commits from both branches and applies them one by one
git rebase rewrites commit history but doesn’t create extra commit for merging


Detailed
Default git strategy for merging is simple three-way merging. It means that Git uses the two snapshots (every commit has snapshot for full working directory) pointed to by the branch tips and the common ancestor of the two and then make result commit with applied changes from both branches.

Let’s say we have two branches feature1 and feature2 that have diverged from a common commit “a” to have four commits each.



Now we want to combine both the features into a single branch. Merge and Rebase are our options. Let’s see what each of them can do.

Git Merge:
Merge will seem like a fairly obvious thing, if you look at the end result. It is pretty much like taking two threads and tying them up in a knot:



Here the commit ‘b’, has the information regarding all the commits in feature1 and feature2. So, Merge preserves the history of the repository.

Git Rebase:
Rebase on the other hand doesn’t preserve the history. It quite literally re-bases one branch on top of the other i.e., it changes the base of the branch. Let’s see rebasing with the same example. Let’s say I want to rebase feature1 onto feature2, what that means is that I want all the commits in the branch feature1 on top of the commits of feature2. So, after rebase your commit history would look like the following:



As you see in the picture, the base of feature1 which was previously the commit “a”, has been shifted to the green commit “4”. Hence the name Re-Base. Here feature1 is sitting on top of feature2 as opposed to being on “a”.

Both Merge and Rebase have their pros and cons. Merge keeps the history of the repository but can make it hard for someone to understand and follow what’s going on at a particular stage when there are multiple merges. Rebase on the other hand ‘rewrites’ history (read - creates new history) but makes the repo look cleaner and is much easier to look at.

When to use:
* git merge is a default behavior when you use git pull. Use it as default if you are not bothering about commit history and want to avoid problems
* use git rebase to make your commit history more clear and consistent (use it only before pushing to remote servers to keep your name and karma clean)
* use git rebase for temporary local branches — they are not necessary for public commit history and won’t make problems
* use git rebase -i (interactive) for rewriting your local commit history into pretty one before pushing it on the remote server.


Real Example
Let's use few git commands to see how git merge and git rebase work.

Merged branch `iss53` back to branch `master`
Check commit history before merge:
# git status
# On branch iss53
nothing to commit, working directory clean


# git log --oneline | head -n 3
2b108f6 C2
8f30e1a C1

bb134c5 Update 105M.img


# git checkout master
Switched to branch 'master'

# git log --oneline | head -n 2
d3f9908 C3
bb134c5 Update 105M.img
faa8827 Upload large file >50M

Now it's time to do the merge in branch `master`:
# git rebase iss53
  1. Merge branch 'iss53'  
  2.   
  3. # Please enter a commit message to explain why this merge is necessary,  
  4. # especially if it merges an updated upstream into a topic branch.  
  5. #  
  6. # Lines starting with '#' will be ignored, and an empty message aborts  
  7. # the commit.  


# git log --oneline | head -n 5
8a747e7 Merge branch 'iss53'
d3f9908 C3
2b108f6 C2
8f30e1a C1
bb134c5 Update 105M.img


# git checkout iss53
Switched to branch 'iss53'

# git log --oneline | head -n 3
2b108f6 C2
8f30e1a C1
bb134c5 Update 105M.img

Rebase feature1 onto feature2
Again, let's check the status of each branch:
# git log --oneline | head -n 3
664eb38 F2
a3033da F1

bb134c5 Update 105M.img


# gis
# On branch feature1
nothing to commit, working directory clean


# git checkout feature2 // Switch to branch `feature2`

# gis
# On branch feature2
nothing to commit, working directory clean


# git log --oneline | head -n 3
a171eef F2_2
c6fd8f1 F2_1

bb134c5 Update 105M.img

Now let's rebase branch `feature1` to branch `feature2` (After switching back to `feature1`):
# git rebase feature2
First, rewinding head to replay your work on top of it...
Applying: F1
Applying: F2


# git log --oneline | head -n 5
4aefbe2 F2
26e83b4 F1

a171eef F2_2
c6fd8f1 F2_1
bb134c5 Update 105M.img


Supplement
Git: 比較 Merge Squash 與 Rebase Squash
用於合併不同分支時,希望在合併後只有一個提交記錄。...

Quora - What is the difference between rebase and merge in Git?
3.2 使用 Git 分支 - 分支和合併的基本用法
【狀況題】剛才的 Commit 後悔了,想要拆掉重做…

2019年10月7日 星期一

[Linux 文章收集] Bash 腳本set 命令教程

Source From Here
一、簡介
set 命令是 Bash 腳本的重要環節,卻常常被忽視,導致腳本的安全性和可維護性出問題。本文介紹它的基本用法,讓你可以更安心地使用 Bash 腳本。我們知道,Bash 執行腳本的時候,會創建一個新的 Shell:
$ bash script.sh

上面代碼中,script.sh 是在一個新的 Shell 裡面執行。這個 Shell 就是腳本的執行環境,Bash 默認給定了這個環境的各種參數。set 命令用來修改 Shell 環境的運行參數,也就是可以定制環境。一共有十幾個參數可以定制,官方手冊有完整清單,本文介紹其中最常用的四個。

二、set -u
執行腳本的時候,如果遇到不存在的變量,Bash 默認忽略它:
  1. #!/usr/bin/env bash  
  2.   
  3. echo $a  
  4. echo bar  
上面代碼中,$a 是一個不存在的變量。執行結果如下:
$ bash script.sh

bar

可以看到,echo $a 輸出了一個空行,Bash 忽略了不存在的 $a,然後繼續執行 echo bar。大多數情況下,這不是開發者想要的行為,遇到變量不存在,腳本應該報錯,而不是一聲不響地往下執行。 set -u 就用來改變這種行為。腳本在頭部加上它,遇到不存在的變量就會報錯,並停止執行。
  1. #!/usr/bin/env bash  
  2. set -u  
  3.   
  4. echo $a  
  5. echo bar  
運行結果如下:
$ bash script.sh
bash: script.sh:行4: a: 未绑定的变量

可以看到,腳本報錯了,並且不再執行後面的語句。-u 還有另一種寫法 -o nounset,兩者是等價的:
三、set -x
默認情況下,腳本執行後,屏幕只顯示運行結果,沒有其他內容。如果多個命令連續執行,它們的運行結果就會連續輸出。有時會分不清,某一段內容是什麼命令產生的。set -x 用來在運行結果之前,先輸出執行的那一行命令:
  1. #!/usr/bin/env bash  
  2. set -x  
  3.   
  4. echo bar  
執行上面的腳本,結果如下:
$ bash script.sh
+ echo bar
bar

可以看到,執行 echo bar 之前,該命令會先打印出來,行首以 + 表示。這對於調試複雜的腳本是很有用的。-x 還有另一種寫法 -o xtrace:
四、Bash 的錯誤處理
如果腳本里面有運行失敗的命令(返回值非 0),Bash 默認會繼續執行後面的命令:
  1. #!/usr/bin/env bash  
  2.   
  3. foo  
  4. echo bar  
面腳本中,foo 是一個不存在的命令,執行時會報錯。但是,Bash 會忽略這個錯誤,繼續往下執行:
$ bash script.sh
script.sh:行3: foo: 未找到命令
bar

可以看到,Bash 只是顯示有錯誤,並沒有終止執行。這種行為很不利於腳本安全和除錯。實際開發中,如果某個命令失敗,往往需要腳本停止執行,防止錯誤累積。這時,一般採用下面的寫法:
  1. command || exit 1  
上面的寫法表示只要 command 有非零返回值,腳本就會停止執行。如果停止執行之前需要完成多個操作,就要採用下面三種寫法:
  1. # 写法一  
  2. command || { echo "command failed"; exit 1; }  
  3.   
  4. # 写法二  
  5. if ! command; then echo "command failed"; exit 1; fi  
  6.   
  7. # 写法三  
  8. command  
  9. if [ "$?" -ne 0 ]; then echo "command failed"; exit 1; fi  
另外,除了停止執行,還有一種情況。如果兩個命令有繼承關係,只有第一個命令成功了,才能繼續執行第二個命令,那麼就要採用下面的寫法:
  1. command1 && command2  
五、 set -e
上面這些寫法多少有些麻煩,容易疏忽。set -e 從根本上解決了這個問題,它使得腳本只要發生錯誤,就終止執行:
  1. #!/usr/bin/env bash  
  2. set -e  
  3.   
  4. foo  
  5. echo bar  
執行結果如下。
$ bash script.sh
script.sh:行4: foo: 未找到命令

可以看到,第4行執行失敗以後,腳本就終止執行了。

set -e 根據返回值來判斷,一個命令是否運行失敗。但是,某些命令的非零返回值可能不表示失敗,或者開發者希望在命令失敗的情況下,腳本繼續執行下去。這時可以暫時關閉 set -e,該命令執行結束後,再重新打開 set -e:
  1. set +e  
  2. command1  
  3. command2  
  4. set -e  
上面代碼中,set +e 表示關閉 -e 選項set -e 表示重新打開 -e 選項。還有一種方法是使用 command || true,使得該命令即使執行失敗,腳本也不會終止執行:
  1. #!/bin/bash  
  2. set -e  
  3.   
  4. foo || true  
  5. echo bar  
上面代碼中,true 使得這一行語句總是會執行成功,後面的 echo bar 會執行。-e 還有另一種寫法 -o errexit
六、set -o pipefail
set -e 有一個例外情況,就是不適用於管道命令。所謂管道命令,就是多個子命令通過管道運算符(|)組合成為一個大的命令。Bash 會把最後一個子命令的返回值,作為整個命令的返回值。也就是說,只要最後一個子命令不失敗,管道命令總是會執行成功,因此它後面命令依然會執行,set -e 就失效了。

請看下面這個例子:
  1. #!/usr/bin/env bash  
  2. set -e  
  3.   
  4. foo | echo a  
  5. echo bar  
執行結果如下:
$ bash script.sh
a
script.sh:行4: foo: 未找到命令
bar

上面代碼中,foo 是一個不存在的命令,但是 foo | echo a 這個管道命令會執行成功,導致後面的echo bar會繼續執行。set -o pipefail 用來解決這種情況,只要一個子命令失敗,整個管道命令就失敗,腳本就會終止執行:
  1. #!/usr/bin/env bash  
  2. set -eo pipefail  
  3.   
  4. foo | echo a  
  5. echo bar  
運行後,結果如下。
$ bash script.sh
a
script.sh:行4: foo: 未找到命令

可以看到,echo bar 沒有執行。

七、總結
set 命令的上面這四個參數,一般都放在一起使用。
  1. # 写法一  
  2. set -euxo pipefail  
  3.   
  4. # 写法二  
  5. set -eux  
  6. set -o pipefail  
這兩種寫法建議放在所有Bash 腳本的頭部。另一種辦法是在執行Bash 腳本的時候,從命令行傳入這些參數:
$ bash -euxo pipefail script.sh


[Git 文章收集] Differences between git merge and git rebase

Source From  Here Preface Merging and rebasing are the two most popular way to applying changes from one branch into another one. They bot...