はじめに
新しい開発を行う時に手が止まらないようGitの設定や手順を残しておきます。
なお、本記事は以下のUdemy講座を元にした覚書です。
米国AI開発者がやさしく教えるGit入門講座
前提
- 手元のPCにGitインストールされていること
- GitHubにアカウントがあること
記事中の構成
実行結果をそのまま記載しているため、適宜以下は変更してください。
- Gitユーザー名:c3drive
- リポジトリ名:my-git-practice / git-practice
- 作業ブランチ名:update-readme / new-feature / conflict など
git構成覚書
【ワーキングディレクトリ】…作業をしているところ
↓ git add ↑ git restore
【ステージングエリア】…addされているところ
↓ git commit ↑ git restore --staged
【リポジトリ/作業用ブランチ】※
--リモートリポジトリがある場合--
↓ git push ↑ git pull
【リモートリポジトリ/作業用ブランチ】
↓ pull request
【リモートorfork元リポジトリ/mainブランチ】
※ローカルブランチしかない場合、このタイミングでローカルでmainにマージ(git merge)する。
SSHアクセス設定(PCを変えた時)
GitHubでパスワード認証は2021/08/13に使えなくなります(参考)
二段階認証など別の認証をしている場合、この手順は不要です。
SSHキー作成
# SSHキーを作成(アルゴリズムはed25519)
# パスフレーズを聞かれる(指定なければそのままエンター)
$ sh-keygen -t ed25519 -C "{メールアドレス}"
# ~/.ssh配下に公開鍵と秘密鍵が作成される
$ ls -l ~/.ssh
-rw------- 1 yukokanai staff 419 2 16 12:56 id_ed25519
-rw-r--r-- 1 yukokanai staff 110 2 16 12:56 id_ed25519.pub
# ssh-agentを起動
$ ebal "$(ssh-agent -s)"
# ~/.ssh/configファイルを編集する(編集内容は後述)
$ subl ~./ssh/config
# SSHキーをssh-agentに登録する
$ ssh-add -K ~/.ssh/id25519
Host *
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
作成したSSHキーをGitHubに登録
# 公開鍵(.pubがついている方)をクリップボードにコピー
$ pbcopy < ~/.ssh/id25519.pub
GitHubに移動して、必要情報を入力したら「Add SSH key」を押下
- URL(※):https://github.com/settings/ssh/new
- Title:(任意)
- Key:(クリップボードにコピーした公開鍵の中身)
※[Account] - [settings] - [SSH and GPG keys] - [New SSH key]でも可能
#ユーザーの設定(global)
$ git config --global user.name "{ユーザー名}"
$ git config --global user.email "{メールアドレス}"
--globalをつけることで、当該ユーザーのシステム全体の設定となる。(~/.gitconfig)
--globalをつけないことで、実行したリポジトリ独自の設定となる。(.git/config)
エディタの設定(global)
$ which subl
/usr/local/bin/subl
$ git config --global core.editor "/usr/local/bin/subl -n -w"
diff/mergeツールの設定(global)
$ ls /Applications/p4merge.app/Contents/MacOS/
p4merge
$ git config --global diff.tool p4merge
$ git config --global difftool.p4merge.path "/Applications/p4merge.app/Contents/MacOS/p4merge"
$ git config --global difftool.prompt false
$ git config --global merge.tool p4merge
$ git config --global mergetool.p4merge.path "/Applications/p4merge.app/Contents/MacOS/p4merge"
$ git config --global mergetool.prompt false
$ git diff // 通常のdiffコマンド
$ git difftool // p4mergeが起動
GitHubにリモートリポジトリ作成
GitHubに移動して、必要情報を入力したら「Create repository」を押下
- URL:https://github.com/new
- Repository name:任意
- Description:任意
- Public/Private:特に理由がなければPublicを選択
- Initialise thie repository with :特に理由がなければREADME .gitgnoreを選択
fork
一からリモートリポジトリを作成するのではなく、別のアカウントの既存のリモートリポジトリを自分のリポジトリにコピーする方法です。
OSSに携わる場合などこのやり方をします。
やり方は、GitHubからforkしたいリモートリポジトリの画面のforkボタンを押下するだけです。
自分のアカウントにforkしたリモートリポジトリが作成されていることを確認してください。
この後cloneしてローカルリポジトリで作業していくと思いますが、cloneするのはfork元ではなく、自分のリモートリポジトリですので注意してください。
Gitリポジトリの作成
いくつか方法があります。
スクラッチから作成する
$ cd {ローカルリポジトリを作成したいディレクトリ}
$ git init {新しく作成したいリポジトリ名}
Gitリポジトリになっているか確認
$ cd {リポジトリ名}
$ ls -la
total 0
drwxr-xr-x 3 yukokanai staff 96 2 22 11:48 .
drwxr-xr-x 4 yukokanai staff 128 2 22 11:48 ..
drwxr-xr-x 9 yukokanai staff 288 2 22 11:48 .git
既存のディレクトリをGitリポジトリにする
$ cd {ローカルリポジトリにしたいディレクトリ}
$ git init
Gitリポジトリになっているか確認
$ cd {リポジトリ名}
$ ls -la
total 0
drwxr-xr-x 3 yukokanai staff 96 2 22 11:48 .
drwxr-xr-x 4 yukokanai staff 128 2 22 11:48 ..
drwxr-xr-x 9 yukokanai staff 288 2 22 11:48 .git
git status
で確認すると、そのディレクトリ配下に存在するファイルやディレクトリが全てワーキングディレクトリに表示されますので、既存のファイルやディレクトリは一括でステージングエリアに追加・コミットしましょう。
$ git add .
$ git commit -m "initial commit"
リモートリポジトリの情報をセットしておく
GitHub上のリモートリポジトリのパス(SSH)をコピーする。
- URL:[リポジトリ] - [code] - [SSH] - クリップボードのマーク押下
$ cd {リポジトリ名}
$ git remote add origin {コピーしたリポジトリパス}
// pullする時エラーになる場合
$ git pull --allow-unrelated-histories origin {branch名}
リモートリポジトリからcloneして作成
GitHub上のリモートリポジトリのパス(SSH)をコピーする。
- URL:[リポジトリ] - [code] - [SSH] - クリップボードのマーク押下
コピーしたパスでclone
$ cd {ローカルリポジトリをcloneしたいディレクトリ}
$ git clone {コピーしたパス}
リモートリポジトリに紐づいているか確認
$ cd {リポジトリ名}
$ git remote -v
origin git@github.com:c3drive/my-git-practice.git (fetch)
origin git@github.com:c3drive/my-git-practice.git (push)
fork元がある場合は、元のリポ情報をセットしておく。
GitHub上のfork元のリモートリポジトリのパス(SSH)をコピーする。
- URL:[リポジトリ] - [code] - [SSH] - クリップボードのマーク押下
$ cd {リポジトリ名}
$ git remote add upstream {コピーしたfork元のリポジトリパス}
$ git remote -v
origin git@github.com:c3drive/git-practice.git (fetch)
origin git@github.com:c3drive/git-practice.git (push)
upstream git@github.com:D-S-Hub/git-practice.git (fetch)
upstream git@github.com:D-S-Hub/git-practice.git (push)
作業用branchを作成し作業
まずは作業用branchを作成し切替
// 作成してから切り替える場合
$ git branch {branch名}
$ git checkout {branch名}
// checkoutにオプション指定することで作成と切替同時に実施することも可能
$ git checkout -b {branch名}
// *が作成したブランチについていればOK
$ git branch
main
* {branch名}
何らかの更新を行いステージングエリアへaddする
$ git status // add前にステータスを確認
On branch update-readme
Changes not staged for commit:
(略)
modified: {更新をしたファイル}
$ git add {更新をしたファイル}
$ git status // add後にステータスを確認
On branch update-readme
Changes to be committed:
(略)
modified: {更新をしたファイル}
diff
$ git diff --staged HEAD // commit前にステージングエリアの状態とHEADを確認
// その他
$ git diff // ワーキングディレクトリとステージングエリアの状態を確認
$ git diff HEAD // ワーキングディレクトリとHEADの状態を確認
$ git diff -- {ファイル名} // {ファイル名}のみの状態を確認
$ git diff {コミットID1} {コミットID2} // {コミットID1}と{コミットID2}の状態を確認
$ git diff {branch名1} {branch名2} // HEAD^(HEADの親)とHEADの状態を確認
$ git diff {branch名1} {branch名2} // {branch名1}と{branch名2}の状態を確認
コミット
$ git commit -m "{コメント}"
[update-readme 8d3845b] update README
1 file changed, 1 insertion(+), 1 deletion(-)
$ git status // commit後にステータスを確認
On branch update-readme
nothing to commit, working tree clean
ファイル名の変更
$ git mv {変更前ファイル名} {変更後ファイル名}
$ git status
Changes to be committed:
renamed: newfile.txt -> namechanged.txt
基本は、git mv
で管理するのが良いと思いますが、mv
してしまった場合でも以下のようにすることでリネームと見做してくれる。
$ mv {変更前ファイル名} {変更後ファイル名}
$ git status
Changes not staged for commit:
deleted: namechanged.txt
Untracked files:
namechanged2.txt
$ git add -A
$ git status
Changes to be committed:
renamed: namechanged.txt -> namechanged2.txt
ファイル削除
$ git rm {ファイル名}
$ git status
Changes to be committed:
deleted: delete_soon.txt
変更や削除をキャンセルしたい場合
// ①
// staging areaへのaddキャンセル
// ※staging areaの変更をリポジトリで上書き
$ git restore --staged {ファイル名}
// ②
// working directoryのキャンセル
// ※working directoryの変更をstaging areaで上書き
$ git restore {ファイル名}
①だけだと、working directoryに変更や削除は残る。全てやり直したい場合は、①②どちらも行うこと。
削除キャンセルために、リポジトリからpullするなどしてもワーキングディレクトリでの削除がリポジトリよりも後の行為なので削除したことは残ってしまう。
他のブランチへ更新を反映する
ローカルリポジトリへのマージ
$ git checkout main //作業ブランチではなくマージ先のブランチに切替(ここだとmain)
$ git diff main new-feature
diff --git a/README.md b/README.md
index e6200e2..38a4340 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,3 @@
# my-git-practice
Git練習用。テスト更新。
+`new-feature`ブランチで変更
\ No newline at end of file
$ git merge new-feature
Updating fe432e4..a961c1f
Fast-forward
README.md | 1 +
1 file changed, 1 insertion(+)
mergeについて
いくつか種類があります。
Fast-forward
ブランチ作成からマージまでの間、マージ先での変更がなかった場合
・main -(commit0)-------------------(commit1)------------->
| ∧merge
∟other ----->(commit1)------------------>
Automatic merge
ブランチ作成からマージまでの間、マージ先での変更があったがコンフリクトしなかった場合
Gitは自動的に差分をマージした新しいコミット(merge commit)を作成する。
・main -(commit0)----(commit2)------------(commit3)-------->
| ∧merge
∟other ----->(commit1)------------------->
####手動マージ(Conflictが起きてAutomatic mergeされなかった場合)
ブランチ作成からマージまでの間、マージ先での同じファイルへの変更があるような場合
・main -(commit0)----(commit2)------------(×)-------------->
| ∧※merge
∟other ----->(commit1)------------------->
conflictが起きた状態、もしくは別の理由でGitによるmergeができなかった場合、
マージファイルはMERGINGに入るため、そのファイルを手動でコミットします。
$ git merge conflict
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
$ git status
(略)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
$ subl README.md //テキストファイルを開き目で変更を修正していく
このようにコンフリクトしている物が両方含まれたファイルが開きます。
最終的にコミットしたい形に修正します。(Gitにより出力されている<<<<HEADなども削除します)
p4mergeを使ったマージは以下の通りに行います。
$ git mergetool // マージツールが開くので編集して保存
$ rm README.md.orig // マージツールで保存すると変更前のファイルができるので削除
修正が終わったらadd/commitしていきます。
$ git add README.md
(略)
modified: README.md
$ git commit -m "resolve conflict"
リモートリポジトリへのマージ
pullとpush
チーム開発の場合、自身がローカルで作業始めた後にリモートリポジトリ側に更新がかかっている可能性があります。
$ git push origin main
To github.com:c3drive/my-git-practice.git
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'git@github.com:c3drive/my-git-practice.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
そのため、pull(他の人の更新を今自分が作業しているブランチに反映)してからpushするようにしましょう。
$ git pull origin main // 作業ブランチではなく元のブランチ(main)からpullする
From github.com:c3drive/my-git-practice
* branch main -> FETCH_HEAD
Already up to date.
pullは、fetch+mergeですのでローカルリポジトリへのマージと同様コンフリフトが起こることがあります(実務ではよく起こる)。
コンフリクトが起きた場合、[手動マージ](####手動マージ(Conflictが起きてAutomatic mergeされなかった場合))を行います。
リモートリポジトリの更新を別の人が行っている場合、どちらを生かすべきか更新者に確認をします。
再度add/commitまで行った後のログを確認すると、リモートはf69454e
が最新、ローカルはd3dbe60
が最新、HEADは両者のマージコミットが作成されていることがわかる。
my-git-practice (:conflict-remote) :$ git log --oneline --graph
* 36d0396 (HEAD -> conflict-remote) resolve conflict
|\
| * f69454e (origin/main, origin/HEAD) Update README conflict
* | d3dbe60 Update README conflict-remote
|/
* 3638894 (main) Update readme on GitHub
問題なければpush(git push <リモートリポジトリリファレンス> <ローカルリポジトリ>
)
$ git push origin update-readme // mainではないので注意
pull request
GitHubに移動して[リポジトリ] - [Pull requests]タブを選択し[New pull request]ボタンを押下します。
以下の画面になるので、マージ先(ここではmain)とマージ元を指定し、変更内容に問題がなければ、[Create pull request]を押下します。
reviewとmerge(チーム開発の場合、原則pullrequestした人以外が行う)
GitHubに移動して[リポジトリ] - [Pull requests]タブを選択しリクエストをreviewし、問題がなければ[Merge pull request]を押下します。
特にコメントに変更がなければそのまま[Confirm merge]押下。
リモートリポジトリの変更をローカルリポジトリに反映する(pull)
現時点で、前手順でマージしたmainのコミットポイントがリモートリポジトリにしかありません。
まずはブランチをmainに変更します。
$ git checkout main
$ git branch
* main
update-readme
pullします。
$ git pull origin main
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (1/1), done.
From github.com:c3drive/my-git-practice
* branch main -> FETCH_HEAD
ce4e1c0..64558cc main -> origin/main
Updating ce4e1c0..64558cc
Fast-forward
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
なお、ローカルmainは今後も開発時の元となるためできるだけリモートmainに近づけておくことが望ましいです。
対して、今回の場合update-readmeは役割を果たしているためpullせず削除しています。
また別の作業があったら、別の新しいbranchを作成します。
fork元リポジトリへのマージ
pullとpush
リモートリポジトリの時と基本的な流れは変わりませんが、
ここでpullする先はfork元、などの相違点に注意します。
$ git pull upstream main
From github.com:D-S-Hub/git-practice
* branch main -> FETCH_HEAD
* [new branch] main -> upstream/main
Already up to date.
問題なければpush(git push <リモートリポジトリリファレンス> <ローカルリポジトリ>
)
$ git push origin git-practice
pull request
GitHubに移動して[リポジトリ] - [Pull requests]タブを選択し[New pull request]ボタンを押下します。
以下の画面になるので、マージ先にfork元を指定し、マージ元を指定し、変更内容に問題がなければ、[Create pull request]を押下します。
pull requestを出す。ここで書くコメントはfork元のルールに従った形式にする。
指定がない場合であってもリクエストの理由や修正内容は丁寧に書きます。画像のようなコメントではダメです。
reviewとmerge
fork元の管理人が行います。
#その他
######gitignoreのサンプル
https://github.com/github/gitignore
######gitignoreの確認と削除
$ git clean -n // 追跡対象外のファイルを確認
$ git clean -f // 追跡対象外のファイルの削除
Gitリポジトリから普通のディレクトリにしたい場合
$ rm -rf .git
Gitのコミットログの確認
$ git log
$ git log --oneline // originからどれくらい進んでいるか
fetch
pullは、fetch+mergeのため、単にfetchするということもできます
//fetch前
$ git status
On branch main
Your branch is up to date with 'origin/main'.
$ git fetch origin
//fetch後
$ git status
On branch main
Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
//"$ git checkout origin/main" するとリモートリポジトリの変更内容が見れる
rebase
将来マージしたいブランチに簡単にマージできるようにするため、mainと作業用ブランチがある場合、mainブランチを作業用ブランチにrebaseする
※コミットがなくなってしまうので一度pushしたコミットがある場合にはやらない
// 1.作業用ブランチでのコミット
$ git commit -m "update README"
// 2.mainブランチでのコミット
$ git commit -m "make anotherfile"
// 3.作業用ブランチに切り替えてmainをrebase
$ git checkout rebase-practice
$ git rebase main
// commitが枝分かれせず、1本に見えている(2のコミットログは削除され7c0b8ddができる)
$ git log --oneline --graph
* 7c0b8dd (HEAD -> rebase-practice) update README
* 7c77ba2 (main) make anotherfile
* b52de06 (upstream/main, origin/main, origin/HEAD) Initial commit
// このあとは普通にマージ
$ git checkout main
$ git merge rebase-practice
$ git log --oneline --graph
* 7c0b8dd (HEAD -> main, rebase-practice) update README
* 7c77ba2 make anotherfile
* b52de06 (upstream/main, origin/main, origin/HEAD) Initial commit
// rebase時にコンフリクトは起きた時はコンフリクト解消後以下を実行
$ git rebase --continue
// pullもmergeではなくrebaseすることも可能
$ git pull --rebase origin main
// デフォルトでpull時にrebaseする
$ git config --global pull.rebase true
stash
Workingディレクトリ、Stagingエリアの作業中の内容を一時待避できる。
待避後WorkingディレクトリとStagingエリアはクリアされる。
戻す時は、元のブランチである必要はなく、戻す操作をする時のブランチに戻せるため、作業ブランチを誤っていた場合の資産移動などにも使える。
$ git status
modified: README.md // modifiedがあることを確認
$ git stash // stash
$ git status
nothing to commit, working tree clean //clean
$ git stash list // stash一覧
stash@{0}: WIP on main: 3bd5579 update anotherfile
$ git stash apply //戻し
$ git shash drop //削除
$ git stash apply stash@{<i>} //特定のstashをapply
$ git stash drop stash@{<i>} //特定のstashをdrop
apply時にstash対象がWorkingディレクトリにある場合、エラー
apply時にstash対象がStagingエリアにある場合、コンフリクトが起こるのでマージする(マージ後はのアクションはcommit)
$ git stash -u //untrackファイルもstash
$ git stash -a //gitignoreファイルもuntrackファイルも全てstash
$ git stash save "<コメント>" //stashにコメントをつける
$ git stash show stash@{<i>} //特定のstashリファレンスを参照
$ git stash pop //spplyとdropを同時に行う
$ git stash show stash@{<i>}
は、untrackファイルだと何も表示されない。
tag
最新のコミットにタグをつけます。
$ git tag <tagname> <commitID> //commitID未指定の場合は最新のコミットが対象
$ git tag -a <tagname> //エディタが起動するのでコメントを記載
$ git tag -a <tagname> -m "<コメント>"
tag情報をリモートリポジトリに反映する
$ git push origin <tagname> //特定のタグ
$ git push origin --tags //すべてのタグ
$ git push origin :<tagname> //リモートリポジトリからタグを削除
submodule
サブモジュールにしたい、リポジトリにてパスをコピー
URL:[リポジトリ] - [code] - [SSH] - クリップボードのマーク押下
$ cd <メインとなるリポジトリのディレクトリ>
$ git submodule add <コピーしたパス>
$ ls -la
drwxr-xr-x 10 yukokanai staff 320 7 7 21:17 .
drwxr-xr-x 7 yukokanai staff 224 7 7 20:41 ..
drwxr-xr-x 16 yukokanai staff 512 7 7 21:23 .git
-rw-r--r-- 1 yukokanai staff 92 7 7 21:17 .gitmodules //サブモジュール追加時に作成
-rw-r--r-- 1 yukokanai staff 79 5 23 21:59 README.md
drwxr-xr-x 4 yukokanai staff 128 7 7 21:17 sub-project //追加されたサブモジュール
$ git add . //.gitmodules と sup-projectをadd
$ git commit -m "<コメント>"
$ git pull origin
$ git push origin main //リモートリポジトリにpush
サブモジュール@<参照しているコミット>がリンクになっており、リンク先は元のリポジトリにつながっている。
サブモジュールは更新されるので適宜pullする
$ git submodule foreach 'git pull origin main'
$ git add . //サブモジュールの更新分をadd
$ git commit -m "<コメント>"
$ git pull origin
$ git push origin main //リモートリポジトリにpush
すでにsubmoduleがあるリポジトリをクローンした場合、submoduleのディレクトリは空でcloneされるため、初期化・更新する必要がある
$ git clone <サブモジュールを含むリポジトリのコード>
$ cd <メインプロジェクト>/<サブモジュールのディレクトリ>
$ git submodule init
$ git submodule update
オプションを指定することで初期化と更新まで行うことも可能
:$ git clone --recurse-submodules <サブモジュールを含むリポジトリのコード>
git-flow, GitHub Flow
git-flowの考え方。
GitHub flowは、git-flowを簡易化したもの。master(main)は常にデプロイできる状態であることは変わらない。
README.md(markdown記法)の書き方
5W1Hを意識:
- そのプロジェクトが行うこと
- なぜそのプロジェクトが有益なのか
- どうやってそのプロジェクトを使うのか
- ヘルプや詳細情報をどこで得ることができるのか(wikiへ書きリンク貼るのが一般的)
- そのプロジェクトは誰がメンテナンスしているのか
markdown記法:cheatsheet
markdownの確認:
- sublimeにて、cmd+shift+P、install package> Markdown HTML Preview
- sublimeにて、README.mdを開きながら、controle+shift+m
GIF作成:
- QuickTimeなどで動画撮影し、convertio などでgifに変換
- プロジェクト配下にimagesなど作成しgifを格納]
- README.mdに以下を記入
![Alt Text](images/sample.gif)
GitHubにナビゲーションバーを表示させるChromeアドイン
GitHubにてカンバンを実現するChromeアドイン
revert
打ち消しのコミットを新しく作ってmainは打ち消しコミットが最新となる。
コミット履歴を書き換えないので機密情報の打ち消しなどには使えない。
$ git log --oneline
b949956 update README.md
$ git revert HEAD
$ git log --oneline
9b1924a (HEAD -> feature) Revert "update README.md"
b949956 update README.md
リモートリポジトリでマージまで行ってしまった場合
Revertボタンを押下して、Revertのpull requestを作成しmerge
$ git log --oneline
fbe18c7 (HEAD -> main, origin/main, origin/HEAD) Merge pull request #6 from c3drive/revert-5-feature
540b7de Revert "Feature"
46414c7 Merge pull request #5 from c3drive/feature
ffc7326 (origin/feature, feature) update README.md mistake2
reset
コミット履歴を書き換える。
$ git reset HEAD^
オプションの使い分け
| working directory | staging area
------------ | ------------- | -------------
--soft | keep | keep(*3)
-- mixed(default) | keep | reset
-- hard| reset | reset
- reset 指定したコミットIDの状態にそのエリアが上書きされる
- keep 指定したコミットIDの状態にエリアが上書きされるが、そのエリアに変更がある場合、上書きされない。
commit済みのファイル/ディレクトリをgit管理対象から除外
まずは.gitignore
に追加。
その後コミットしてしまったものを削除します。
$ git rm --cached articles // ファイル
$ git rm -r --cached articles // ディレクトリ
$ git add . //deleteを追加