Software engineering notes

Git

add

Add a new file or folder

git add example.txt

Add all untracked files and folders

git add .

branch

Rename branch

git branch -m {old_name} {new_name}

Rename current branch

git branch -m {new_name}

刪除branch

git branch -d developXD
git branch -D {branch name}

git branch —no-merged

Remove outdated local branch

  1. git checkout master
  2. git branch —merged or git branch -vv

[origin/bug/more-phan-fixes: gone] [REFACTOR] Phan fixes

blame

Show the editor of each line

git blame test.qq

merge

讓 commit log 紀錄您是開分支出去再 merge 回來的。

git merge --no-ff develop

Undo 某一個 merged 但未 push 的 branch, 有幾種方法:

  1. git reset --hard HEAD~1
  2. 先用 git reflog 看 log, 再把 HEAD@{1} 的 SHA1 記下來 (不是 HEAD@{0} 的, 因為那是你目前的點, 我們要還原到前一動), 再執行 git reset --hard {SHA1}
  3. git reset --hard {COMMIT_ID} 到早一點的點, 然候再慢慢補齊後來的 commit

如果對結果不太確定的話, 建議先用目前的 branch 建一個測試用的 branch 練習, 確定結果符合你要的再做在原本的 branch

rebase

rebase 基本操作概念

  1. rebase master

     git checkout feature
     git rebase master
    
  2. merge 回 master

     git checkout master
     git merge feature
    

類似 merge, 但將兩個不同的 branch 合併成同一條線且不會有 merge commit, tig 的線圖也會比較漂亮

如果有發生 conflict 並修好後要繼續 rebase

git rebase --continue

修改 past commit message

git rebase -i 637bd5ac^

Choose “reword” for the commit in question, then you’ll have an opportunity to edit the message.

conflict standard vs diff3

Example 1

[standerd]
<<<<<<< HEAD
GreenMessage.send(include_signature: true)      // HEAD
=======
BlueMessage.send(include_signature: false)      // target branch
>>>>>>> merged-branch

[diff3]
<<<<<<< HEAD
GreenMessage.send(include_signature: true)      // HEAD
||||||| merged common ancestor
BlueMessage.send(include_signature: true)       // origin
=======
BlueMessage.send(include_signature: false)      // target branch
>>>>>>> merged-branch

[Solution]
GreenMessage.send(include_signature: false)

Example 2

[standard]
List Of Best Emojis
Rainbow 🌈
<<<<<<< HEAD
Pizza 🍕
Unicorn 🦄
=======
Avocado 🥑
>>>>>>> topic-branch

[diff3]
List Of Best Emojis
Rainbow 🌈
<<<<<<< HEAD
Pizza 🍕
Unicorn 🦄
||||||| merged common ancestors
Pizza 🍕
=======
Avocado 🥑
>>>>>>> topic-branch

[solution]
Rainbow 🌈
Pizza 🍕
Unicorn 🦄
Avocado 🥑

Edit specific commit

  1. git rebase -i HEAD~3
  2. Mark e for the commit that you want to change
  3. Add or remove files
  4. git commit —amend
  5. git rebase —continue

rebase a specific commit

  1. (edit files)
  2. git add
  3. git commit –fixup {Parent commit id}
  4. git rebase -i HEAD~3 (assume we want to rebase on the second commit)
  5. mark fixup on the commit that you want it to go (called commit C)
  6. change the order of commit C to middle, because it will rebase onto the commit above it

merge vs rebase

兩者結果是一樣的,產生的 history 不同點 :

checkout

新增 develop branch 並且切換過去

$ git checkout -b develop

新增由 develop 分支出來的 myfeature

$ git checkout -b myfeature develop

使檔案回復成最近一次commit的狀態

$ git checkout -- test.php

強制回復己在 add 狀態被修改過的檔案, Untracked files 則不受影響

git checkout -f

所有track中且修改過的檔案回復成最近一次commit的狀態

git checkout .

切到某一個 commit

git checkout 62a4a5c9a6e8a323a1ea12ec54ac35da7ce1b662

從某個 commit 切回原本 branch

git checkout -
相當於 git checkout master (不一定是 master 取決於在哪一個 branch)

commit

How to write commmit message

reset

回復到某一個 commit id

git reset --hard {commit id}

回復上一個 commit (包含 merge)

git reset HEAD^ --hard

但修改的資料不保留,也就是回覆到上一個 commit 的初始狀態

回復上一個 commit 但修改的資料保留下來

git reset HEAD^ --soft

回復到 origin/master 的狀態

git reset --hard origin/master

當 merge 或做了什麼後悔的事, 在還沒 push 前都可以使用此指令還原到 remote 的最新狀態

git reset HEAD^ --hard 如何救回

git reflog                  # 會有你的每條 git 操作的 log, 左邊有 commit id, 記下你要回復的 commit id
git reset --hard bceefb7    # 這樣就可以救回了

(補充)如果不放心可以自行測試

