2017年07月21日

git リモートでの操作

リモートリポジトリの操作のために、まずはローカルリポジトリとの関係を整理する。

git_remote01.png
リポジトリファイルやディレクトリの履歴を管理する場所。
ローカルリポジトリ各ユーザごとに、自分のマシン上に配置するリポジトリ。
リモートリポジトリローカルリポジトリとは別の領域に配置されるリポジトリ。複数人で作業する場合、共有マシンやインターネット上に配置されることが多い。ローカルリポジトリに対して異なるマシンである必要はなく、同一マシン上にあってもよい。デフォルトで "origin" と名称がつけられる。
作業ツリーgit管理下に置かれた、実際に作業を行うディレクトリ。
ローカルブランチ作業ツリーと結びついているブランチ。checkout を行うことで作業するブランチを切り替え、作業ツリーの内容を切り替える。
masterブランチ最初にコミットすると作成されるブランチ。デフォルトで "master" と名称がつけられる。
リモートブランチリモートリポジトリ上のブランチを参照するブランチ。『リモート追跡ブランチ』とも言う。
リモート追跡ブランチローカルリポジトリ内にあり、リモートブランチの状態を保持するポインタ。"origin/master" というのは、"origin" というリモートリポジトリにある "master" ブランチを追跡していることを示す。
追跡ブランチリモート追跡ブランチからローカルブランチに checkout すると自動的に作成されるブランチ。リモートリポジトリのブランチと直接のつながりを持つローカルブランチ。
上流ブランチupstream branch。『追跡ブランチ』が追跡するブランチのこと。

参考:

『リモートブランチ』について
「Pro Git Book(日本語訳)」は『リモートブランチ』と『リモートリポジトリにあるブランチ』を両方、『リモートブランチ』として混在して記述しているのではないかと思う。非常に混乱したが「Pro Git Book」の原文や図などから
『リモートブランチ』=『リモート追跡ブランチ』で解釈した。






リモートリポジトリの複製


他メンバーと作業を共有するためにはリモートリポジトリが必要。リモートリポジトリを使うためにはローカルリポジトリに clone する。

$ git clone <リモートリポジトリの場所>


リモートリポジトリの場所は、http,httpsや、SSHプロトコル、同一マシンであればディレクトリパスを指定する。

リモートリポジトリを clone したら、自動的に "master" ブランチを作成し、"origin/master" を追跡するようになる。



ローカルとリモート(リモート追跡ブランチ)両方のブランチ一覧を確認する。
-a オプションを使う。

$ git branch -a

* master
remotes/origin/HEAD -> origin/master
remotes/origin/master

リモートリポジトリを区別するために、"remotes/" が頭に付く。
"remotes/origin/HEAD" は、リモートリポジトリの現在の "HEAD" が存在するブランチを指す。



orign/master ブランチ以外のブランチを複製


git clone では "master" ブランチしかローカルに複製されていない。
他のブランチを確認するには、-r オプションを使う。

$ git branch -r

  origin/HEAD -> origin/master
origin/master
origin/new_branch

確認できたら、追跡ブランチを作れば良い。-b オプションを使う。

$ git checkout -b new_branch origin/new_branch



ローカルリポジトリのブランチを確認してみる。
 git branch -a
master
* new_branch
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/new_branch







ローカルリポジトリの変更を反映


ローカルリポジトリにコミットされた内容を他メンバーへ公開するために、ローカルリポジトリの内容をリモートリポジトリへ反映する。

$ git push <リモートリポジトリ> <ローカルのブランチ名>



試しに git push を引数無しで実行してみる。

$ git push


リモートリポジトリへ反映はできたようだが、警告が出た。
warning: push.default is unset; its implicit value has changed in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the traditional behavior, use:

git config --global push.default matching

To squelch this message and adopt the new behavior now, use:

git config --global push.default simple

When push.default is set to 'matching', git will push local branches
to the remote branches that already exist with the same name.

Since Git 2.0, Git defaults to the more conservative 'simple'
behavior, which only pushes the current branch to the corresponding
remote branch that 'git pull' uses to update the current branch.

See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)

この意味は、git push を引数無しで実行したことによる警告。

git push を引数無しで実行したときの動作が push.default が指定されていないため警告がでている。また、Git 2.0 以降では matching から simple にデフォルトが変わったと教えてくれている。

この警告を表示させないようにするためには、push.default の設定を追加すればよい。
Defines the action git push should take if no refspec is explicitly given. Different values are well-suited for specific workflows; for instance, in a purely central workflow (i.e. the fetch source is equal to the push destination), upstream is probably what you want. Possible values are:

  • nothing - do not push anything (error out) unless a refspec is explicitly given. This is primarily meant for people who want to avoid mistakes by always being explicit.


  • current - push the current branch to update a branch with the same name on the receiving end. Works in both central and non-central workflows.


  • upstream - push the current branch back to the branch whose changes are usually integrated into the current branch (which is called @{upstream}). This mode only makes sense if you are pushing to the same repository you would normally pull from (i.e. central workflow).


  • tracking - This is a deprecated synonym for upstream.


  • simple - in centralized workflow, work like upstream with an added safety to refuse to push if the upstream branch’s name is different from the local one.

    When pushing to a remote that is different from the remote you normally pull from, work as current. This is the safest option and is suited for beginners.

    This mode has become the default in Git 2.0.


  • matching - push all branches having the same name on both ends. This makes the repository you are pushing to remember the set of branches that will be pushed out (e.g. if you always push maint and master there and no other branches, the repository you push to will have these two branches, and your local maint and master will be pushed there).


To use this mode effectively, you have to make sure all the branches you would push out are ready to be pushed out before running git push, as the whole point of this mode is to allow you to push all of the branches in one go. If you usually finish work on only one branch and push out the result, while other branches are unfinished, this mode is not for you. Also this mode is not suitable for pushing into a shared central repository, as other people may add new branches there, or update the tip of existing branches outside your control.

This used to be the default, but not since Git 2.0 (simple is the new default).
引用:git help config

simple現在のブランチを "upstream branch" に push する。ただし "upstream branch" 名が現在のブランチと異なる場合は push しない。
Git 2.0 でデフォルトとなった。
matchingローカルとリモートで同一名称のブランチに全て push する。いままでのデフォルト。
nothing引数が省略された場合は push しない。
upstream現在のブランチに "upstream branch" が設定されている場合に push する。ブランチ名が異なっていても push する。
current現在のブランチをリモートの同一名称のブランチに push する。"upstream branch" が設定されていなくても push される。


git push 操作には、安全性と効率を考えて simple を設定しておく。

$ git config --global push.default simple






ここで、新しいブランチ "new_branch" を作り、リモートリポジトリへ反映してみる。
$ git push origin new_branch

$ git branch -a
master
* new_branch
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/new_branch

新たにリモート追跡ブランチ "remotes/origin/new_branch" が現れた。



さらに、上流ブランチと追跡ブランチの関係を見てみる。
-vv オプションを使う。

$ git branch -vv

  master     18ccb33 [origin/master] master 追加 3
* new_branch 6fd4bd7 new_branch 追加 1

"master" ブランチに対しては、上流ブランチ(upstream branch) "origin/master" が設定されているが、先ほど作った "new_branch" ブランチには、上流ブランチが設定されていない。



この状態で、"new_branch" ブランチを更新し、再度 push する。
ただし、次は引数無しで push してみる。
$ git push
fatal: The current branch new_branch has no upstream branch.
To push the current branch and set the remote as upstream, use

git push --set-upstream origin new_branch

push.defaultsimple を設定したので、上流ブランチが設定されていない "new_branch" ブランチでは、引数無しの push 操作ができないため警告がでている。


"new_branch" ブランチは、リモート追跡ブランチから checkout したものではなく、ローカルで準備したブランチのため、追跡ブランチにはなっていない。追跡ブランチとして使うためには、上流ブランチを設定すれば良い。

$ git branch --set-upstream-to=origin/new_branch new_branch

$ git branch -vv
master 18ccb33 [origin/master] master 追加 3
* new_branch 6fd4bd7 [origin/new_branch] new_branch 追加 1

上流ブランチが設定できたことがわかる。
これで push を引数無しでも実行できるようになった。


上流ブランチを設定する別のやり方もある。
push するときに合わせて指定すれば良い。
-u オプションを使う。

$ git push -u origin new_branch








リモートリポジトリの履歴取得


複数人で作業していると、リモートリポジトリは他メンバーによって、どんどん更新されていく。ローカルリポジトリも最新履歴を取り込む必要がある。
リモートリポジトリの最新履歴をローカルリポジトリに持ってくる操作が fetch

$ git fetch origin


fetch で更新されるのは、リモート追跡ブランチまで。
ローカルブランチはまだ更新されない。

git_remote02.png

リモートリポジトリの履歴をローカルブランチへ反映するためには、マージすればよい。
"master" ブランチへマージするために、"checkout" してから実行する。

$ git merge origin/master








リモートリポジトリの履歴反映


git fetch + git merge することで、リモートリポジトリから履歴を取得し、ローカルブランチへ反映した。
これをまとめて実行するのが、git pull 操作。

$ git pull origin master


ローカルブランチでの作業の区切りが悪い状態で実行してしまうと面倒なことになる。
なので、git pull は使わなくてもいい。git fetch + git merge で事が足りる。

操作が増えるほうが面倒だと思うかもしれないが、良くわからず git pull を実行したことでローカルブランチが大変なことになるぐらいなら、確実に操作できるほうが良い。

リモートリポジトリ、ローカルブランチ、リモート追跡ブランチなどの関係性が整理できていれば、「このタイミングであれば、まとめて実行したほうが楽。だから git pull を使っても大丈夫」だとわかる。それでなければ git pull を使わないほうが安全。





リモートリポジトリからのブランチ削除


役目を終えたローカルブランチを削除するには、-d オプションを使って、

$ git branch -d new_branch



リモートリポジトリに push されているのであれば、リモートリポジトのブランチも削除する。

$ git push origin :new_branch


この操作の意味を解説すると、
push 操作は、次のように、
$ git push origin new_branch

と、<リモートリポジトリ> <ローカルブランチ名> を指定するが、これは push する先のリモートリポジトリ側のブランチ名が省略されている。ローカル、リモートともに "new_branch" ブランチを指定したということで省略されている。

git push は、本来、次のコマンドのように :(コロン)を使って、リモートのブランチを指定する。

$ git push <リモートリポジトリ> <ローカルブランチ名>:<リモートのブランチ名>




話を戻して、リモートブランチの削除する操作は、git push origin :new_branch

これは push するローカルのブランチが指定されていない →「何もないブランチ」を「リモートのブランチ」へ push する → 「存在しない状態に更新」ということになり、ブランチが削除できる。


次のようにして削除することもできる。
こちらのほうが明示的に削除を指定するのでわかり易い。

$ git push --delete origin new_branch








まとめ


複数人で作業するときはリモートリポジトリが必要となる。リモートリポジトリとローカルリポジトリの関係性が理解できていれば、どのようにデータが更新されるのか理解できるので、コンフリクトしても焦らず対応できる。
git push の逆にあたる操作は、git pull ではなく、git fetch 。"push" の反対なので "pull" だと思ってしまうが、"pull" はローカルブランチに影響を与えてしてしまう操作だが、"push"・"fetch" ともローカルブランチに影響を与えない操作。
今回、リモートとローカルリポジトリの関係性を理解できたことで、今後のリモート操作が安心してできる。



gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミット履歴を変更する
git 変更をリセットする
git ファイルの追跡
git リリース準備
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする

2017年07月19日

git 変更をリセットする

コミット内容を訂正する操作(git commit --amend)、コミットの取り消し(git revert)は、コミット自体を無かったことにはできない。
これに対して、"reset" はコミットを無かったことにできる。
"reset" は前の状態に戻す操作。

"reset" がわかりにくいので、図を書いて整理してみた。

まず、"HEAD"、"ステージングエリア"、"作業ツリー" の関係について整理しておく。
  • 作業ツリー:最新のファイルの状態
  • ステージングエリア:コミットするためのファイルの状態
  • HEAD:最新コミットの状態


git_reset01.png
"ステージングエリア"、"作業ツリー" ともに "HEAD" に一致している。


コミット直後の reset


git_reset02.png
  • ファイルを変更したら、作業ツリーが変更
  • "git add" したら、ステージングエリアが変更
  • "git commit" したら、HEAD が変更(1つ進む)



"git reset" では、戻したいコミット位置を指定する。

今 "HEAD"はコミット(Z) 。これから1つ前のコミット(Y)に戻る場合(HEAD^)
git_reset03.png


すべて1つ前に戻す


作業ツリーも、ステージングエリアもすべて1つ前に戻す。すべてクリアされるので、ある意味、簡潔でわかりやすい。
$ git log --oneline
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset --hard HEAD^

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

ステータスを確認してみても、作業ツリーに何も変更がない。




git add を取り消す


"git add" を取り消す。作業ツリーはそのまま。
最新コミットを無かったことにし、かつ、ステージングも無かったことにする。
$ git log --oneline
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset HEAD^

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)

modified: index.html

no changes added to commit (use "git add" and/or "git commit -a")

ステータスを確認すると、作業ツリーには変更があるが、ステージングはされていない。





git commit を取り消す


"git commit" を取り消す。作業ツリー、ステージングエリアはそのまま。
単に最新コミット操作を無かったことにする。
$ git log --oneline
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset --soft HEAD^

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)

modified: index.html

ステータスを確認すると、ステージングがされた状態。





最新のコミット位置が "HEAD" なのに、"HEAD" に戻すとはどういうことか?

"HEAD" 位置ではなく、作業ツリー、ステージングエリアが先に進んでいる状態を想定。
コミット後に変更したものを "reset" する操作。
考え方は、コミット直後の "reset" と同じ。
git_reset04.png




git_reset05.png
"resret --soft" は、もともと "HEAD" に居るので、"HEAD" へ戻す操作をしても何も変わらない。



git_reset06.png
"resret --mixed" は、ステージングエリアを戻すので、"git add" を取り消すことになる。



git_reset07.png
"resret --hard" は、作業ツリーも戻す。すべて消え、最新コミット直後に戻る。





昔の状態に reset


コミットIDを指定して、以前のコミット状態に "reset" する。
git_reset08.png





すべて戻す


git_reset09.png
$ git log --oneline
fee81ff master 追加 5
67c1282 master 追加 4
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset --hard 1f391e3

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

ステータスを確認してみても、作業ツリーに何も変更がない。





git add を取り消す


git_reset10.png
$ git log --oneline
fee81ff master 追加 5
67c1282 master 追加 4
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset 1f391e3

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)

modified: index.html

no changes added to commit (use "git add" and/or "git commit -a")

ステータスを確認すると、作業ツリーには変更があるが、ステージングはされていない。
作業ツリーのファイルの中身は、コミット(E):fee81ff のまま。
これを再度ステージし、コミットしてみると、
$ git log --oneline
3d15bcd master "reset"後の再コミット:
1f391e3 master 追加 2
6edb23d master 追加 1

となり、ある意味 コミット(C):509957a ~ コミット(D):fee81ff までを "squash" した状態での再コミットと同じになる。





git commit を取り消す


git_reset11.png
$ git log --oneline
fee81ff master 追加 5
67c1282 master 追加 4
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset --soft 1f391e3

$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)

modified: index.html

ステータスを確認すると、ステージングがされた状態。
これも再度コミットすると、コミット(C):509957a ~ コミット(D):fee81ff までを "squash" した状態での再コミットと同じになる。





reset を無かったことにする


上記のように、近々のコミットを無かったことにして、昔の状態に "reset" するということはあまりないと思うが、この昔の状態に戻す "reset" の使い方としては、昔の動作を確認するために、一時的に環境をその当時の状態にし、動作確認するときに使える。

このような場合、動作確認が終われば、再度、元のコミットに戻す必要がある。
$ git reset --hard 1f391e3
$ git log --oneline
1f391e3 master 追加 2
6edb23d master 追加 1

$ git reset --hard ORIG_HEAD

$ git log --oneline
fee81ff master 追加 5
67c1282 master 追加 4
509957a master 追加 3
1f391e3 master 追加 2
6edb23d master 追加 1


"git reset --hard ORIG_HEAD" を使えば、直前の "reset" を無かったことにできる。
間違って "git reset" してしまった場合も使えるので、落ち着いて対応する。

ただし、コミットした状態に戻すだけなので、作業ツリーや、ステージングエリアで変更していたものは戻らないので注意。

また "git reset --hard ORIG_HEAD" は前回位置に戻すだけなので、さらに "git reset" を重ねてしまうと、前回位置が上書き更新され、最初の位置に戻せなくなるので注意。





まとめ


図を書いて整理したおかげで、動作が理解できた。
"reset" で、誤ってステージしてしまった・コミットしてしまった、という場合に修正できる。これを知らないとコミット履歴が汚くなる。"git reset" は便利だが、指定オプションを間違うと、目の前の作業内容を失うこともあるので、作業途中のファイルがあれば、"git stash" で退避しておくと、失敗しても救済できる。




gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミット履歴を変更する
git リモートでの操作
git ファイルの追跡
git リリース準備
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする

2017年07月18日

git コミットを取り消す

コミットしたものを取り消したい場面、
例えば、ある機能をコミットした後で、仕様変更が入り、運悪く、その機能がドロップしてしまった。
そのため、コミットしたものを削除することに。
再度、削除してコミットしても良いが、ケアレスミスで削除ミスが発生するかも。
コミットしたものを丸ごとなかったことにするのが確実。


コミットの取り消し


git_revert01.png
$ git log --oneline --graph
* e3ac1eb new_branch 追加 3
* 5cb8c6a new_branch 追加 2
* 4f42c71 new_branch 追加 1
* 1f391e3 master 追加 2
* 6edb23d master 追加 1

