git

git commandを用いたブランチ操作

How to use git command 1/N

公開日: 2020-12-29
更新日: 2023-05-29

  Table of Contents

Local Branch Operation

List up Repository Branches

Local Branch一覧の取得

1
2
3
4
5
% git branch
  develop
  feature_add_cross_validation
* feature_add_visualize_module
  main

Remote Branch一覧の表示

  • origin/HEADclone した後に作業ディレクトリにチェックアウトするブランチを示したもの
  • git clone後のGitHub のデフォルトのブランチの最新位置に基本的に出現する
1
2
3
4
5
6
7
% git branch -r
  origin/HEAD -> origin/main
  origin/develop
  origin/add_cross_validation
  origin/add_visualize_module
  origin/main
  origin/project/ad_hoc_analysis

Local/Remote Branch一覧の表示

1
2
3
4
5
6
7
8
9
10
11
% git branch -a
  develop
  feature_add_cross_validation
* feature_add_visualize_module
  main
  origin/HEAD -> origin/main
  origin/develop
  origin/add_cross_validation
  origin/add_visualize_module
  origin/main
  origin

List up Branches with the last updatetime

  • branchをcommit-baseの更新日時順(最新が上)で表示する
  • git branchのoptionが利用可能とする
1
% git branch --sort=-committerdate --format='%(refname:short)%09%(committername)%09%(objectname:short)%09%(committerdate:iso-strict)' | tr '\t' '|'| column -s '|' -t

formatについてはgit-for-each-refと同様なものが使用可能です.

--sort=-committerdate branchをcommit更新順(最新が上)にsortする
--sort=committerdate branchをcommit更新順(最新が下)にsortする
%(objectname:short) commit-hashをshortで表示, defaultはlong

なお, .gitconfigにて引数optionが使用可能にするため, 以下のようにalias指定をしている:

1
2
[alias]
    branch-update = "!f(){ git branch --sort=-committerdate --format='%(refname:short)%09%(committername)%09%(objectname:short)%09%(committerdate:iso-strict)' $1| tr '\t' '|'| column -s '|' -t;};f"

Switch/Create Branch Locally

  • ブランチ移動はgit switchコマンドを用いる
  • git checkoutでも可能だが, 公式は git switch 推奨
  • ブランチ作成 & 移動したい場合は -c オプションを加える
1
2
3
4
% git switch existing_branch
Switched to branch 'existing_branch'
% git switch -c non_existing_branch
Switched to a new branch 'non_existing_branch'

作成されていないブランチを指定しまった場合

1
2
% git switch test
fatal: invalid reference: test

作成済みのブランチ名を指定してgit switch -cの場合

1
2
% git switch -c non_existing_branch
fatal: a branch named 'non_existing_branch' already exists

Create a New Branch Locally from a Specified Branch

Syntax

1
% git switch -c <new branch> <rerefence branch>
  • <rerefence branch>はremote branchも指定可能

Example

1
2
3
4
5
% git switch -c foo2 foo
Switched to a new branch 'foo2'
% git switch -c hoge2 origin/hoge
branch 'hoge2' set up to track 'origin/hoge'.
Switched to a new branch 'hoge2'

Rename a Local Branch Name

git branchコマンドと -m オプションを用いてブランチ名を変更することが出来ます.

1
% git branch -m <new_branch_name>

Delete a Local Branch

  • Upstream branchに編集記録が最新版までmergeされているローカルブランチを消去する場合に使用可能
1
2
3
4
% git branch --delete non-existing-branch
Deleted branch non-existing-branch (was 1d6ed41).
% git branch -d <branch> 
Deleted branch <branch> (was 1d6ed42).

複数の場合は

1
% git branch --delete <branch> <branch> <branch>

upstream branchにmergeされていない場合は次のようなエラーが返ってくる

1
2
3
% git branch -d hoge
error: The branch 'hoge' is not fully merged.
If you are sure you want to delete it, run 'git branch -D hoge'.

強制的に消去したい場合は

1
2
% git branch -D hoge
Deleted branch hoge (was eb79802).

Restore a branch after its deletion in Git

What I Want

  • 誤って削除してしまったbranchをlocalで復旧させたい
  • branchを削除してから別ブランチですでに作業を開始してしまっている状況を考える
  • 復元後も, 昔の作業履歴が確認できるようにしたい

How to Solve the problem

  • git reflogは, すでに消去された履歴自体も確認できるコマンド
  • git reflogで消去してしまったbranchからcheckoutしたcommit-hash
  • 上記で取得したcommit-hashを参照する形でgit switch -cを実行

Example

non_existing_branchというbranchを消去してしまったあと, main branchで作業を開始してし まった状況を考えます.

main branchへ移動した直後にnon_existing_branchを消去してしまいます.

1
2
% git switch main
% git branch --delete non_existing_branch

消去したあと, main branchで適当に作業します

1
2
3
4
5
% touch hoge.txt
% echo unkounko > hoge.txt
% git add hoge.txt
% git commit -m "DOC: Add hoge.txt about the family secret"
% git push

このタイミングで, ``non_existing_branchを消去するのはやばかったと発覚しました. 慌てずに, git reflog`コマンドでブランチ消去直前のcommit-hashを確認します.

1
2
3
% git reflog
c65bac9 (HEAD -> main, origin/main) HEAD@{0}: commit: DOC: Add hoge.txt about the family secret
9fb60c5 HEAD@{1}: checkout: moving from non_existing_branch to main

checkoutタイミングの履歴が9fb60c5で残っていることがわかります. なお, このcommit-hashはgit logでは確認できません( = git refloggit logの違い)

最後にこのcommit-hashを用いて, branchを復元します.

1
% git switch -c non_existing_branch 9fb60c5

Check the Upstream Branch

What I Want

  • local repositoryとupstream branchの対応関係を知りたい

How

1
2
3
4
5
6
7
8
% git branch -vv
branch_A            214c991 [origin/branch_A: ahead 1, behind 3] FIX readme
  branch_B            a387619 [origin/non_existing_branch] add yml
  hoge                f5663c5 [origin/hoge] hoge test
* hoge2               4a1e30f Update hoge2.txt
  hoge3               376ef7c [origin/hoeg3: gone] add hoge
  main                16a340c [origin/main] a
  non_existing_branch a387619 [origin/non_existing_branch] add yml
  • 対応していないブランチ(上ではhoge2)では [origin/<remote branch name>]が出力されない

Set up an upstream branch to a local branch

What I Want

  • upstream branchが設定されていないlocal branchに対して, upstream branchを指定したい

How

1
2
% git branch -u <remote branch>
% git branch <local branch> -u <remote branch>
  • <local branch>を省略した場合は, 現在のbranchに対してupstream branchを設定する

Check the Nearest Branch

What I Want

  • カレントブランチの派生元ブランチ名を取得する
  • Merge後は, 直近のMerge元を派生元ブランチとして参照する

Setup

.gitconfigファイルにてエイリアスを以下のように設定

1
2
[alias]
	nearest = "!git show-branch  -a| grep '*' | grep -v `git rev-parse --abbrev-ref HEAD`| head -n1| sed 's/.*\\[\\(.*\\)\\].*/\\1/'| sed 's/[\\^~].*//' #"
  • !はシェルコマンドを入力しますよ, という指示語

How to Use it

  • developブランチから派生したfeature_modelブランチに現在いるとする
  • feature_modelブランチの派生元ブランチとしてdevelopブランチ名が出力されてほしい
1
2
3
4
5
6
7
% git branch
  develop
  feature_cv
* feature_model
  main
% git nearest
develop

Local Branch Operation: Compare and Merge

Compare the current branch with the Selected Branches

What I Want

  • 選択したブランチと比較した時, conflictを引き起こすファイル及び差分箇所を確認したい
  • コマンド実行中に, ファイルの編集も実現可能

Requirements

.gitconfigに以下のラインを追記

1
2
3
4
[diff]
	tool = vscode
[difftool "vscode"]
	cmd = code --wait --diff $LOCAL $REMOTE

How

1
% git difftool <the selected branch> <path>
  • 左側にthe selected branch, 右側にthe current branchのファイルが表示される
  • the current branchのファイルは編集 & 保存が可能
  • <path>指定時は, その対象ファイルのみの差分を表示
  • <path>を指定しなかった場合は, 差分全てを表示

Undo a Merge Commit

What I Want

  • merge commitを取り消す

How

merge commitを取り消すには2つの方法があります:

方針 効果
git reset --merge Branch Historyを強制的に書き換える(= merge commitの痕跡を履歴から消す)
git revert merge変更自体は打ち消すが, merge commitの履歴は残る

本来mergeされるべきではなかったbranchがmergeされたときに実施するundoなので, 基本的には git reset --mergeを用いることが良いと考えています. 実際に, Linusも次のように言っています:

1
2
3
4
5
6
7
8
9
10
11
12
13
Reverting a regular commit just effectively undoes what that commit did, and is fairly straightforward. 
But reverting a merge commit also undoes the _data_ that the commit changed, 
but it does absolutely nothing to the effects on _history_ that the merge had.

So the merge will still exist, and it will still be seen as joining the two 
branches together, and future merges will see that merge as the last shared 
state – and the revert that reverted the merge brought in will not affect that at all.

So a "revert" undoes the data changes, but it's very much not an "undo" in the 
sense that it doesn't undo the effects of a commit on the repository history.

So if you think of "revert" as "undo", then you're going to always miss this 
part of reverts. Yes, it undoes the data, but no, it doesn't undo history.

How to do git reset --merge in order to undo the merge

1
% git reset --merge <commit-hash>

git reset –mergeはtracked filesに加えられたuncommitedな変更についての情報を残してくれるため, 全てを参照地点までの情報まで戻してしまうgit reset --hardに対してsafer versionと一般的に言われています.

documentを確認してみると,

1
2
3
4
5
6
7
8
--hard    Matches the working tree and index to that of the tree being
               switched  to. Any changes to tracked files in the working tree since
               <commit> are lost.

--merge
              Resets the index to match the tree recorded by the named commit, and
              updates the files that are different between the named commit and
              the current commit in the working tree.

Check the difference between --hard and --marge

--hard--margeはtracked fileのuncommittedな変更について情報を残すか残さないかの違いです. それを確認するため以下の状況を作ります:

  1. mainmerge_testの2つのbranch
  2. mainmerge_testをmergeする前に、tracked-fileのREADME.mdに「good bye」という文字列を加える
  3. good byeをstashしてから, merge_testで適当に作業, mainへ戻る
  4. good byeをstashからpopしてからmerge_testmainへmerge
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
% mkdir test
% cd ./test
% git init
% touch README.md
% git commit -m "initial commit"
% git switch -c merge_test
% touch merge_log.txt
% echo test > merge_log.txt
% git add merge_log.tx
% git commit -m "DOC: Additing merge_log"
% git switch main
% echo hello world > README.md
% git add README.md
% git commit -m "DOC: updating README"
% echo good bye >> README.md
% git stash
% git switch merge_test
% echo 'hope this line will disappear'
% echo 'hope this line will disappear' > merge_log.txt
% git add merge_log.txt
% git commit -m "DOC: updating merge_log"
% git switch main
% git stash pop
% git merge merge_test

このとき, --hard--margeで以下のような挙動の違いが発生します:

1
2
3
4
5
## stashされたgood byeは残る
% git reset --merge ORIG_HEAD

## stashされたgood byeが消える
% git reset --hard ORIG_HEAD

Remote Branch Operation

Get/Switch to a remote branch: git switch version

What I Want to Do

  • リモートブランチ(project/adhoc_analysis)をローカルにチェックアウトしたい

How

  1. リモートの追跡ブランチを更新
  2. git switch (リモート側にすでにブランチが存在するので-cは必要なし)
1
2
3
4
5
6
7
8
9
% git branch -r
  origin/HEAD -> origin/main
  origin/develop
  origin/add_cross_validation
  origin/add_visualize_module
  origin/main
  origin/project/ad_hoc_analysis
% git fetch --all
% git switch project/adhoc_analysis

REMARKS

  • 内部的にLocal側に新しくリモートと同じブランチ名のブランチが作成, からのswitchとなる

Get/Switch to a remote branch: git fetch version

What I Want to Do

  • リモートブランチ(project/adhoc_analysis)をローカルにbranch nameを指定して取り込みたい

How

1
% git fetch <remote> <remote-branchname>:<local-branchname>

Delete Remote Branch

1
2
3
% git push <remote> --delete <branch>
## 同義
% git push <remote> :<old_branch_name>
  • コロン(=:)の前に何も指定しないことで,「空」をpushするという挙動になる

複数の場合

1
% git push origin --delete <branch1> <branch2> <branch3>

Refresh the list of remote branches

What I Want

  • To update the local list of remote branches

How: remote updateで実施する場合

1
% git remote update origin --prune

How: fetchで実施する場合(推奨)

1
% git fetch -p 

Rename Remote Branch

Syntax

1
2
% git switch -c <new_branch_name> <old_branch_name>
% git push <remote> :<old_branch_name> <new_branch_name>

REMARKS

  • branchのrenameというよりかは, ローカルで別名で作った同一内容ブランチをremoteへ反映し直すという挙動
  • remote branchの消去と新しいbranchのpushを同時に実施しているだけ

Remote Branch Operation: git clone

git clone to a specified folder

1
% git clone <repo> <directory>
  • <repo>: リポジトリURL
  • <directory>: レポジトリ内容を格納するローカル上の空フォルダ
    • 事前に空フォルダを作成する必要はない
    • パスには絶対パス, 相対パスどちらでも指定可能

REMARKS

directoryを中身の入ったフォルダを指定すると以下のようなerrorが返ってくる:

1
2
% git clone hogehoge pokochin
fatal: destination path 'pokochin' already exists and is not an empty directory.

git clone a specified branch only

1
% git clone -b <branch> <repo>
  • <branch>: branch name
  • <repo>: リポジトリURL
  • -b--branch optionのこと

Install Private Python Packages via git clone

  • : installしたいversionを指定
  • : レポジトリのローカルにおける保存先
1
% git clone -b <branch-name> <repository url> <local-folder-path>

Then, pip経由の場合は編集可能性を考慮して -e オプションを付与してインストール

1
% pip install -e <folder-name>

Poetry経由の場合は

1
% poetry add --editable <folder-name>

References

git config

git sparse checkout

git branch operation tips



Share Buttons
Share on:

Feature Tags
Leave a Comment
(注意:GitHub Accountが必要となります)