$ git init
Initialized empty Git repository in .git/

$ echo "testing reset" > file1
$ git add file1
$ git commit -m 'added file1'
Created initial commit 1a75c1d: added file1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file1

$ echo "added new file" > file2
$ git add file2
$ git commit -m 'added file2'
Created commit f6e5064: added file2
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file2

$ git reset --hard HEAD^
HEAD is now at 1a75c1d... added file1

$ cat file2
cat: file2: No such file or directory

$ git reflog
1a75c1d... HEAD@{0}: reset --hard HEAD^: updating HEAD
f6e5064... HEAD@{1}: commit: added file2

$ git reset --hard f6e5064
HEAD is now at f6e5064... added file2

$ cat file2
added new file

revert

與 reset 不同的事他回復是會有紀錄的,log 會疊下去,所以如果你不小心 push 了一個 commit 到 master,你想回復,用 revert 會比 reset 簡單操作

回復上一個 commit

git revert HEAD

回復到某一個 commit id

git revert {commit_id}

將某一個已經 commit/merge 的 commit_id 回復

git revert --strategy resolve 305a9bd07ae40c585ac2f2761dea4374e7fee93e

回復後它會有一個 commit 紀錄, 如果想再把它加回來, 無法用 merge (Already up-to-date.), 但可以用 cherry-pick 加回來

cherry-pick

在某一個branch加上每個commit(from another branch)

git checkout develop
git cherry-pick f08515bc579a06dd9c8bd7f2dfc30ad4d5a73646

將某一個 branch 的某一個 commit 的變動 merge 到另一條 branch, 假如 develop 只想要 merge feature 的某一個 commit, 則需要先把 feature 的那條 commit id 記下來, 再切到 develop 做 cherry-pick

rerere

It is used for cherry-pick Especially for fixing conflict when cherry-pick latest commits from develop branch against an old tag commit Basically, it records the change you made and apply the fixes to next conflicts

See more: https://git-scm.com/docs/git-rerere

show

查看某一個 tag 的變動

git show v1.0.0

查看 commit id 修改內容

git show 41e24b9349acc9bbc1b8853284866f498892ce4b

log

最近一次 commit 改過的檔案

git log -n 1 --stat

最近一次 commit 的更改細節

git log -n 1 -p

reflog

顯示 git 的每個 log (包括操作 command 及 commit 內容等等)

diff

未加入 stage 時直接下

git diff qq.php

已加入 stage 要下

git diff HEAD qq.php

比較兩個 branch

git diff develop master

diff-tree

List modified files on a specific commid id

git diff-tree --no-commit-id --name-only -r cf5f610b67ce52c8d6e39a526e6405675f430bd8

config

將 diff 以 vimdiff 方式顯示

git config --global diff.tool vimdiff       # 使用 difftool 就可以啟動 vimdiff
git config --global difftool.prompt false   # 執行上面指令, 會問你是否執行, 很麻煩所以關掉它
git config --global alias.d difftool        # 使用 `git d` 相當於 `git difftool`

rm

push

強制 push, 之前的commit 紀錄會不見,只會有目前這個branch的commit 紀錄

git push origin master -f

(建議) 強制 push, 但不想覆蓋別人已經推的

git push --force-with-lease

when you do need to do a forced push but still ensure you don’t overwrite other’s work

建立遠端 branch

git branch dev          // 先建立 local branch
git push origin dev:refs/heads/dev

push to a private repo

git push https://dummy@github.com/dummy/example.git

pull

git pull --rebase 等於以下兩步

git fetch
git merge origin dev

git pull –rebase 會使 merge commit 不見, 改用 :

git fetch origin
git rebase -p origin/develop

在目前 branch 下 git pull origin {branch_name}: 代表將 {branch_name} (remote branch) merge 到目前這個 branch

pull from a private repo

git pull https://dummy@github.com/dummy/example.git

stash

放進暫存

git stash

查看暫存

git stash list

取出暫存

git stash pop

清空暫存

git stash clear

如果暫存有多個,可以指定要還原哪個commit ID

git stash [commit ID]

stash specific files

git add a1.go                   // files that You don't want to stash
git stash save --keep-index     // this command will stash the rest of files

tag

查看目前 tag

git tag
v1.0.0

新增 tag

git tag -a v1.4 -m "my version 1.4"
git push --tags

fetch

更新 git branch -r 的名單

git fetch

更新 remote branch list

git fetch --all

遠端已刪除的 branch, 本機 branch 不會刪除,只會更新新增的 remote branch

當要 merge 遠端 branch 前,先看修改了什麼,再merge

git fetch origin dev
tig
git merge origin dev

clean up local cache of remote branch

git fetch —prune

remote

(建議) 更新 remote branch list

git remote prune origin

git remote update origin --prune

遠端已刪除的 branch, 本機 branch 會幫你刪除,同時也會更新新增的 remote branch

新增遠端 repo