コミット(2):5cb8c6a と、コミット(3): e3ac1eb の差分を確認すると、
$ git diff 5cb8c6a e3ac1eb
diff --git a/index.html b/index.html
index 7743992..65aebf5 100755
--- a/index.html
+++ b/index.html

@@ -13,5 +13,6 @@
<h2>new_branch</h2>
<p>new_branch 追加 1</p>
<p>new_branch 追加 2</p>
+ <p>new_branch 追加 3</p>
</body>
</html>


コミット(3): e3ac1eb を取り消してみる。

$ git revert e3ac1eb

$ git log --oneline --graph
* 2e3f015 Revert "new_branch 追加 3"
* e3ac1eb new_branch 追加 3
* 5cb8c6a new_branch 追加 2
* 4f42c71 new_branch 追加 1
* 1f391e3 master 追加 2
* 6edb23d master 追加 1

コミット(3):e3ac1eb と、HEADコミット: 2e3f015 の差分を確認すると、
追加されていた変更が削除されている。
$ git diff e3ac1eb 2e3f015
diff --git a/index.html b/index.html
index 65aebf5..7743992 100755
--- a/index.html
+++ b/index.html

@@ -13,6 +13,5 @@
<h2>new_branch</h2>
<p>new_branch 追加 1</p>
<p>new_branch 追加 2</p>
- <p>new_branch 追加 3</p>
</body>
</html>


"revert" 操作は、指定したコミットを打ち消す内容を作成し、コミットする。

コミットした内容を無かったことにするのではなく、コミットした履歴も、コミットを訂正した履歴も両方ともにきちんと残る。またコミットした内容を取り消すときに、gitが訂正内容を準備するので、ヒューマンエラーによるミスを防ぐことにもつながり、訂正するときに便利。
git_revert02.png





複数のコミットの取り消し


取り消したいコミットが複数ある場合を考える。
git_revert01.png
$ git log --oneline --graph
* e3ac1eb new_branch 追加 3
* 5cb8c6a new_branch 追加 2
* 4f42c71 new_branch 追加 1
* 1f391e3 master 追加 2
* 6edb23d master 追加 1

コミット(1):4f42c71 と、コミット(3): e3ac1eb の差分を確認すると、
$ git diff 4f42c71 e3ac1eb
diff --git a/index.html b/index.html
index 200b61d..65aebf5 100755
--- a/index.html
+++ b/index.html

@@ -12,5 +12,7 @@
<h2>new_branch</h2>
<p>new_branch 追加 1</p>
+ <p>new_branch 追加 2</p>
+ <p>new_branch 追加 3</p>
</body>
</html>


コミット(3):e3ac1eb と、コミット(2): 5cb8c6a を取り消してみる。
コミットを複数取り消す場合、コミットした順番と逆の順序で、"revert" することで目的を達する。
$ git revert e3ac1eb
$ git revert 5cb8c6a

$ git log --oneline
3ff18a9 Revert "new_branch 追加 2"
06f718a Revert "new_branch 追加 3"
e3ac1eb new_branch 追加 3
5cb8c6a new_branch 追加 2
4f42c71 new_branch 追加 1
1f391e3 master 追加 2
6edb23d master 追加 1

git_revert03.png

単純だが、これだと訂正したいコミット数だけ、訂正コミットを行うので手間がかかるし、後から見たときに見通しが良くない。

ただ、確実に1つ1つ対応しないと不安だという場合は、このようにすべきであるが、
せめて後から履歴を眺めたときに見通しをよくするために、訂正コミットをすべて行った後、"git rebase -i" で "squash" するのがおすすめ。
訂正時に1つ1つ対応することで安心できるし、コミット履歴が整理されるので見通しが良くなる。



せめて "rebase" の手間ぐらい省きたい場合は、"revert" 操作に、"-n" or "--no-commit" オプションを指定するとよい。"-n" オプションは、逆マージまで行い、コミットを行わない操作。
コミットした順版と逆順でマージ操作のみを行い、最後にコミットする。

$ git revert -n e3ac1eb
$ git revert -n 5cb8c6a
$ git commit

$ git log --oneline
eaac52c Revert "new_branch 追加 1" & "new_branch 追加 2"
e3ac1eb new_branch 追加 3
5cb8c6a new_branch 追加 2
4f42c71 new_branch 追加 1
1f391e3 master 追加 2
6edb23d master 追加 1

git_revert04.png
後から、"squash" する必要がないので、少し手間が省ける。





マージコミットの取り消し


ブランチを切って対応した機能を本流にマージした後に仕様ドロップになってしまった場合を考える。
git_revert05.png
$ git log --oneline --graph
* ca27af6 Merge branch 'new_branch'
|\
| * 5cb8c6a new_branch 追加 2
| * 4f42c71 new_branch 追加 1
* | 509957a master 追加 3
|/
* 1f391e3 master 追加 2
* 6edb23d master 追加 1


マージコミット(D):ca27af6 に対して、"revert" 操作を行うと、
$ git revert ca27af6
error: Commit ca27af63e56738811fb79d06a44b35e5de60dd21 is a merge but no -m option was given.
fatal: revert failed

と、エラーメッセージが出る。
つまりは、マージコミットに対しては普通には "revert" できない。"-m" オプションを指定しなさいと。

"-m" or "--mainline" を使うときは続けて、"parent-number" を指定する。
"parent-number" は、"git show" を見るとわかる。
$ git show ca27af6
commit ca27af63e56738811fb79d06a44b35e5de60dd21
Merge: 509957a 5cb8c6a
・・・

マージすると、親コミットが複数存在する。"git show" で示される、①509957a と、②5cb8c6a が親コミット。

マージコミットに対して "revert" で訂正した場合、どのブランチの流れで訂正するのか?
git_revert06.png

これを指定するのが、"-m" オプション。
"git show" の結果で、"master" ブランチ側は1、"new_branch" ブランチ側が2 とわかった。
"new_branch" ブランチを、"master" ブランチへマージしたことを訂正したいので、"-m 1" を指定する。

$ git revert -m 1 ca27af6

$ git log --oneline --graph
* a30ed5c Revert "Merge branch 'new_branch'"
* ca27af6 Merge branch 'new_branch'
|\
| * 5cb8c6a new_branch 追加 2
| * 4f42c71 new_branch 追加 1
* | 509957a master 追加 3
|/
* 1f391e3 master 追加 2
* 6edb23d master 追加 1






マージコミット取り消しの注意点


マージするのが早かったと思い、一旦、"revert" でマージコミットを取り消し、取り消したブランチ側で継続して作業を行い、再度マージしたとする。
git_revert07.png
$ git log --oneline --graph
* a6ea348 new_branch 追加 4
* 20655ff new_branch 追加 3
* 5cb8c6a new_branch 追加 2
* 4f42c71 new_branch 追加 1
* 1f391e3 master 追加 2
* 6edb23d master 追加 1

コミット(1):4f42c71 と、コミット(4): a6ea348 の差分を確認する。
$ git diff 4f42c71 a6ea348
diff --git a/index.html b/index.html
index 200b61d..7743992 100755
--- a/index.html
+++ b/index.html

@@ -12,5 +12,6 @@
<h2>new_branch</h2>
<p>new_branch 追加 1</p>
+ <p>new_branch 追加 2</p>
</body>
</html>
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..02ab331
--- /dev/null
+++ b/readme.txt

@@ -0,0 +1,3 @@
+readme
+readme 変更
+



この状態でマージしてみる。

$ git checkout master
$ git merge new_branch

$ git log --oneline --graph
* 77c32e4 Merge branch 'new_branch'
|\
| * a6ea348 new_branch 追加 4
| * 20655ff new_branch 追加 3
* | a30ed5c Revert "Merge branch 'new_branch'"
* | ca27af6 Merge branch 'new_branch'
|\ \
| |/
| * 5cb8c6a new_branch 追加 2
| * 4f42c71 new_branch 追加 1
* | 509957a master 追加 3
|/
* 1f391e3 master 追加 2
* 6edb23d master 追加 1



マージ前後の差分を確認してみる。コミット:a30ed5c と、コミット: 77c32e4 の差分を確認。
$ git diff a30ed5c 77c32e4
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..02ab331
--- /dev/null
+++ b/readme.txt

@@ -0,0 +1,3 @@
+readme
+readme 変更
+



すると、訂正したマージコミット分の差分(コミット(1):4f42c71 、コミット(2):5cb8c6a)が反映されていないことがわかった。
git_revert08.png

これは、マージしたことをブランチ操作として管理しているが、"revert" はブランチ管理外の操作のため、マージが訂正され、本流から削除されてしまったことはブランチの動きとして把握していない。
そのため前回マージされたコミット位置(コミット(2): 5cb8c6a)が基準となり、今回のマージが作成された。結果、コミット(1):4f42c71 、コミット(2):5cb8c6a がマージに含まれない状態でマージされている。

この動作を理解できていないと、マージしたつもりでも実際にはマージできていないので注意が必要。


では、この場合はどうするのか?

この場合、マージコミットを "revert" したものを、再度 "revert" し直すしかない。
git_revert09.png





まとめ


コミットした内容を取り消すには、再度、元の形に変更したファイルでコミットし直せば良い。
ただ、ヒューマンエラーを起こさないためにも、git に備わる機能で "revert" という便利な機能を使って、スマートな対応をしたい。
ただし、マージコミットを訂正した場合は、その後の動作を理解していないと嵌る。無駄な労力を使わないためにも、しっかりと理解して使いたい。


gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミット履歴を変更する
git 変更をリセットする
git リモートでの操作
git ファイルの追跡
git リリース準備
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする

2017年07月14日

git コミット履歴を変更する

コミットした後で、間違ってコミットした!とか、コミットメッセージが適切ではなかったとか、さっきのコミットと今の変更は合わせてコミットしたほうがよかったとか。
そんな場合に、コミットした内容を訂正する方法。

直前のコミットの修正


直前のコミット限定の操作。
"git commit --amend" を使う。

コミットメッセージの修正


コミット後に、コミットメッセージが不足していたとか、レビューで「コミットメッセージが対応内容と一致していない」と指摘されたときとかに使う。
$ git log --oneline
02c1321 commit 内容
9fe9714 master 追加 2
1c0a080 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html

コミットID:02c1321 のコミットメッセージを修正する。

$ git commit --amend


エディタが立ち上がるので修正する。


$ git log --oneline
ecb63b0 master 追加 3 (コミットメッセージを修正)
9fe9714 master 追加 2
1c0a080 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html

コミットメッセージが修正できた(コミットIDも変更される)


コミットの修正


コミットした後に、1ファイルをステージするの忘れていたとか、"stash" から戻すのを忘れて別の修正をコミットしたとか、そんなときには必要なファイルを "git add" してステージしてから実行する。
$ git log --oneline
a383399 master 追加 3 (間違ったコミット)
9fe9714 master 追加 2
1c0a080 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html

$ git add index.html
$ git commit --amend

$ git log --oneline
2e3f7ae master 追加 3 (コミット内容を修正)
9fe9714 master 追加 2
1c0a080 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html




これで、コミット後に、あっ!しまった!!! と思うことがあっても落ち着いて作業できる。ただし、この操作はコミットしたという履歴を無かったことにはできない。あくまでコミットした履歴内容を訂正するだけ。






過去のコミットの修正


"git commit --amend" は直前のコミットに対してのみ。2つ前、3つ前のコミットを修正したい場合はどうするのか?
その場合は、ブランチの付け替えとして使う "rebase" 操作を使う。
"rebase" を使う場面は付け替え操作よりも、コミット内容の修正が多いと思う。


"-i" or "--interactive" オプションを使うと、「interactiveモード」で "rebase" 操作が使える。
具体的な操作は後から指定するから、とりあず指定したコミット履歴を列挙して!という操作。

git_rebase10.png
$ git log --oneline
762bccf master 追加 3
9fe9714 master 追加 2
1c0a080 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html


$ git rebase -i <コミットID>


とすると、指定したコミットIDの次のコミット以降をコミット履歴の修正対象とする。

コミット(C):1c0a080 のコミット履歴を修正したい場合、1つ前の コミット(B):07c5066 を指定する。

$ git rebase -i 07c5066

pick 1c0a080 master 追加1
pick 9fe9714 master 追加 2
pick 762bccf master 追加 3

# Rebase 07c5066..762bccf onto 07c5066 (3 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out


すると、エディタが立ち上がり、そこには先頭に対象コミットが古いコミット~新しいコミットの順で並んでいる。
この "pick <コミットID> <コミットメッセージ>" と記載されている箇所に注目する。

これを以下の内容で操作することで、コミット履歴を修正できる。
Commands処理
「p」or「pick」このコミットを適用する
「r」or「reword」このコミットを適用するが、コミットメッセージは編集する
「e」or「edit」このコミットを適用するが、ファイルを修正する
「s」or「squash」このコミットを適用するが、1つ前のコミットと合体させる
「f」or「fixup」「squash」と同じだが、このコミットメッセージは破棄する
「x」or「exec」以降のコマンドをshellで実行する
「d」or「drop」このコミットを削除する



対象コミットの指定は、"HEAD" を使い、「"HEAD" から何個目までの履歴を対象とする」と指定することもできる。

$ git rebase -i HEAD~3








コミットメッセージを修正


「reword」を指定。

reword 1c0a080 master 追加1
pick 9fe9714 master 追加 2
pick 762bccf master 追加 3

として、エディタを終了すると、
続けて、コミットメッセージを修正するエディタが立ち上がるのでメッセージ内容を変更し終了する。
$ git log --oneline
0e83bbd master 追加 3
cd5173c master 追加 2
d41ea49 master 追加 1(コミットメッセージを修正)
07c5066 master branch :変更 1
830f4ca Add new file :index.html

コミットメッセージが変更された(変更したコミット以降もコミットIDが変更される)。
git_rebase11.png





コミットをまとめる1


1つ前のコミットとまとめる。
"merge" 操作時にコミットをまとめる操作があるが、"rebase" でまとめてからマージするほうが、使用頻度は多いと思う。

pick 1c0a080 master 追加1
squash 9fe9714 master 追加 2
pick 762bccf master 追加 3

# This is a combination of 2 commits.
# The first commit's message is:

master 追加1

# This is the 2nd commit message:

master 追加 2

コミットメッセージを入力するエディタが立ち上がるのでコミットメッセージを入力し終了。
$ git log --oneline
64a5d77 master 追加 3
27f2fe8 master 追加1 & master 追加 2 (コミットを圧縮)
07c5066 master branch :変更 1
830f4ca Add new file :index.html


コミットが圧縮された(変更したコミット以降もコミットIDが変更される)。
git_rebase12.png



コミットをまとめる2


"squash" と "fixup" の違いは、コミットメッセージが編集できるかどうか。

pick 1c0a080 master 追加1
fixup 9fe9714 master 追加 2
pick 762bccf master 追加 3


終了すると、メッセージを編集するエディタは起動せず完了した。
$ git log --oneline
eb3834a master 追加 3
eb91f22 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html

"fixup" を指定したコミットはまとめられたが、コミットメッセージは削除された(変更したコミット以降もコミットIDが変更される)。
git_rebase13.png




コミットの順番を入れ替える


作業の区切りの良いところでコミットしていたが、後で履歴を見たときに作業の意図がわかるように整然とコミット順番を並べたいと思ったときにはコミット順番を入れ替える。

コミットを示している行を、意図したコミット順序に並び替え、エディタを終了させる。

pick 1c0a080 master 追加1
pick 762bccf master 追加 3 <<入れ替えた↓
pick 9fe9714 master 追加 2 <<入れ替えた↑


ここで注意したいことは、コミット順番が入れ替わるということは、変更差分の順番も変わるということ。
git が上手いことやってくれる訳ではないので、コンフリクトが発生することを前提として、自分の手できちんと組み替え直さないとダメ。
特に、この作業をやっている時点では、きっと自分自身では変更差分の最終形がわかっているので、この操作でのコンフリクトを理解せずに、最初のコンフリクトで思わず最終形で修正し、以降のコンフリクトでも同じ状態で保存してしまうと、本来発生していたはずの差分が発生せず、結果的にコミットが圧縮(squash)された状態になってしまう。

コミット順番の入れ替えは、過去のファイル差分の入れ替えにあたるので、確実に差分の登録順番を意識して修正すること。
$ git log --oneline
1d79372 master 追加 2'
7335bd0 master 追加 3'
1c0a080 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html

コミットの順番が変更された(変更したコミット以降もコミットIDが変更される)。
git_rebase14.png




コミットを分割する


コミットしたが、内容的に2つの目的を含んだコミットだったので、別々にコミットしたほうが良かったと思った場合にコミットを分割する。

コミット(D):9fe9714 の内容を分割したいとすると、

pick 1c0a080 master 追加1
edit 9fe9714 master 追加 2
pick 762bccf master 追加 3


終了させると、"rebase" 処理が指定した箇所で止まり、shell に制御が戻される。
Stopped at 9fe9714240de35b1ed77601d14bd965e647f226c... master 追加 2
You can amend the commit now, with

git commit --amend

Once you are satisfied with your changes, run

git rebase --continue

ここで変更内容を確認してみると、コミット(D):9fe9714 までコミットされた状態で止まっている。

既にコミット(D):9fe9714 が含まれているので、このコミット分割するためには、直前のコミットを修正する
対象ファイルを変更し、ステージしたら、直前のコミットを修正する。

$ git add index.html
$ git commit --amend -m "master 追加 2-1"


分割したかったので、残りの変更も追加する。

$ git add index.html
$ git commit -m "master 追加 2-2"


コミットが修正できたら、"rebase" を継続する。

$ git rebase --continue

$ git log --oneline
48f861d master 追加 3
c9d0e42 master 追加 2-2
a85263e master 追加 2-2
1c0a080 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html

コミットが分割された(変更したコミット以降もコミットIDが変更される)。
git_rebase15.png




コミットを挿入する


"edit" は分割だけではない。"rebase" 処理が指定場所で中断するので、その間にコミットを行えば、コミットを挿入することもできる。

pick 1c0a080 master 追加1
edit 9fe9714 master 追加 2
pick 762bccf master 追加 3

Stopped at 9fe9714240de35b1ed77601d14bd965e647f226c... master 追加 2
You can amend the commit now, with

git commit --amend

Once you are satisfied with your changes, run

git rebase --continue

$ git add index.html
$ git commit -m "master 挿入"
$ git rebase --continue

$ git log --oneline
ded34b1 master 追加 3
ecba81d master 挿入
9fe9714 master 追加 2
1c0a080 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html

コミットが挿入された(変更したコミット以降もコミットIDが変更される)。
git_rebase16.png




コミットを削除する1


コミットを削除する。
「コミットを削除」と言っても、変更差分も無かったことにはしてくれないので、コンフリクトが起こることを前提とし、コンフリクトした場合はしっかりと内容確認して削除しないといけない。

pick 1c0a080 master 追加1
drop 9fe9714 master 追加 2
pick 762bccf master 追加 3

$ git log --oneline
f82c062 master 追加 3
1c0a080 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html

コミットが削除された(変更したコミット以降もコミットIDが変更される)。
git_rebase17.png




コミットを削除する2


"drop" を指定せずとも、並んでいるコミットの行から不要なものを削除しても同様。

pick 1c0a080 master 追加1
<<< 行を削除 >>>
pick 762bccf master 追加 3

$ git log --oneline
29ff05c master 追加 3
1c0a080 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html

コミットが削除された(変更したコミット以降もコミットIDが変更される)。
git_rebase18.png





shell を使う


"exec" は趣旨が異なる。コミットに対する操作ではない。
"rebase" の途中に指定した処理を挿入することができる。

全く意味が無いが、例として、"ls -a" コマンドを実行してみる。

pick 1c0a080 master 追加1
pick 9fe9714 master 追加 2
exec ls -a
pick 762bccf master 追加 3

Executing: ls -a
. .. .git index.html
Successfully rebased and updated refs/heads/master.

"ls -a" が途中で実行されたのがわかる。


コミット履歴の修正で使う場合、次のようなやり方がある。
今の作業ツリー上の変更は、コミット(D):9fe9714 と合わせてコミットしたほうが良かったと思った場合に、"stash" を使って設定してみる。
今の変更分を一旦 "stash" しておく。

$ git stash
$ git rebase -i HEAD~3



"exec" を使い、git 操作を記述する。

pick 1c0a080 master 追加1
pick 9fe9714 master 追加 2
exec git stash pop; git commit -a --amend
pick 762bccf master 追加 3

$ git log --oneline
ba09ebd master 追加 3
9706693 master 追加 2(追加修正あり)
1c0a080 master 追加1
07c5066 master branch :変更 1
830f4ca Add new file :index.html

コミット(D):9fe9714 を "rebase" したところで、shell の実行に移る。
"git stash pop" を実行し、変更したファイルを作業ツリーに戻し、続けて、
"git commit -a --amend" で直前のコミットを、現在の作業ツリーのファイルで修正コミットする(変更したコミット以降もコミットIDが変更される)。
git_rebase19.png






まとめ


"git commit --amend" はまだしも、
"rebase" は過去の歴史を変えるという非常に強力な操作のため、普段は "reword" "squash" ぐらいの内容に留めておくのが安全で良い。

コミット履歴の変更を行えば、ほとんど場合でコンフリクトが発生すると考えておき、コンフリクトの修正もコミットという履歴を理解したうえで、慎重に対応すべき。
後から修正できるからと、とりあえずコミットしておけばよいとか思わないように。
特に、他メンバーに展開したブランチに対する、コミット履歴の修正は混乱を招くだけなのでダメ。

コミット履歴の修正は、しまった!と思うような場合に、「非効率なリカバリではなく、効率よくリカバリできる手段としてのみ使用できるもの」ぐらいの意識でいたほうが良い。




gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミットを取り消す
git 変更をリセットする
git リモートでの操作
git ファイルの追跡
git リリース準備
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする

2017年07月13日

git ブランチを付け替える

ブランチを統合する操作として "merge" がある。
もう一つ別の方法として、 "rebase" がある。





基本的な操作


git_rebase01.png



"new_branch" ブランチを "master" ブランチへ "merge" すると、以下のようになる。

$ git checkout master
$ git merge new_branch

git_rebase02.png
$ git log --oneline --graph
* e379414 Merge branch 'new_branch' Conflicts -> modified
|\
| * 0792fc6 new_branch 追加 3
| * ef693eb new_branch 追加 2
| * 24edebf new_branch 追加1
* | 762bccf master 追加 3
* | 9fe9714 master 追加 2
* | 1c0a080 master 追加1
|/
* 07c5066 master branch :変更 1
* 830f4ca Add new file :index.html


これを "rebase" を使って操作してみる。

"rebase" は、「re-base」のこと。ブランチの分岐元を付け替える操作。
分岐元を変更したい("rebase" したい)ブランチへ切り替える。今は、"new_branch" を変更する。

$ git checkout new_branch


"new_branch" ブランチの分岐元を、"master" ブランチの "HEAD" に切り替える。

$ git rebase master


コンフリクトが起きた場合は、コンフリクトを修正後、修正ファイルをステージし、継続する。

$ git add index.html
$ git rebase --continue


"rebase"が終了すると、以下のようになる。
git_rebase03.png
$ git log --oneline --graph
* 22d32e0 new_branch 追加 3
* d7e8b73 new_branch 追加 2
* 5893adb new_branch 追加1
* 762bccf master 追加 3
* 9fe9714 master 追加 2
* 1c0a080 master 追加1
* 07c5066 master branch :変更 1
* 830f4ca Add new file :index.html

"new_branch" は分岐元が付け替えられている。そして、各コミットも改めてコミットされてたのでコミットIDが変わっている。

この状態で、"master" ブランチで、"merge" を行うと、

$ git checkout master
$ git merge new_branch

コミット状態がきれいになっているので Fast-forward マージ が行われる。
Updating 762bccf..22d32e0
Fast-forward
index.html | 3 +++
1 file changed, 3 insertions(+)

git_rebase04.png
$ git log --oneline --graph
* 22d32e0 new_branch 追加 3
* d7e8b73 new_branch 追加 2
* 5893adb new_branch 追加1
* 762bccf master 追加 3
* 9fe9714 master 追加 2
* 1c0a080 master 追加1
* 07c5066 master branch :変更 1
* 830f4ca Add new file :index.html


ログを見ると、単にマージしたときと統合内容は同じだが、こちらのほうがブランチの分岐の履歴が無く、すっきりとした状態になる。

ただし、作業した結果が残らない履歴は、個人的には好みではない。

使うのであれば、本流のブランチへ統合するときに行うのではなく、バグ修正や、機能開発などの作業ブランチにおいて、個人の作業範疇でさらにブランチを切る必要があった場合に、最終的なブランチの結果としてコミットをまとめるときに使うことをすすめる。これであれば、個人単位での作業としては履歴が残ることと、後から履歴を見たときに作業の都合のために切ったブランチが全体の見通しを悪くしてしまうことを避けることができる。





onto オプション


"new_branch" ブランチを切って開発中に、開発のためにバグ修正が必要だったので "sub_branch" ブランチを切って修正した。このバグ修正が他の機能開発でも必要だとわかったので "master" ブランチへ統合したい。
git_rebase05.png

"new_branch" ブランチはまだ開発途中で取り込めない。"sub_branch" のみ取り込みたい。
必要なコミットは、コミット(X):0cb061a、コミット(Y):0a8d056 の2つのみ。
ここで "rebase" をしてみる。

$ git checkout sub_branch
$ git rebase master

"master" ブランチへマージする。

$ git checkout master
$ git merge sub_branch

Updating 762bccf..ce98d22
Fast-forward
index.html | 3 +++
1 file changed, 3 insertions(+)

$ git log --oneline --graph
* ce98d22 sub_branch 追加 2
* 216520c sub_branch 追加 1
* b4d8495 new_branch 追加1
* 762bccf master 追加 3
* 9fe9714 master 追加 2
* 1c0a080 master 追加1
* 07c5066 master branch :変更 1
* 830f4ca Add new file :index.html


git_rebase06.png

この場合、今はまだ取り込みたくなかった コミット(1):24edebf が一緒に取り込まれてしまった。

このような変則的な取り込みを行うときに、"onto" オプションを使う。

指定がややこしいが、以下のようにブランチを指定する。
現在、チェックアウトしているブランチに対して、

$ git rebase --onto <どこへ> <どこから>



今の操作であれば、"sub_branch" ブランチをチェックアウトした状態で、"new_branch" から、"master" へ、切り替える操作。

$ git checkout sub_branch
$ git rebase --onto master new_branch

"master" ブランチへマージする。

$ git checkout master
$ git merge sub_branch

Updating 762bccf..9617339
Fast-forward
index.html | 3 +++
1 file changed, 3 insertions(+)

$ git log --oneline --graph
* 9617339 sub_branch 追加 2
* 66d8fb8 sub_branch 追加 1
* 762bccf master 追加 3
* 9fe9714 master 追加 2
* 1c0a080 master 追加1
* 07c5066 master branch :変更 1
* 830f4ca Add new file :index.html

git_rebase07.png


これは、ちょっと特殊な場面だったが、次のような場合も、"oonto" オプションを使えば解決できる。

"new_branch" ブランチから、新たなブランチを切って作業を進めようとしていたが、誤って、"master" ブランチから、ブランチを切って作業していたことに気づいた。"new_branch" ブランチも履歴が進んでしまった。本当は コミット(1):24edebf からブランチを切りたかった。
git_rebase08.png

こういう場合に、"onto" オプションをつけて "rebase" をすることで修正できる。
(付け替え先はブランチ名ではなく、コミットIDを指定する。ブランチ名だと、先頭のコミットが付け替え先になる。)

$ git checkout sub_branch
$ git rebase --onto 24edebf master

$ git log --oneline --graph
* 026323a sub_branch 追加 2
* 29540b6 sub_branch 追加 1
* 24edebf new_branch 追加1
* 07c5066 master branch :変更 1
* 830f4ca Add new file :index.html

git_rebase09.png





rebase と cherry-pick


上記のような "rebase" の操作は、実は連続で cherry-pick を実行することでも同じ結果が得られる。
少ないコミットであれば、cherry-pick で操作すれば済むが、ブランチ丸ごとに対して操作することに対して、簡単に cherry-pick 操作できるようしたものが "rebase" だともいえる。





まとめ


"rebase" することのメリットとしては、
  • コミットログがきれいになる。
    無数に入り組んだ履歴ではなく、必要な情報が俯瞰できるようになり、後から履歴を見たときにわかりやすくなる。その結果、運用、保守、生産性の効率が期待できる。

という点で、作業の効率化のためにもどんどんブランチを切って作業し、それを整理していくことは良いことだと思う。
ただ、これが許されるのは閉じられた世界での話。

チーム作業で公開してしまったブランチに対してはダメ。
ブランチを作り、リモートリポジトリに push した後、他のメンバーがそのブランチから新たにブランチを切って作業していたら、もうそのブランチは "rebase" してはいけない。禁止。

"rebase" してしまうと、過去のコミットIDが変わってしまう。派生させたブランチは親ブランチを追いかけることができなくなってしまう。"merge" するときに多大なる迷惑をかけてしまう。

なので、push する前のローカルなブランチであれば "rebase" で整理して、全体の見通しを良くすることは、以降の開発効率を上げるための一手間としておススメ。





gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git コミット履歴を変更する
git コミットを取り消す
git 変更をリセットする
git リモートでの操作
git ファイルの追跡
git リリース準備
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする