[Git] 運用ルールの決定〜導入

検討事項

  • Gitアカウント取得は自社か、先方か
  • ブランチ管理モデルは、Git flowか、Github flowか
    • Git flow: 全員が全員ブランチを切り全員がマージできる状態
    • Github flow: develop masterへのマージはコード管理者へのプルリクで行う
    • 後者の方がコード品質は保てるが実装期間が短い場合これは該当しない。
  • 有識者・管理者の選定
  • 本番サーバに上がっているソースとgit masterに上がっているファイルの比較/保守

ブランチ管理(例)

  • リポジトリの作成
  • ブランチと役割を明確化
    • 例: master, develop, feature, release, hotfixで以下のように運用。
    • masterはマージ後テスト済みのリリース可能な状態のブランチ。直接このブランチで作業しない。
    • developは開発ブランチ。feature/release/hotfixブランチを切り、終わったらdevelopへマージ。直接このブランチで作業しない。
    • featureは追加機能開発ブランチ。
    • releaseはリリース用ブランチ。developブランチから切ってmasterブランチへマージ。
    • hotfixは本番障害時など早急に対応が必要な場合にmasterブランチから切られる。
  • ポータルのWiki等で周知し、ブランチ管理を管理者の元徹底化。

Gitクライアントの選定

  • Git CLI
  • SourceTree
  • GitHub Desktop

参考サイト

[Git] “detached HEAD”となり、SHA1を参照してしまう

checkout時、コミットSHA1を指定、若しくは誤ったブランチパスを指定すると、”detached HEAD”の状態となることがある。

$ git checkout origin/feature/ph1.5-cms-format-change
Note: checking out 'origin/feature/ph1.5-cms-format-change'.

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 performing another checkout.

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

  git checkout -b <new-branch-name>

HEAD is now at 8xxxxx... xxxを対応しました。

$ git status
HEAD detached at origin/feature/ph1.5-cms-format-change
nothing to commit, working tree clean

この時、対象ブランチではなく、ブランチのHEAD SHA1を参照している。
該当のSHA1から、対象ブランチが何なのか把握する。

$ git name-rev 8xxxxx
8xxxxx remotes/origin/feature/ph1.5-cms-format-change

そして、checkoutし直すことにより、SHA1ではなくブランチのHEADを参照するようになる。

$ git checkout feature/ph1.5-cms-format-change
Branch feature/ph1.5-cms-format-change set up to track remote branch feature/ph1.5-cms-format-change from origin.
Switched to a new branch 'feature/ph1.5-cms-format-change'

$ git status
On branch feature/ph1.5-cms-format-change
Your branch is up-to-date with 'origin/feature/ph1.5-cms-format-change'.
nothing to commit, working tree clean

[Git] コマンド

随時更新

前提知識

fetch

“git fetch origin xxx”は、リモートレポジトリ “origin” のブランチ “xxx” を、ローカルのリモート追跡ブランチ (remote tracking branch) に反映させるということである。

$ git branch -a
* develop
  feature/dynamodb
  feature/ph1-add-flag
  master
  remotes/origin/HEAD -> origin/master # remote追跡ブランチ
  remotes/origin/develop # remote追跡ブランチ
  remotes/origin/ph1-test # remote追跡ブランチ

merge

“git merge origin/xxx” は、リモートレポジトリ “origin” のブランチ “master” の追跡ブランチ (origin/xxx) を、現在のブランチにマージするということである。
マージする場合は --no-ff オプションを基本的に付与する。二つのブランチが fast-forward な関係かどうかにかかわらず、必ずマージコミットが作られる。

-ff: fast-foward

Git コマンド

$ mkdir {WORKING_COPY}
$ cd {WORKING_COPY}

# Gitリポジトリを新たに作成する。バージョン管理外の既存プロジェクトをGitリポジトリに変換したり, カレントリポジトリの初期化を行う。
$ git init

# リモートからローカルへリポジトリのクローン
$ git clone https://{USERNAME}:{PASSWD}@github.com/{ORGANIZATION}/{REPOSITORY}.git

# リモートからローカルへリポジトリのクローンができない場合
# sslVerifyを解除しない場合 "SSL certificate problem: Invalid certificate chain" のようにエラーとなる場合がある。
$ git config --global http.sslVerify false
$ git clone git@xxx...
$ git config --global http.sslVerify true

# ユーザー名/メールアドレスの設定
$ git config user.name "xxx"
$ git config user.email "xxx@gmail.com"

# 他のワーキングコピーも共通でユーザー名/メールアドレスを設定
$ git config --global user.name "xxx"
$ git config --global user.email "xxx.gmail.com"

# 設定の一覧を表示する
$ git config --global --list

# 現在のリポジトリの設定のみ表示
$ git config --list

# 半角区切りで複数ファイルをインデックスへ追加可能。コミット前の一時保存場所への追加と思えば良い。
$ git add README.md xxx.html

# -Aをつけると、変更を全てインデックスへ追加
$ git add -A

# コミット
$ git commit -m "first commit"

# リモートレポジトリのURL情報を追加。どこへpushするのか指定する。
$ git remote add origin git@git....git

# リモートブランチのURL確認
$ git remote -v
origin https://github.com/xxx

# リモートリポジトリを指定する
$ git remote set-url origin git@xxx.github.com:xxx/xxx.git

# リモートリポジトリのmasterへプッシュ。-uは次回からブランチ名(今回はmaster)を省略可能なオプション。
$ git push -u origin master

# チェックアウト (ローカルへブランチを落とす)
$ git checkout origin release/1.1.2

# 最新のコミットをリモートから取得する
$ git pull origin master

# (コミット前の)ローカルの変更を全て取り消す
$ git checkout .

# (コミット前の)ローカルの変更を一部取り消す
$ git checkout -- /path/to/file
# コミットIDを指定してコミットしたものも含めてローカルの変更を全て取り消す
$ git reset --hard c6c2114

# ブランチを指定してコミットしたものも含めてローカルの変更を全て取り消す
$ git reset --hard origin/master

# ローカルでrevertを使ってpush内容を取り消し、リモートへ反映(resetと異なり、コミットが残る)
$ git revert {commit id}

# ステータスを確認する
$ git status

# コミットログを確認する
$ git log --oneline

# グラフ上に展開
$ git log --graph

# コミットログをコミットメッセージから検索する。
$ git log --grep hoge

# 現在のコミットIDを確認する
$ git show -s --format=%H

# 全てのブランチの一覧 (表示されない場合は、更新する)
$ git branch -a | grep release

# リモートブランチの更新
$ git remote update

# ブランチの作成
$ git branch develop

# 作成ブランチをプッシュ
$ git push origin develop

# developをチェックアウトし
$ git checkout develop

# masterへマージする
$ git merge -m "create develop" master

# ブランチを削除する
$ git branch -d origin/feature/xxx

# ブランチを強制削除する
$ git branch -D origin/feature/xxx

# リモートリポジトリの削除情報をローカルへ反映させる
$ git fetch --prune

# gitignoreが有効になっているファイルを表示する
$ git status --ignored

1つのマシンで複数のGitアカウントを使用する

$ ssh-keygen -t rsa -f ~/.ssh/xxx -C "some comment"

# catした内容をGithubへ貼り付ける。
$ cat .ssh/xxx.pub

configファイルへ以下を追記する。
~/.ssh/config

Host xxx
    User yyy
    HostName github.com
    IdentityFile ~/.ssh/xxx

Gitへ認証させる

$ ssh xxx
The authenticity of host 'github.com (xxx.xx.xxx.xxx)' can't be established.
RSA key fingerprint is SHAxxx:xxx.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,xxx.xx.xxx.xxx' (RSA) to the list of known hosts.
Saving password to keychain failed
Identity added: /Users/user/.ssh/xxx ((null))
PTY allocation request failed on channel 0
Hi xxx! You've successfully authenticated, but GitHub does not provide shell access.
Connection to github.com closed.

CloneとRemoteへの追加

$ git clone git@{USER}:{PASSWD}/{REPOSITRY}.git
$ git remote add origin git@{USER}:{PASSWD}/{REPOSITRY}.git

Pushしたコミットをある時点まで完全に取り消したい

$ git log --graph
$ git reset --hard c53hgoehhogoeo53h53hog3l
$ git status
On branch feature/test
Your branch is behind 'origin/feature/test' by 3 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)
nothing to commit, working tree clean

# そのままpushを行ってもエラーとなる
$ git push -u origin feature/test
To https://github.com/org/test.git
 ! [rejected]        feature/test -> feature/test (non-fast-forward)
error: failed to push some refs to 'https://user:pass@github.com/org/test.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.

# オプション "-f" を付与する
$ git push -uf origin feature/test
Total 0 (delta 0), reused 0 (delta 0)
To https://github.com/org/test.git
 + c53hgoehhogoeo53h53hog3l feature/test -> feature/test (forced update)
Branch feature/test set up to track remote branch feature/test from origin.

Rebaseを取り消したい

$ git rebase -i {コミットID}
Successfully rebased and updated refs/heads/feature/feature-xxx.