git remote add origin https://git.heroku.com/my-app.git

查看remote有哪些branch

git branch -r
git remote show origin

有時候用 git branch -r, 查看 remote branch 時, 發現明明刪掉的 branch 為何還在? 使用 git remote prune origin 來更新暫存檔, 就可以取得最新的 remote branch list

git pull branch from forked repo

cd into/cloned/fork-repo
git remote add upstream git://github.com/ORIGINAL-DEV-USERNAME/REPO-YOU-FORKED-FROM.git
git fetch upstream
git pull upstream master

show remote origin list

git remote -v

Update local repo and remote repo on github

  1. Update github repo via settings
  2. git remote set-url origin git@github.com:username/your-repo.git
  3. git remote -v to check if it’s all set
  4. rename local folder
  5. done
  6. (optional for go project) update module name in go.mod

rev-parse

latest commit hash

git rev-parse HEAD

clean

Remove untracked file and directory.

git clean -df

submodule

Basic commands

Add a new submodules

git submodule add https://github.com/msanders/snipmate.vim.git bundle/snipmate.vim

Create the local config file for submodules if it doesn’t exist

git submodule init

Fetch all data from submodules (after cloning a repo)

git submodule update

it will check out the commit that was most recently registered in the main repository. This does not necessarily align with the most recent commit in the submodule’s remote repository

Pull the latest changes from all submodules

git submodule update --remote

git will ignore the commit recorded in the parent repository, and instead, it will fetch and check out the latest commit from the remote of the submodule

Clone a repo and automatically clone all its submodules

git clone --recurse-submodules https://github.com/msanders/snipmate.vim.git

Remove a submodule

Delete the relevant section from .gitmodules and .git/config

oldPath="bundle/snipmate.vim"
git config -f .git/config --remove-section "submodule.${oldPath}"
git config -f .gitmodules --remove-section "submodule.${oldPath}"

Run git rm –cached path_to_submodule (no trailing slash).

$ git rm --cached "${oldPath}"

Commit and delete the untracked submodule files.

rm 'bundle/snipmate.vim'
rm -rf "${oldPath}"
rm -rf ".git/modules/${oldPath}"
git add .gitmodules
git commit -m "Removed ${oldPath}"

tig (not built-in command)

只顯示 merge 線圖

tig --merges

use Git’s default commit order

tig --topo-order

Operation in practice

Add a new submodule

git submodule add https://github.com/test-user/practice_submodule.git
git submodule init

Pull submodule for the first time

git submodule init
git submodule update

Pull submodule in the existing repo

git submodule update

Remote branch operations

create

git push origin develop:refs/heads/branch_to_create
git fetch origin
git branch --track branch_to_create origin/branch_to_create
git checkout branch_to_create

track

git fetch origin
git branch --track branch_to_track origin/branch_to_track

delete remote branch

git push origin --delete <branch_name>

rename

# 如果 branch 已存在要先 remove branch
git push origin --delete <branch_name>


git push origin develop:refs/heads/branch_to_rename            # 這步只是將遠端 develop copy 成 branch_to_rename, develop 還在
git fetch origin
git branch --track branch_to_rename origin/branch_to_rename
git checkout branch_to_rename
git push origin :refs/heads/develop
git branch -D develop

publish

git push origin branch_to_publish:refs/heads/branch_to_publish
git fetch origin
git branch -u origin/branch_to_publish branch_to_publish
git checkout branch_to_publish

減少 Merge branch 'master' of XXX 這種多餘節點

加上--rebase :

git pull --rebase

若在不同的 branch 要接遠端的 master 更新的話,執行:

git pull --rebase origin master

不過使用--rebase是在沒有conflict的情況下使用,它並不會像git merge那樣聰明地處理conflict,原理是rebase並沒有參考parent節點做同步

遇到conflict作法

如果產生了衝突回復 pull 前的狀態 :

git rebase --abort

然候合併code與 master 同步 :

git merge origin master

但這樣就還是會產生 Merge branch 'master' of XXX 節點

更新從別人 fork 過來的 repository

IMAG1680.jpg

git remote add upstream git@...(略)...
git remote -v
git pull upstream master

註 :

git pull upstream master 也等於

git fetch upstream
git merge upstream/master

Fixup and Squash

Fixup a specific commit id

git commit --fixup fb2f677

squash commits into one

git rebase -i HEAD~2
git rebase -i --autosquash ac5db87   (previous commit id)

Mark squash or fixup at the beginning of commits

ref

Troubleshooting

.gitignore not working

git rm -r --cached .
git add .
git commit -m ".gitignore is now working"

git clone error

$ git clone https://github.com/xxx/ccc.git
Cloning into 'conf'...
error: Problem with the SSL CA cert (path? access rights?) while accessing https://github.com/xxx/ccc.git/info/refs
fatal: HTTP request failed

解決 :

git config --global http.sslVerify false

contributing (pull request)

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am ‘Add some feature’)
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Code review best practices

ref: