📚

git コマンド 利用シーン別まとめ

2021/02/07に公開
1

ライセンス

この文書は CC 3.0 (BY-NC-SA) である.

表記,語句,確認環境

  • コマンド :: この文書ではもっぱら git <command> を指す.
  • Git :: バージョン管理アプリケーション. 差分管理ではなく,ファイルシステムに近い.
  • Git project :: 管理しようとしている全体のルートディレクトリ. 実体として, repositoryとworking areaを持つ.
  • repository :: Git管理情報が入ってるGit project直下の .gitディレクトリ.
  • working area :: Git projectのGit管理情報以外のユーザーファイルとディレクトリ.
  • staging area :: repositoryの1ファイルだが, 現在のファイル状態や次の記録の対象ファイルが閲覧できる管理上のスペース.
  • remote :: 通常オンライン上に用意された(だいたい全ての)localが参照するrepository.
  • local :: (おおよそオフラインの)自分のマシンに設置してるrepository.
  • ホスティングサービス :: GitHubBitbucketなど. remoteの置き場所として使われる. 自前構築も可能ではある.
  • commit :: Gitにおける変更の記録単位.
  • branch :: 単純にはcommitの履歴. 大抵, 履歴の本流をmasterとして, あるcommitから支流を作り, 作業したら本流へ戻す.
  • merge :: あるbranchの変更を別のbranchへまとめること.
  • tag :: Gitのある履歴地点への目印.

参考にした日本語版 Pro Git [1] にてカタカナで表記されていたものでも, gitコマンドのオプションやパラメータとして頻出するものはあえて英字(リモート => remote等)に直している.

shellコマンド表記において, だいたい行頭の $ は一般ユーザー権限のコマンドプロンプトである. また, # はroot権限のコマンドプロンプトである. まれにこれらの前に /tmp などがついて, ~$/tmp# となっていた場合, $ , # のさらに行頭側にあるのはカレントディレクトリ位置である. すなわち, "一般ユーザーのホームディレクトリから/tmpに移って,rootになって,また一般ユーザーに戻る"が次である.

~$ cd /tmp
/tmp$ sudo su
/tmp# exit
exit

なおshell scriptでは # でコメントアウトを示すが, 本文書にはその用途で出てくることはない. また,この文書においてsehllコマンド実行前に export LC_ALL=C; set -o posix を設定してから行うように努めている. さらに,一部コマンド実行時に必要,または,表示される個人情報部分はこの文書作成時に置きかえている. 置きかえた後の文字列と本来そこにある情報の対応は次のとおり.

  • user.name :: gitの各種コマンドを実行した,または,記録されてるユーザー名. Linuxマシンのlogin名と必ずしも一致しない.
  • user.email :: gitの各種コマンドを実行した,または,記録されてるユーザーのemailアドレス.

動作確認に用いた環境はLinux(Ubuntu)の以下のとおりである.

$ cat /proc/version
Linux version 5.4.0-64-generic (buildd@lcy01-amd64-021) (gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)) #72-Ubuntu SMP Fri Jan 15 10:27:54 UTC 2021
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.1 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.1 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
$ git --version
git version 2.25.1

利用シーン

一覧

  1. 作成
  2. いつでも
  3. 作業中
  4. 未分類

localを新規作成する

  1. 管理予定のprojectディレクトリを作成する.
  2. 移動.
  3. git init 実行.
$ mkdir GitProject
$ cd GitProject/
GitProject$ git init
Initialized empty Git repository in GitProject/.git/

新規localをremoteに上げる

状況は次のとおり.

GitProject$ git log --oneline
7fdbadb (HEAD -> master) init.

GitProject$ git show 7fdbadb
commit 7fdbadb99e98a202d66863173b869f6f7c95751a (HEAD -> master)
Author: user.name <user.email>
Date:   Sat Jan 30 17:11:58 2021 +0900

    init.

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1cc357c
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+Git Project
\ No newline at end of file

remoteの保管場所は例ではGitHubを用いる. ホスティングサービス側であらかじめrepositoryの場所を新規作成しておく. localでは git remote add <remoteの登録名> <remoteのURL>git push -u <remoteの登録名> <localのbranch> を行う. remoteの登録名はoriginと付けることが多い. また, remoteにはまだbranchが無いため -u (--set-upstream) オプションでremoteにlocalのbranchを新規作成させつつ, そのlocalのbranchの履歴をアップロードする. [2] 新規localのデフォルトのbranchは大体masterとなっている. [3]

GitProject$ git remote add origin git@github.com:user.name/GitProject.git
GitProject$ git push -u origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 220 bytes | 220.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:user.name/GitProject.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

remoteを落としてくる

git clone <remoteのURL> でカレントディレクトリにremoteを落とせる. project名はremoteの名前そのままである.

$ git clone git@github.com:user.name/GitProject.git
Cloning into 'GitProject'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
$ ls
GitProject
$ cd GitProject/
GitProject$ ls
README.md

git clone <remoteのURL> <新規ディレクトリ (local project名)> とすればproject名をremoteと別名にして作れる.

$ git clone git@github.com:user.name/GitProject.git GitProjectConflict
Cloning into 'GitProjectConflict'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
$ ls
GitProjectConflict
$ cd GitProjectConflict/
GitProjectConflict$ ls
README.md

workingの状態を見る

何も変更などが無いとき.

GitProject$ git status
On branch master
nothing to commit, working tree clean
  1. 新規作成したファイルがあり, それをまだ記録対象にしてないとき.
  2. git add して記録対象に加える.
  3. 新規作成したファイルがあり, それを記録対象にしているとき.
GitProject$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
   README.md

nothing added to commit but untracked files present (use "git add" to track)
GitProject$ git add README.md
GitProject$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   README.md

変更を比較する

git diff を行う. 無指定なら, workingと直前commitとの差分が見れる. 新規作成ファイルは出てこない. git diff <source commit> <target commit> とすれば, source-targetの間の差分が見れる.

  1. 事前状態
  2. git diff
GitProject$ git status
On branch feature/Conflict
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
   modified:   store.txt

no changes added to commit (use "git add" and/or "git commit -a")
GitProject$ git diff
diff --git a/store.txt b/store.txt
index 1506f4c..65552e0 100644
--- a/store.txt
+++ b/store.txt
@@ -1,2 +1,5 @@
 c729a9dd-ac15-49f9-863b-5166697b1718
 7de50b4f-6fda-4b97-89ef-f6214a688938
+52058625-709c-495b-9fad-63f3bf9f4a13
+7b9480d1-ca63-4cb7-bdaa-1ecac5585637
+5a7cb7af-95d9-4706-8bd1-2914810e9a5b

リダイレクトでそのままdiffファイルとすることもできるため, その後patchで適用とかもできる.

remoteの状態を落としてくる

git fetch を行う. workingがどのbranchだろうが, remoteの全履歴を落としてくる.

GitProject$ git fetch
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
Unpacking objects: 100% (1/1), 619 bytes | 619.00 KiB/s, done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
From github.com:user.name/GitProject
   7fdbadb..ce572e0  master     -> origin/master

変更履歴を見る

git log を行う. さらにオプションの指定方法で異なる詳細度で並べることができる. オプションでどの程度のことを見るかを指定できる. 筆者はよく --oneline または, --oneline --all を利用する. --all無しでは,今のworkingに至るまでのcommitを表示する. --allでは全てのcommitを表示する.

  1. git log --oneline
  2. git log --oneline --all
GitProject$ git log --oneline
873fd7c (HEAD -> feature/Conflict) update store.txt.
03dcd8c add <date>.txt and squashed.
17a48d9 add <date>.txt.
48fabb7 add <date>.txt.
a8de3e9 (tag: lightweight) add <date>.txt.
9e5290b (origin/develop, develop) Merge branch 'feature/AddStoreFile' into develop
93bfb5f (origin/feature/AddStoreFile, feature/AddStoreFile) add <date>.txt.
24b27fa add <date>.txt.
581fe4a add the <date>.txt for exsample.
b9711dd add store.txt.
7fdbadb (tag: annotated) init.
GitProject$ git log --oneline --all
873fd7c (HEAD -> feature/Conflict) update store.txt.
03dcd8c add <date>.txt and squashed.
17a48d9 add <date>.txt.
48fabb7 add <date>.txt.
2964aa4 (refs/stash) WIP on Conflict: a8de3e9 add <date>.txt.
5e9a8c9 index on Conflict: a8de3e9 add <date>.txt.
a8de3e9 (tag: lightweight) add <date>.txt.
ce572e0 (origin/master, master) Merge branch 'develop' into master.
9e5290b (origin/develop, develop) Merge branch 'feature/AddStoreFile' into develop
93bfb5f (origin/feature/AddStoreFile, feature/AddStoreFile) add <date>.txt.
24b27fa add <date>.txt.
581fe4a add the <date>.txt for exsample.
b9711dd add store.txt.
7fdbadb (tag: annotated) init.

いわゆるGUIツールの見た目にもっとも近いのは次のオプション指定だろう.

  1. git log --oneline --decorate --graph --all
GitProject$ git log --oneline --decorate --graph --all
* 873fd7c (HEAD -> feature/Conflict) update store.txt.
* 03dcd8c add <date>.txt and squashed.
* 17a48d9 add <date>.txt.
* 48fabb7 add <date>.txt.
| * 2964aa4 (refs/stash) WIP on Conflict: a8de3e9 add <date>.txt.
|/|
| * 5e9a8c9 index on Conflict: a8de3e9 add <date>.txt.
|/
* a8de3e9 (tag: lightweight) add <date>.txt.
| *   ce572e0 (origin/master, master) Merge branch 'develop' into master.
| |\
| |/
|/|
* |   9e5290b (origin/develop, develop) Merge branch 'feature/AddStoreFile' into develop
|\ \
| |/
|/|
| * 93bfb5f (origin/feature/AddStoreFile, feature/AddStoreFile) add <date>.txt.
| * 24b27fa add <date>.txt.
| * 581fe4a add the <date>.txt for exsample.
| * b9711dd add store.txt.
|/
* 7fdbadb (tag: annotated) init.

ある変更の内容を見る

git show <commit> を行う. commitはいくつかのパターンで指定できる.

  1. 変更履歴確認.
  2. 指定をcommitの番号(チェックサムの先頭7文字以上で可能)でしたとき.
GitProject$ git log --oneline
b9711dd (HEAD -> feature/AddStoreFile) add store.txt.
7fdbadb (origin/master, master, develop) init.
tmp/GitProject$ git show b9711dd
commit b9711dd8ae85cc32d758891b2c7988fcf09e7a26 (HEAD -> feature/AddStoreFile)
Author: user.name <user.email>
Date:   Sun Jan 31 14:00:15 2021 +0900

    add store.txt.

    The content written in sotre.txt is the date information when the editing was performed.

diff --git a/store.txt b/store.txt
new file mode 100644
index 0000000..dddab29
--- /dev/null
+++ b/store.txt
@@ -0,0 +1 @@
+c729a9dd-ac15-49f9-863b-5166697b1718

repositoryにあるbranchを見る

git branch --list を行う. 行頭アスタリスク*は working areaがどのbranchであるかを示す.

GitProject$ git branch --list
* master

branchを作る

git branch <新規branch名> を行う. ただ, "どんな名前をつけるか"によって日々の編集作業の(特に複数人での)管理や,中~長期におけるコンテンツ(プロダクト)管理に直結させる考えが主流である. 作成するbranchは今のworking areaにとっての直前commitを派生の根本とする. この派生についてはより詳しいことを後述する.

  1. 事前のbranch状況
  2. git branch develop でdevelop branchを追加.
  3. 追加した後のbranch状況.
GitProject$ git branch --list
  deadBranch
* master
GitProject$ git branch develop
GitProject$ git branch --list
  deadBranch
  develop
* master

また, git branch -d <branch> で指定したbranchを削除する.

GitProject$ git branch --list
  deadBranch
  develop
* master
GitProject$ git branch -d deadBranch
Deleted branch deadBranch (was 7fdbadb).
GitProject$ git branch --list
  develop
* master

なお, 筆者自身はbranchを作るときは後述のbranch切り替えと同時に行うため, git branch で作ることはあまりない.

branchを切り替える

git checkout <branch> を行う.

また, git checkout -b <新規branch名> でbranchを新規作成しながら切り替えられる.
このとき作成するbranchは今のworking areaにとっての直前commitを派生の根本とする. この派生についてはより詳しいことを後述する.

  1. 事前状態
  2. developへ切り替え
  3. 事後状態
GitProject$ git branch --list
  develop
* master
GitProject$ git checkout develop
Switched to branch 'develop'
GitProject$ git branch --list
* develop
  master
  1. 事前状態
  2. feature/AddStoreFileを作成して, 切り替え
  3. 事後状態
GitProject$ git branch --list
* develop
  master
GitProject$ git checkout -b feature/AddStoreFile
Switched to a new branch 'feature/AddStoreFile'
GitProject$ git branch --list
  develop
* feature/AddStoreFile
  master

repositoryを過去のある変更地点へ切り替える

一番ピンポイントに飛ぶには git checkout <commit> となるかな. この飛びかたを実行した後飛んだ先で変更をする場合にはbranchを作ること.

  1. 飛びさき確認
  2. git checkout <commit>(a8de3e9へ飛ぶこととした.)
  3. 事後状態(log)
  4. 事後状態(working area)
GitProject$ git log --oneline
947b4b4 (HEAD -> feature/Conflict) ad <date>.txt.
a8de3e9 add <date>.txt.
9e5290b (origin/develop, develop) Merge branch 'feature/AddStoreFile' into develop
93bfb5f (origin/feature/AddStoreFile, feature/AddStoreFile) add <date>.txt.
24b27fa add <date>.txt.
581fe4a add the <date>.txt for exsample.
b9711dd add store.txt.
7fdbadb init.
GitProject$ git checkout a8de3e9
Note: switching to 'a8de3e9'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at a8de3e9 add <date>.txt.
GitProject$ git log --oneline
a8de3e9 (HEAD) add <date>.txt.
9e5290b (origin/develop, develop) Merge branch 'feature/AddStoreFile' into develop
93bfb5f (origin/feature/AddStoreFile, feature/AddStoreFile) add <date>.txt.
24b27fa add <date>.txt.
581fe4a add the <date>.txt for exsample.
b9711dd add store.txt.
7fdbadb init.
GitProject$ git status
HEAD detached at a8de3e9
nothing to commit, working tree clean

作業した内容を変更の対象にする

git add <file> を行う. なお, 事前に git status でどのファイルが追加されたか, どのファイルに変更があるかを確認しておくとよい.

  1. 例のためstore.txt作成. [4]
  2. 事前状態
  3. git add store.txt
  4. 事後状態
$ uuid -v 4 >> store.txt
GitProject$ git status
On branch feature/AddStoreFile
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    store.txt

nothing added to commit but untracked files present (use "git add" to track)
GitProject$ git add store.txt
GitProject$ git status
On branch feature/AddStoreFile
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   store.txt

また, git restore -S <file> で変更対象から外せる. [2:1] [5]

  1. 事前状態
  2. git restore -S store.txt
  3. 事後状態
GitProject$ git status
On branch feature/AddStoreFile
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   store.txt
GitProject$ git restore -S store.txt
desertglass@desertglass-desktop:/tmp/GitProject$ git status
On branch feature/AddStoreFile
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    store.txt

変更を記録する

メッセージが1行だけでよいならば git commit -m <message> でメッセージを与えつつ, 変更の記録ができる.

GitProject$ git commit -m "init."
[master (root-commit) 7fdbadb] init.
 1 file changed, 1 insertion(+)
 create mode 100644 README.md

1行でなくとも -m <message> を複数つけると, そのメッセージの間に空行が挿入して, メッセージを連結したものが記録される. 1メッセージ1パラグラフという考え方のようだ. [2:2]

  1. 事前状態
  2. 変更登録
  3. git commit -m <message 1> -m <message 2>
  4. 変更履歴確認
  5. 3番の変更の内容表示.
GitProject$ git status
On branch feature/AddStoreFile
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   store.txt

GitProject$ git commit -m "add store.txt." -m "The content written in sotre.txt is the date information when the editing was performed."
[feature/AddStoreFile b9711dd] add store.txt.
 1 file changed, 1 insertion(+)
 create mode 100644 store.txt
GitProject$ git log --oneline
b9711dd (HEAD -> feature/AddStoreFile) add store.txt.
7fdbadb (origin/master, master, develop) init.
tmp/GitProject$ git show b9711dd
commit b9711dd8ae85cc32d758891b2c7988fcf09e7a26 (HEAD -> feature/AddStoreFile)
Author: user.name <user.email>
Date:   Sun Jan 31 14:00:15 2021 +0900

    add store.txt.

    The content written in sotre.txt is the date information when the editing was performed.

diff --git a/store.txt b/store.txt
new file mode 100644
index 0000000..dddab29
--- /dev/null
+++ b/store.txt
@@ -0,0 +1 @@
+c729a9dd-ac15-49f9-863b-5166697b1718

git commit だけで行った場合, メッセージ記入のエディタが立ち上がり, それでメッセージ入力を行う. どのエディタが立ち上がるかは環境設定に依存する. 書くときには,

  1. 1行目で何を行ったかを端的に記述し,
  2. 空行を挟んで3行目からより詳しい内容を記述する,

ことを強く推奨する. というのも, 巷にある多くのgitをより便利に利用するためのアプリケーションが, 1行目を簡易説明として, それ以外を詳細説明として, 表示するからである. なお, エディタ入力で行頭が # で始まる行は無視されて記録に反映される.

  1. 例のため <日付>.txtを作成. [4:1]
  2. 変更登録
  3. git commit
  4. 入力エディタ立ち上がり. (一端区切る)
  5. 入力後のエディタ. (一端区切る)
  6. 入力確定した後.
  7. 変更履歴確認.
  8. 変更内容確認.
GitProject$ uuid -v 4 > $(date -u +%Y-%m-%dT%H:%M:%S).txt
GitProject$ git add 2021-01-31T06\:15\:44.txt
GitProject$ git commit
  GNU nano 4.8                                                                         /tmp/GitProject/.git/COMMIT_EDITMSG                                                                                    

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch feature/AddStoreFile
# Changes to be committed:
#       new file:   2021-01-31T06:15:44.txt
#


                                                                                               [ Read 8 lines ]
^G Get Help      ^O Write Out     ^W Where Is      ^K Cut Text      ^J Justify       ^C Cur Pos       M-U Undo         M-A Mark Text    M-] To Bracket   M-Q Previous     ^B Back          ^Left Prev Word
^X Exit          ^R Read File     ^\ Replace       ^U Paste Text    ^T To Spell      ^_ Go To Line    M-E Redo         M-6 Copy Text    ^Q Where Was     M-W Next         ^F Forward       ^Right Next Word
  GNU nano 4.8                                                                         /tmp/GitProject/.git/COMMIT_EDITMSG                                                                          Modified  
add the <date>.txt for exsample.

The content written in the <date>.txt is UUID4 when the editing was performed.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch feature/AddStoreFile
# Changes to be committed:
#       new file:   2021-01-31T06:15:44.txt
#


^G Get Help      ^O Write Out     ^W Where Is      ^K Cut Text      ^J Justify       ^C Cur Pos       M-U Undo         M-A Mark Text    M-] To Bracket   M-Q Previous     ^B Back          ^Left Prev Word
^X Exit          ^R Read File     ^\ Replace       ^U Paste Text    ^T To Spell      ^_ Go To Line    M-E Redo         M-6 Copy Text    ^Q Where Was     M-W Next         ^F Forward       ^Right Next Word
[feature/AddStoreFile 581fe4a] add the <date>.txt for exsample.
 1 file changed, 1 insertion(+)
 create mode 100644 2021-01-31T06:15:44.txt
GitProject$ git log --oneline
581fe4a (HEAD -> feature/AddStoreFile) add the <date>.txt for exsample.
b9711dd add store.txt.
7fdbadb (origin/master, master, develop) init.
GitProject$ git show
commit 581fe4ad48656206de0436781fcc6658621c3d37 (HEAD -> feature/AddStoreFile)
Author: user.name <user.email>
Date:   Sun Jan 31 15:16:07 2021 +0900

    add the <date>.txt for exsample.

    The content written in the <date>.txt is UUID4 when the editing was performed.

diff --git a/2021-01-31T06:15:44.txt b/2021-01-31T06:15:44.txt
new file mode 100644
index 0000000..28981f7
--- /dev/null
+++ b/2021-01-31T06:15:44.txt
@@ -0,0 +1 @@
+66c00ad6-d69a-4ffc-ac64-72e381b48af1

remoteの変更を取り込む

ここではremoteと同期することの意とする. 同期はbranch単位で行うため, git chekcout <branch>で同期したいbranchに切り替えてから git pull を行う. 以前にremoteと同期してから, "localを変更し, remoteでも(なぜか)変更が発生している" 場合, 衝突が発生する.

衝突したらとりあえず remoteの状態だけ取得して local側の変更を元に戻したり退避したりしてから git pull して 退避した変更を再度適用する. この再適用も量が嵩んでいると修正が大変なので, そもそもまめに同期しておくことが望ましい. 衝突をどうにかする も気休め程度の参考とする.

  1. git checkout master
  2. git fetch
  3. git pull
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
GitProject$ git fetch
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
Unpacking objects: 100% (1/1), 619 bytes | 619.00 KiB/s, done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
From github.com:user.name/GitProject
   7fdbadb..ce572e0  master     -> origin/master
GitProject$ git pull
Updating 7fdbadb..ce572e0
Fast-forward
 2021-01-31T06:15:44.txt | 1 +
 2021-01-31T06:55:00.txt | 1 +
 2021-01-31T06:56:20.txt | 1 +
 store.txt               | 1 +
 4 files changed, 4 insertions(+)
 create mode 100644 2021-01-31T06:15:44.txt
 create mode 100644 2021-01-31T06:55:00.txt
 create mode 100644 2021-01-31T06:56:20.txt
 create mode 100644 store.txt

別のbranchの変更を取り込む

統合先のbranchへcheckoutし, 変更のあったbranchをmergeする. mergeにはfast-forwardとそうでないものがある. fast-forwardは, 変更のあったbranchの派生タイミングから今に至るまで統合先のbranchに変更が一切ない場合, 統合先のbranchの最新を変更のあったbranchに移動させる, というような考え方の統合方法である. むしろ, mergeの場面よりも, pullの場面のほうに感覚が近い. そうでないmergeは, 変更の分を1つのマージコミットと呼ばれるものに固めて, 統合先のbranchに記録するものである.

取り込みたいbranchが巨大な変更とかしてると大抵衝突するので, そもそもまめに取り込むことを推奨する. mergeの場面でfast-forwardとそうでないもののどちらにするかは好みや運用のルールである. 多分, そうでないほうを選択しておいたほうが無難.

mergeの際にfast-forwardでないもので統合するとき, (結局それはcommitすることと同義であるから), commitのメッセージと同じようにメッセージを与える. -m オプションを使用できるし使用しないこともできる. メッセージの記述, 入力方法もcommitのメッセージと同じである. ただし, -m オプションなしで実行しメッセージ入力画面になると, デフォルトのmergeした旨のメッセージが自動挿入されている. よって, そのまま確定してしまうこともできる.

  1. git checkout develop
  2. `git merge feature/AddStoreFile (一端区切る)
  3. メッセージ入力画面 (一端区切る)
  4. そのままのメッセージで確定後
  5. 変更履歴確認
GitProject$ git checkout develop
Switched to branch 'develop'
GitProject$ git merge feature/AddStoreFile
  GNU nano 4.8                                                                            /tmp/GitProject/.git/MERGE_MSG                                                                                      
Merge branch 'feature/AddStoreFile' into develop
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.


                                                                                               [ Read 6 lines ]
^G Get Help      ^O Write Out     ^W Where Is      ^K Cut Text      ^J Justify       ^C Cur Pos       M-U Undo         M-A Mark Text    M-] To Bracket   M-Q Previous     ^B Back          ^Left Prev Word
^X Exit          ^R Read File     ^\ Replace       ^U Paste Text    ^T To Spell      ^_ Go To Line    M-E Redo         M-6 Copy Text    ^Q Where Was     M-W Next         ^F Forward       ^Right Next Word
Merge made by the 'recursive' strategy.
 2021-01-31T06:15:44.txt | 1 +
 2021-01-31T06:55:00.txt | 1 +
 2021-01-31T06:56:20.txt | 1 +
 store.txt               | 1 +
 4 files changed, 4 insertions(+)
 create mode 100644 2021-01-31T06:15:44.txt
 create mode 100644 2021-01-31T06:55:00.txt
 create mode 100644 2021-01-31T06:56:20.txt
 create mode 100644 store.txt
GitProject$ git log --oneline
9e5290b (HEAD -> develop) Merge branch 'feature/AddStoreFile' into develop
93bfb5f (origin/feature/AddStoreFile, feature/AddStoreFile) add <date>.txt.
24b27fa add <date>.txt.
581fe4a add the <date>.txt for exsample.
b9711dd add store.txt.
7fdbadb (origin/master, master) init.

衝突をどうにかする

一番よく起こるのは誰かの変更が入ったdevelopを自分の変更に取りこむときだろうか. 地道に解決すれば怖くない. ただ, 巨大な量だと単純に物量がしんどいので, そんなに巨大にならないようにこまめにmergeしておいた方が良い.

衝突の解決はシンプルには衝突箇所を手動で直すのが確実だと思う. 善人的には相手の(取り込んできた)変更を全て受け入れて自分の変更を破棄する, という手段もある. もっとも, その場合は多分同じ修正を再びほどこすことになると思う.

  1. git merge develop でのコンフリクト発生.
  2. git status 衝突ファイルの確認. Unmerged paths: 以下が衝突したファイル. (一端区切る)
  3. 衝突解消前. (一端区切る)
  4. 衝突解消後. (一端区切る)
  5. git add <file> でstagingに衝突解消を実施したとmarkする.
  6. git commit でmergeを完了させる. (message入力になるけど省略)
GitProject$ git merge develop
Auto-merging store.txt
CONFLICT (content): Merge conflict in store.txt
Automatic merge failed; fix conflicts and then commit the result.
GitProject$ git status
On branch feature/AddStoreFile
Your branch is ahead of 'origin/feature/AddStoreFile' by 1 commit.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Changes to be committed:
    new file:   2021-01-31T09:46:36.txt
    new file:   2021-01-31T09:46:38.txt
    new file:   2021-01-31T10:03:33.txt
    new file:   2021-02-07T03:53:43.txt
    new file:   2021-02-07T03:53:44.txt
    new file:   2021-02-07T04:39:56.txt

Unmerged paths:
  (use "git add <file>..." to mark resolution)
    both modified:   store.txt

衝突したstore.txtの中身.
<<<<<<< HEAD, =======, >>>>>>> develop の区間が衝突範囲.
この場合, <<<<<<< HEAD, ======= にあるのが自分のworkingで,
=======, >>>>>>> develop にあるのがdevelopの内容.

c729a9dd-ac15-49f9-863b-5166697b1718
<<<<<<< HEAD
68f17561-14fb-4722-9ed9-7bc3e61fb3cf
68da98e5-23d6-4706-a119-d76cd0b3e7bd
=======
7de50b4f-6fda-4b97-89ef-f6214a688938
52058625-709c-495b-9fad-63f3bf9f4a13
7b9480d1-ca63-4cb7-bdaa-1ecac5585637
5a7cb7af-95d9-4706-8bd1-2914810e9a5b
>>>>>>> develop

両方(自分とdevelop)を両方保持するように修正.
つまり, <<<<<<< HEAD, =======, >>>>>>> develop を消したもの.

c729a9dd-ac15-49f9-863b-5166697b1718
68f17561-14fb-4722-9ed9-7bc3e61fb3cf
68da98e5-23d6-4706-a119-d76cd0b3e7bd
7de50b4f-6fda-4b97-89ef-f6214a688938
52058625-709c-495b-9fad-63f3bf9f4a13
7b9480d1-ca63-4cb7-bdaa-1ecac5585637
5a7cb7af-95d9-4706-8bd1-2914810e9a5b
GitProject$ git add store.txt
GitProject$ git commit
[feature/AddStoreFile 5aca1a0] Merge branch 'develop' into feature/AddStoreFile

変更をremoteへ上げる

git push を行う. また, working areaとstaging areaを変更が無い状態にして, 上げたいbranchにいる状態のほうが煩雑さがない. さらに事前作業として, remoteの変更を取り込んでおくこと. さもなくば, 七面倒臭い衝突解消をせまられる. もっとも, この段階でそれを行うことは手遅れである. 作業に取りかかる前に取り込んでおいて, クリアな状態のまま上げるのが望ましい.

  1. 事前状態(編集中のものがないことの確認)
  2. git push
GitProject$ git status
On branch feature/AddStoreFile
Your branch is ahead of 'origin/feature/AddStoreFile' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
GitProject$ git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 311 bytes | 311.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:user.name/GitProject.git
   24b27fa..93bfb5f  feature/AddStoreFile -> feature/AddStoreFile

新規にbranchを作ってからまだremoteへそのbranchを上げていない場合, git push -u <remote> <branch> を行う. 新規にbranchしたタイミングから今までの分がまとめて上がる. (-uは--set-upstreamである.)

GitProject$ git push -u origin feature/AddStoreFile
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 764 bytes | 764.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for 'feature/AddStoreFile' on GitHub by visiting:
remote:      https://github.com/user.name/GitProject/pull/new/feature/AddStoreFile
remote:
To github.com:user.name/GitProject.git
 * [new branch]      feature/AddStoreFile -> feature/AddStoreFile
Branch 'feature/AddStoreFile' set up to track remote branch 'feature/AddStoreFile' from 'origin'.

記録前の作業を退避する

git stash push を行う [6] . 既に履歴に登録されているファイルへの変更が退避される. そのままでは新規作成のファイルは退避されない. 新規作成のファイルを退避に含めるには, 先に git add しておいて staging areaに登録しておく. 退避した作業はどのcommitのチェックサムを元に記録される.

  1. 事前状態
  2. git stash list によるstashリスト(退避してる作業)表示
  3. git stash push
  4. git stash list によるstashリスト(退避してる作業)表示
  5. 事後状態
GitProject$ git status
On branch feature/Conflict
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
   new file:   2021-01-31T10:03:33.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
   modified:   store.txt

GitProject$ git stash list
stash@{0}: WIP on Conflict: a8de3e9 add <date>.txt.
GitProject$ git stash push
Saved working directory and index state WIP on Conflict: a8de3e9 add <date>.txt.
GitProject$ git stash list
stash@{0}: WIP on Conflict: a8de3e9 add <date>.txt.
stash@{1}: WIP on Conflict: a8de3e9 add <date>.txt.
GitProject$ git status
On branch feature/Conflict
nothing to commit, working tree clean

git stash push -m <message> とすると, stashの格納リストでメッセージが表示される. つまり, ちょっとだけ何の変更のやつか特定する手掛りになる.

GitProject$ git stash push -m "page2"
Saved working directory and index state On Conflict: page2
GitProject$ git stash list
stash@{0}: On Conflict: page2

退避した作業を元に戻す

git stash apply stash@{<num>} でそのstashを適用する. git stash pop だと直近で退避したstashを適用しながら, そのstashを消す. なお, 退避しているstashの番号は0番がもっとも新しい.

  1. git stash list
  2. git status
  3. git stash apply stash@{1}
  4. git stash list
GitProject$ git stash list
stash@{0}: WIP on Conflict: a8de3e9 add <date>.txt.
stash@{1}: WIP on Conflict: a8de3e9 add <date>.txt.
GitProject$ git status
On branch feature/Conflict
nothing to commit, working tree clean
GitProject$ git stash apply stash@{1}
On branch feature/Conflict
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   2021-01-31T10:03:33.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   store.txt

GitProject$ git stash list
stash@{0}: WIP on Conflict: a8de3e9 add <date>.txt.
stash@{1}: WIP on Conflict: a8de3e9 add <date>.txt.
  1. git stash list
  2. git stash push -m "page 3."
  3. git stash list
  4. git stash pop
  5. git stash list
GitProject$ git stash list
stash@{0}: WIP on Conflict: a8de3e9 add <date>.txt.
stash@{1}: WIP on Conflict: a8de3e9 add <date>.txt.
GitProject$ git stash push -m "page 3."
Saved working directory and index state On Conflict: page 3.
GitProject$ git stash list
stash@{0}: On Conflict: page 3.
stash@{1}: WIP on Conflict: a8de3e9 add <date>.txt.
stash@{2}: WIP on Conflict: a8de3e9 add <date>.txt.
GitProject$ git stash pop
On branch feature/Conflict
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   2021-01-31T10:03:33.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   store.txt

Dropped refs/stash@{0} (4b4fa60b5abdb4d64f63f411e1159f41af7b6281)
GitProject$ git stash list
stash@{0}: WIP on Conflict: a8de3e9 add <date>.txt.
stash@{1}: WIP on Conflict: a8de3e9 add <date>.txt.

退避した作業を消す

git stash drop stash@{<num>} を行う. ただ, 消したあとに0番方向へ詰めていくので分かりにくい.

GitProject$ git stash drop stash@{0}
Dropped stash@{0} (295e54fdc928280e233c4162d8b4046999b923bf)
GitProject$ git stash list
stash@{0}: WIP on Conflict: a8de3e9 add <date>.txt.

tagをつける

git tag を実行する. しかし, gitのtagには軽量版と注釈付きの2種類ある. 軽量版はその記録に対して別名を付けるようなものであり, 注釈付きは変更差分の無いcommitのようなものである. 注釈付きはcommitと同じようなものであるため, 詳細なメッセージやgpg署名を付与できる. どちらも, 後から指定のコミットへ付与することが可能.

  1. 事前状態
  2. git tag <tag name> 軽量版タグ作成
  3. 事後状態
GitProject$ git log --oneline
a8de3e9 (HEAD) add <date>.txt.
9e5290b (origin/develop, develop) Merge branch 'feature/AddStoreFile' into develop
93bfb5f (origin/feature/AddStoreFile, feature/AddStoreFile) add <date>.txt.
24b27fa add <date>.txt.
581fe4a add the <date>.txt for exsample.
b9711dd add store.txt.
7fdbadb init.
GitProject$ git tag lightweight
GitProject$ git log --oneline
a8de3e9 (HEAD, tag: lightweight) add <date>.txt.
9e5290b (origin/develop, develop) Merge branch 'feature/AddStoreFile' into develop
93bfb5f (origin/feature/AddStoreFile, feature/AddStoreFile) add <date>.txt.
24b27fa add <date>.txt.
581fe4a add the <date>.txt for exsample.
b9711dd add store.txt.
7fdbadb init.
  1. 事前状態
  2. git tag -a <tag name> -m <message> <commit> 注釈付きタグを指定のコミットに作成
  3. 事後状態
GitProject$ git log --oneline
a8de3e9 (HEAD, tag: lightweight) add <date>.txt.
9e5290b (origin/develop, develop) Merge branch 'feature/AddStoreFile' into develop
93bfb5f (origin/feature/AddStoreFile, feature/AddStoreFile) add <date>.txt.
24b27fa add <date>.txt.
581fe4a add the <date>.txt for exsample.
b9711dd add store.txt.
7fdbadb init.
GitProject$ git tag -a annotated -m "Initialize" 7fdbadb
GitProject$ git log --oneline
a8de3e9 (HEAD, tag: lightweight) add <date>.txt.
9e5290b (origin/develop, develop) Merge branch 'feature/AddStoreFile' into develop
93bfb5f (origin/feature/AddStoreFile, feature/AddStoreFile) add <date>.txt.
24b27fa add <date>.txt.
581fe4a add the <date>.txt for exsample.
b9711dd add store.txt.
7fdbadb (tag: annotated) init.

なお, tagはそのままの状態ではgit pushでのアップロード対象にならない. 明示的にtagをpushしないといけない.

GitProject$ git push origin annotated
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 166 bytes | 166.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To github.com:user.name/GitProject.git
 * [new tag]         annotated -> annotated

使い分けとしては私の勝手ながら以下のやり方はどうだろうか.

  • 軽量版 :: 完全にlocalの個人利用として割り切る.
  • 注釈付き :: 全体で多きな節目(リリースや後方互換性を失うなど)に署名付きでタグを付ける.

直近の変更記録を戻す

戻したい記録がlocalだけに存在するならできる. remoteに上げてしまっているなら, 諦めよ.

git reset HEAD~ で1記録分戻すことができる.

  1. 事前状態
  2. git reset HEAD~
  3. 事後状態
GitProject$ git log --oneline
cfb3b0e (HEAD -> feature/Conflict) add <date>.txt.
f6e41aa add <date>.txt.
5d7a532 update store.txt.
8b771d1 add <date>.txt.
17a48d9 add <date>.txt.
48fabb7 add <date>.txt.
a8de3e9 (tag: lightweight) add <date>.txt.
GitProject$ git reset HEAD~
GitProject$ git log --oneline
f6e41aa (HEAD -> feature/Conflict) add <date>.txt.
5d7a532 update store.txt.
8b771d1 add <date>.txt.
17a48d9 add <date>.txt.
48fabb7 add <date>.txt.
a8de3e9 (tag: lightweight) add <date>.txt.
GitProject$ git status
On branch feature/Conflict
Untracked files:
  (use "git add <file>..." to include in what will be committed)
   2021-02-07T04:39:57.txt

nothing added to commit but untracked files present (use "git add" to track)

なお, 直前のcommit messageを修正したいだけなら, resetせずとも git commit --amend で実施できる.

  1. 事前状態
  2. git commit --amend -m <message> -m オプション無しなら, メッセージ入力モードとなる.
  3. 事後状態
GitProject$ git log --oneline
f6e41aa (HEAD -> feature/Conflict) add <date>.txt.
5d7a532 update store.txt.
8b771d1 add <date>.txt.
17a48d9 add <date>.txt.
48fabb7 add <date>.txt.
a8de3e9 (tag: lightweight) add <date>.txt.
GitProject$ git commit --amend -m "amended."
[feature/Conflict 7168e6c] amended.
 Date: Sun Feb 7 13:40:15 2021 +0900
 1 file changed, 1 insertion(+)
 create mode 100644 2021-02-07T04:39:56.txt
GitProject$ git log --oneline
7168e6c (HEAD -> feature/Conflict) amended.
5d7a532 update store.txt.
8b771d1 add <date>.txt.
17a48d9 add <date>.txt.
48fabb7 add <date>.txt.
a8de3e9 (tag: lightweight) add <date>.txt.

変更を戻す手段として, revert commitと呼ばれる過去の変更記録の差分と丁度逆の変更を実施する方法もある. これならremoteに上げてしまっている変更でも作成できる.

  1. 事前状態
  2. git revert <commit> (commitと同じようにmessage編集画面がでるが,そのままのため省略.)
  3. 事後状態
GitProject$ git log --oneline
7168e6c (HEAD -> feature/Conflict) amended.
5d7a532 update store.txt.
8b771d1 add <date>.txt.
17a48d9 add <date>.txt.
48fabb7 add <date>.txt.
a8de3e9 (tag: lightweight) add <date>.txt.
GitProject$ git revert 5d7a532
[feature/Conflict 44850cf] Revert "update store.txt."
 1 file changed, 2 deletions(-)
GitProject$ git log --oneline
44850cf (HEAD -> feature/Conflict) Revert "update store.txt."
7168e6c amended.
5d7a532 update store.txt.
8b771d1 add <date>.txt.
17a48d9 add <date>.txt.
48fabb7 add <date>.txt.
a8de3e9 (tag: lightweight) add <date>.txt.

直近の変更履歴をまとめる

まとめたい記録がlocalだけに存在するならできる. remoteに上げてしまっているなら, 諦めよ.

ここでは git rebase -i HEAD~<num> (-i == --interactive) を利用する. numには
"変更したい記録が現在からどれだけ離れているか+1" くらいを指定する. まとめると一口に言っても, interactiveオプションではそれなりに選択肢がある.

次の例ではrevertのcommitとその元になったcommitまでを固めて, そもそもそんな変更など無かったかのようにまとめなおす.

  1. 事前状態
  2. git rebase -i HEAD~4 (一端区切る)
  3. 修正指示内容 (一端区切る)
  4. 修正commitのmessage編集 (一端区切る)
  5. 事後状態
GitProject$ git log --oneline
44850cf (HEAD -> feature/Conflict) Revert "update store.txt."
7168e6c amended.
5d7a532 update store.txt.
8b771d1 add <date>.txt.
17a48d9 add <date>.txt.
48fabb7 add <date>.txt.
a8de3e9 (tag: lightweight) add <date>.txt.
GitProject$ git rebase -i HEAD~4
  GNU nano 4.8                                                                  /tmp/GitProject/.git/rebase-merge/git-rebase-todo                                                                             
pick 8b771d1 add <date>.txt.
pick 5d7a532 update store.txt.
pick 7168e6c amended.
pick 44850cf Revert "update store.txt."

# Rebase 17a48d9..44850cf onto 44850cf (4 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message

  GNU nano 4.8                                                                  /tmp/GitProject/.git/rebase-merge/git-rebase-todo                                                                   Modified  
pick 8b771d1 add <date>.txt.
s 5d7a532 update store.txt.
s 7168e6c amended.
s 44850cf Revert "update store.txt."

# Rebase 17a48d9..44850cf onto 44850cf (4 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message

  GNU nano 4.8                                                                         /tmp/GitProject/.git/COMMIT_EDITMSG                                                                                    
# This is a combination of 4 commits.

# This is a combination of 4 commits.
# This is the 1st commit message:

add <date>.txt.

# This is the commit message #2:

update store.txt.

# This is the commit message #3:

amended.

# This is the commit message #4:

Revert "update store.txt."

This reverts commit 5d7a532c07d26ae1da939a4b6cbfbf61108d6dac.

[detached HEAD 03dcd8c] add <date>.txt and squashed.
 Date: Sun Feb 7 12:55:18 2021 +0900
 2 files changed, 2 insertions(+)
 create mode 100644 2021-02-07T03:53:44.txt
 create mode 100644 2021-02-07T04:39:56.txt
Successfully rebased and updated refs/heads/feature/Conflict.
GitProject$ git log --oneline
03dcd8c (HEAD -> feature/Conflict) add <date>.txt and squashed.
17a48d9 add <date>.txt.
48fabb7 add <date>.txt.
a8de3e9 (tag: lightweight) add <date>.txt.

参考

脚注
  1. &3 ↩︎

  2. &1 ↩︎ ↩︎ ↩︎

  3. GitHubの方でrepository初期化すると, デフォルトのbranch名称はmainとなる. The default branch for newly-created repositories is now main ↩︎

  4. 中身はUUID4と呼ばれる世界中でほぼ衝突しないだろうユニークIDである. UUIDを利用しているのは手っ取り早く乱数を得るためである. ↩︎ ↩︎

  5. git restore -S <file> の -S は --staged オプションである. また, git reset <file> でも同様のことが可能である. ↩︎

  6. &2 p234より, それまでの git stash save が廃止されることが示唆されている. ↩︎

Discussion

td-shitd-shi

とりあえず力尽きたので公開. CC3.0なのは参考にした公式pdfがCC3.0であったため.