$ git status
On branch feature/feature-xxx
Your branch and 'origin/feature/feature-xxx' have diverged,
and have 2 and 4 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

$ git reflog
826ab92 HEAD@{0}: rebase -i (finish): returning to refs/heads/feature/feature-xxx
826ab92 HEAD@{1}: rebase -i (pick): xxx -> yyyへ変更
03d8142 HEAD@{2}: rebase -i (pick): yyy
b28cfc2 HEAD@{3}: rebase -i (start): checkout b28cfc2
92db362 HEAD@{4}:
92db362 HEAD@{6}: commit: xxx -> yyyへ変更
58e9522 HEAD@{7}: checkout: moving from develop to feature-xxx

$ git reset --hard HEAD@{6}
HEAD is now at xxxxxxx xxx -> yyyへ変更

$ git reflog
92db362 HEAD@{0}: reset: moving to HEAD@{6}
826ab92 HEAD@{1}: rebase -i (finish): returning to refs/heads/feature/feature-xxx
826ab92 HEAD@{2}: rebase -i (pick): xxx -> yyyへ変更
03d8142 HEAD@{3}: rebase -i (pick): yyy
b28cfc2 HEAD@{4}: rebase -i (start): checkout b28cfc2

Mergeしたcommitを取り消す

$ git branch
* develop
  feature/feature-xxx

$ git revert -m 1 {mergeしたcommit ID}

# 誤ってrevertした場合
$ git reset --hard HEAD~

# 後はcommit, push

.gitignoreが効かない

“.gitignore” は、Remoteを消しつつ、ローカルでファイルを残してコミット対象から無視させる (という理解)。

$ git touch .gitignore
$ vim .gitignore
$ git add .gitignore
$ git commit -m "add gitignore"

# キャッシュを削除する。ローカルのファイルへは影響しない。
$ git rm --cached {FILE_NAME}

# ディレクトリごとキャッシュ削除。
$ git rm -r --cached .

# commit, pushする。
$ git add .
$ git commit -m "remove cache of gitignore"
$ git push -u origin develop

Remoteにファイルを残しつつ、ローカルの変更をコミット対象から無視させる場合は、以下のコマンドを利用する。

$ git update-index --assume-unchanged {FILE_NAME}

# 元に戻す場合
$ git update-index --no-assume-unchanged {FILE_NAME}

# 設定の反映確認
$ git ls-files -v

[Git] Git Flow

# git flowパッケージの導入
% brew install git-flow

# リモートの更新
% git remote update

# developブランチの作成
% git branch develop

# git flowの導入
% git flow init
Which branch should be used for bringing forth production releases?
   - develop
   - feature-ph1-add-breadcrumb
   - feature-ph1-test
   - master
Branch name for production releases: [master]

Which branch should be used for integration of the "next release"?
   - develop
   - feature-ph1-add-breadcrumb
   - feature-ph1-test
Branch name for "next release" development: [develop]
How to name your supporting branch prefixes?
Feature branches? [feature/]
Release branches? [release/]
Hotfix branches? [hotfix/]
Support branches? [support/]
Version tag prefix? []

# 作成を確認
% git config --list
gitflow.branch.master=master
gitflow.branch.develop=develop
gitflow.prefix.feature=feature/
gitflow.prefix.release=release/
gitflow.prefix.hotfix=hotfix/
gitflow.prefix.support=support/
gitflow.prefix.versiontag=

# 既存リポジトリをfeatureへ移動
% git branch -m feature-ph1-test feature/feature-ph1-test
% git checkout feature/feature-ph1-test
% git push origin feature/feature-ph1-test

ブランチの作成

$ git flow feature start {branch_name}
Switched to a new branch '{branch_name}'

Summary of actions:
- A new branch 'feature/{branch_name}' was created, based on 'develop'
- You are now on branch 'feature/{branch_name}'

Now, start committing on your feature. When done, use:

     git flow feature finish {branch_name}

ブランチの公開(push)

git flow feature publish {branch_name}
Counting objects: 702, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (424/424), done.
Writing objects: 100% (702/702), 15.56 MiB | 1.51 MiB/s, done.
Total 702 (delta 401), reused 218 (delta 185)
remote: Resolving deltas: 100% (401/401), completed with 280 local objects.
To https://github.com/xxx/xxx.git
 * [new branch]      feature/{branch_name} -> feature/{branch_name}
Already on 'feature/{branch_name}'
Your branch is up-to-date with 'origin/feature/{branch_name}'.

Summary of actions:
- A new remote branch 'feature/{branch_name}' was created
- The local branch 'feature/{branch_name}' was configured to track the remote branch
- You are now on branch 'feature/{branch_name}'