20190714のGitに関する記事は3件です。

vimでreStructuredTextを使うまでの準備

GUIで文字入力をしたい。もっと言えば、入力したMarkdown表記を即時プレビューしてくれるソフトウェアが欲しかった。
しかし、Markdownではなく、reStructuredTextを使いたいことに気づいた。
そして、Markdownならまだしも、reStructuredText用のGUIソフトが存在しない(私は見つけられなかった)。

あなたがこの記事を読んでいると言うことは、私はすでにVimmerとして名声を得られる1歩を踏み込んだことを褒めてくれていることでしょう。
私は、Vim使いとしての浅はかさをさらけ出しただけでなく、世にあふれるVimmerの一人に数えられることになったことも実感しており、それは事実だ。
それほどまでに、Vimmerは心が広い人たちばかりだと気づかされる。

先日もmattnさんにTwitterで質問したとき、返事をしてくれたからな。

(MacとWindowsを混在させた書き方をしているため、見にくいかもしれない。Vimmer達が許してくれたとしてもいずれ分けよう)
と言うことで、Vimの環境構築をすることにした。
訳の分からないところで躓いたため、Markdownを使いながらここに記録を残す。
もちろん、今回はvimを使って執筆している(まだまだ.vimrcの改良余地は大幅に残されているが)。

ちなみに、Github独自のMarkdown記法が存在する。

vimrc

今回、設定ファイルは必要最小限で実行する。
以下、起動直後の画面。

設定ファイルナッシングゥ.jpg

以下の内容で今回実行する。

.vimrc
" 以下、パッケージ管理
packadd minpac
call minpac#init()

" minpacでminpacを更新する場合は以下を有効にする。
call minpac#add('k-takata/minpac', {'type': 'opt'})

"call minpac#add('previm/previm', {'type': 'opt'})
call minpac#add('previm/previm')    " プレビュー用
call minpac#add('tyru/open-browser.vim')    " ブラウザを起動し、上記のprevimを実行させる。

" すべてのプラグインを起動時に読み込むのであれば、以下を実行。
packloadall

"let g:previm_open_cmd = 'open -a Safari'   " open-browser.vim
let g:previm_open_cmd = 'open -a Firefox'   " Safariが動かなかったため。

ディレクトリ準備

事前に、以下のディレクトリ構造を作成しておく必要がある。

ディレクトリ構造
[chesscommands@Qiita minpac] $ pwd
/Users/chesscommands/.vim/pack/develop/opt/minpac
[chesscommands@Qiita minpac] $ ls -R1 ../../../..
../../../../dict:
../../../../pack:   ←自分で作成
../../../../pack/develop:   ←自分で作成
../../../../pack/develop/opt:   ←自分で作成
../../../../pack/develop/opt/minpac:    ←自分で作成(配下のは違う)
../../../../pack/develop/opt/minpac/autoload:
../../../../pack/develop/opt/minpac/autoload/minpac:
../../../../pack/develop/opt/minpac/doc:
../../../../pack/develop/opt/minpac/plugin:
../../../../pack/develop/opt/minpac/test:
../../../../pack/develop/opt/minpac/tools:
../../../../pack/develop/start: ←自分で作成
../../../../writing:    ←自分で作成
../../../../writing/opt:    ←自分で作成
../../../../writing/start:  ←自分で作成

※自分で作成していないディレクトリは、コマンド実行によって作られたディレクトリになる。

最小パック.jpg

ディレクトリ作成後、ターミナル上で、git clone https://github.com/k-takata/minpac ~/.vim/pack/develop/opt/minpacを実行する(Mac用)。

git後.jpg

(当然だがMacの専用Pathになる。Windowsでは~やピリオド始まりのディレクトリ名が使えないはずなので)
そして、Windowsは、以下のコマンドでgitコマンドを実行する。

git clone https://github.com/k-takata/minpac "C:\Users\chesscommands\vimfiles\pack\develop\opt\minpac"

OSに限るのか否か知らないが、Path固定でしか操作方法を知らないので、このままで続ける。
実行後は、minpacディレクトリ配下に、上記のように、autoloadやdocディレクトリなどが(自動で)作られる。
正確には、cloneしている。

最小パック.jpg

下準備ができたため、exコマンド上で、call minpac#update()を実行する。
(当たり前だが、事前にディレクトリを作成しておかなければ、Vim本体の起動時にエラーが発生する。Windowsに限るが)

winのみerr.jpg

(Windowsでエラーを出せるならば、Macでも出せると思うのだが・・・なぜ出さなくていいと思ったのだろう)
あとはminpac(から自動)でprevimopen-browser.vimがcloneされる。
そうすれば、exコマンド上でPrevimOpenするだけでブラウザが開き、執筆中の記事がブラウザに表示される(Macのみ)。

Windowsの場合は、絶対Pathを指定しなければブラウザが起動してくれなかった。
以下の設定をする。

_vimrc
let g:previm_open_cmd = 'C:\/\/Program\ Files\/Mozilla\ Firefox\/firefox.exe'   " open-browser.vim
"let g:previm_open_cmd = '' " open-browser.vim

私の環境では、何の設定もしていないため、/記号やスペース記号を\記号で打ち消しておく必要がある。
また、Path指定が面倒ならば、空でも問題ない。その場合は、既定のブラウザが起動する。
そもそもMacのように、ブラウザ名だけで起動して欲しい(Helpを見てもよく分からなかった)。

しかも、Macのように、open関数(?)の引数にブラウザ名を指定した場合、その関数でエラーになるという現象に遭遇した。
それも解決方法が分からないままだった(だからフルPathを使った)。

開きたいのに開けない.png

OS独自の関数を外部から引っ張って用いているのかと思ったが、そうではないようだし・・・。
よく分からない。

環境用意放棄

ディレクトリなどを用意せず、アップデートコマンドだけを打ったとしてもエラーになるのは当たり前のこと。

minpacerr.jpg

Path固定と知らず、好きな場所で作業して悩まされたエラーだった。
ぬぅ。

その他

ちなみに、vimで執筆する場合は、モード切替を頻繁に行うはず。
そのため、ESCでインサートモードを抜けたときにIMEが確実にオフになってくれたらうれしい。
Ctrl+[の組み合わせもオフにする)

.gvimrc
inoremap <ESC> <ESC>:set iminsert=0<CR>
inoremap <CR>[ <CR>[:set iminsert=0<CR>

ノーマルモードで間違えて日本語入力にしてしまい、Escキーを押してもオフにならないのは何とかならないものか・・・。

set imdisableを使う方法もあるようだが、限定された場所でしか使えないらしく、今回見送り。
また、行内での検索もIMEがOffになってくれたらうれしい。
それが以下の設定になる。

.gvimrc
nnoremap <silent> f :set iminsert=0<CR>f
nnoremap <silent> F :set iminsert=0<CR>F

上記のEscキーなどでOffにする場合、この設定は不要に思うが、念のため設定した。
(後日:確実に不要だった。その理由は、Ctrl+oの組み合わせをするときに、これが有効だった場合、Escキーが押されたような挙動をしてしまい、想定外の挙動に繋がるため、使用を中止した)

完成

この設定をするだけなのに、数週間かかってしまった。それとも数ヶ月?

ブラウザ表示完了.jpg

他の人ならば数時間もかけずにできるだろうから私は本当に技術者に向いていないのかもしれないが、満足できた結果になったので問題なしとしよう。

困っている

今回のこととは全く無関係なのだが、Ctrl+hの組み合わせでバックスペース機能になるはずが、全く動かなくなる。
そのときの事象は、インサートモードで、command+vで貼り付けたあとに上記の組み合わせを用いたときに発生する。
コマンド上でのpを使って貼り付けた文字でも削除できない現象に見舞われた。
貼り付けた文字ではなく、普通に打ち込んだ文字を消すときには普通に機能する。
また、Enterキーの挙動が遅く反応するようになってしまった(俗に言うもっさり感が出てきた)。
あぁ解決できない。

ある程度の削除は解決した

set backspaceが有効化されていなかった。
設定ファイルに存在しないため、設定していないだけだったようだが、今回まで気づかなかったのはなぜだろう。
今まで普通に削除できたような気が・・・。

冒頭の躓き発言

今回執筆した経緯は、環境を整えても動いてくれなかったためだ。
その原因は、上記のminpac#addprevimをcloneするときの引数に余計な記述をしていたためだった。
そのため、全く動かず、かなり困惑した。
そもそも、なぜこの引数を付けたのか謎だ(多分・・・コピペが原因だろう)。
見直しても気づかなかったのだから記録に残したいと思うだろう?

おまけ

また、reStructuredTextとは関係ないが、previmはマークダウン形式の執筆にも対応しているため、ファイルの拡張子の対応範囲を拡大しておく必要がある。

.vimrc
autocmd BufNewFile,BufRead *.{md,markdown,mkd,mkdn} set filetype=markdown

そして、勝手に折りたたみが行われるため、それを抑止しなければならない。
もちろん自動的に折りたたまれる挙動が好きな人は何もしなくて構わない。

.vimrc
let g:vim_markdown_folding_disabled=1

次に、ショートカットキーを設定する。
執筆記事をプレビューさせるのに、Exコマンド上でコマンドを打つのがめんどくさいため、以下の設定を行い、Ctrl+pにて、代替させる。

.vimrc
nnoremap <silent> <C-p> :PrevimOpen<CR>

これで、ある程度は使いやすくなったはず。
何より、執筆環境が整った。
以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

git pull --rebase origin developを理解し使っていく

事の始め

先輩「git pull --rebaseを使ってね」
私「はい!(なんのことか全然知らないけど)」

rebase関連は全然知らなかったのでこれを機にしっかり見てみることにした。

実際に動作を見てみる

実際にチーム開発である例で確認してみる。
ローカルではdevelopブランチからfeature/something-1ブランチを切っている状態を想定(リモートにはプッシュしていない)。

figure-local.png

一方リモートでは誰かが作業を行ったプルリクエストがマージされている状態。

figure-remote.png

この状態からローカルに変更を適用する場合にgit pullgit pull --rebaseで何が違うのかを確認する。

git pull

remoteのdevelopブランチをpullする。

% git pull origin develop

git logで確認。

% git log
commit 2076b38bd7801d0271485fdb3baed5b074769397 (HEAD -> feature/something-1)
Merge: a25f40f 2db3d26
Author: dys <51311958+dys6@users.noreply.github.com>
Date:   Sun Jul 14 16:50:20 2019 +0900

    Merge branch 'develop' of github.com:dys6/git-practice into feature/something-1

commit 2db3d260a29b2f44fcc72c716bc4d7ec972335a8 (origin/develop)
Merge: f1d0035 f4f2e6e
Author: dys <51311958+dys6@users.noreply.github.com>
Date:   Sun Jul 14 16:45:23 2019 +0900

    Merge pull request #1 from dys6/feature/something-2

    Implement something-2

commit f4f2e6efdad3aab6d7d5330bbdef7fd9abadf4bd
Author: dys <51311958+dys6@users.noreply.github.com>
Date:   Sun Jul 14 16:45:11 2019 +0900

    Implement something-2

commit a25f40faef493d2f4c0589d2ad8ed5027fb4756d
Author: dys <51311958+dys6@users.noreply.github.com>
Date:   Sun Jul 14 16:44:12 2019 +0900

    Implement something-1

commit f1d0035dee3a67d8f2ecd1d22bad2a31613298b2 (develop)
Author: dys <51311958+dys6@users.noreply.github.com>
Date:   Sun Jul 14 16:42:31 2019 +0900

    first commit

figure-2.png

git pull --rebase

remoteのdevelopブランチをpull reabseする。

% git pull --rebase origin develop
% git log
commit c7d2da23a86f81db2f5c01fa445b9fc604beba8d (HEAD -> feature/something-1)
Author: dys <51311958+dys6@users.noreply.github.com>
Date:   Sun Jul 14 16:44:12 2019 +0900

    Implement something-1

commit 2db3d260a29b2f44fcc72c716bc4d7ec972335a8 (origin/develop)
Merge: f1d0035 f4f2e6e
Author: dys <51311958+dys6@users.noreply.github.com>
Date:   Sun Jul 14 16:45:23 2019 +0900

    Merge pull request #1 from dys6/feature/something-2

    Implement something-2

commit f4f2e6efdad3aab6d7d5330bbdef7fd9abadf4bd
Author: dys <51311958+dys6@users.noreply.github.com>
Date:   Sun Jul 14 16:45:11 2019 +0900

    Implement something-2

commit f1d0035dee3a67d8f2ecd1d22bad2a31613298b2 (develop)
Author: dys <51311958+dys6@users.noreply.github.com>
Date:   Sun Jul 14 16:42:31 2019 +0900

    first commit

figure-3.png

具体的に何が違ったのか

ただしgit pullしてマージの際にfast forwardが行える場合にはマージコミットは発生しない。今回の例はnon fast forwardである。

マージのコミット

git pullをした際には「Merge branch 'develop' of github.com:dys6/git-practice into feature/something-1」というメッセージのコミットが発生している。一方git pull --rebaseをした時にはそれが発生していない。
ただしgit pullしてマージの際にfast-forwardが行える場合にはマージコミットは発生しない。今回の例はnon-fast-forwardである。

「Implement something-1」コミットの履歴上の位置とハッシュ値

git pullではgit logで表示すると実際にコミットされた順番にコミットが表示される。ハッシュ値にも変化がない。一方git pull --rebaseは「Implement something-1」のコミットハッシュ値がa25f40faef493d2f4c0589d2ad8ed5027fb4756dからc7d2da23a86f81db2f5c01fa445b9fc604beba8dに変わっており、別のコミット扱いになっていることがわかる。また履歴上の位置も先頭になっている(コピーが作成される)。
リモートに変更がない状態でgit pull --rebaseしても履歴は変わらずコミットのハッシュ値も変わらない。

何が嬉しいのか

自分の作業ブランチに最新のdevelopブランチの変更を適用するためにgit pullを使っていると変更がある度にマージのコミットが発生してしまう。最終的にdevelopブランチにマージされるとdevelopの履歴が汚く?なってしまう。git pull --rebaseを使うことで不要なマージコミット発生させずに履歴を綺麗に保つことができる。

実際に使っていく

git pull --rebaseしてgit pushする

基本は自分のブランチで作業を行なってコミットしたらgit pushの前にgit pull --rebaseをしてリモートの変更をローカルに適用しながら作業を行なっていく形になる。
しかし、1回リモートにプッシュしたあと、同じブランチでまたコミットしてリモートに変更がある状態でgit pull --rebaseをしてからgit pushをしようとすると以下のように怒られる。

% git push origin feature/something-3
To github.com:dys6/git-practice.git
 ! [rejected]        feature/something-3 -> feature/something-3 (non-fast-forward)
error: failed to push some refs to 'git@github.com:dys6/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.

先輩「そういう時はgit push --force-with-leaseを使ってね」
私「はい!(全然知らないけど)」

forceオプションってなんとなく危ないっていう印象で全然使わず調べずだったのでこれを機に理解する。

そもそもなぜ怒られているのか

git pull --rebaseをしたことでorigin/feature/something-3をfast-forwardによって更新できなくなったからである。怒られている内容にもあるgit push --helpNote about fast-forwardsの項目について読むとプッシュはコミット履歴を失わないためにもfast-forwardによってローカルの変更を取り込める必要があるそうだ。

// git push --helpから引用
When an update changes a branch (or more in general, a ref) that used to point at commit A to point at another commit B, it is called a fast-forward update if and only if B is a descendant of A.
コミットAから別のコミットBを指すようにブランチ(一般的にはref)が変更された時、BがAの子孫である場合のみそれはfast-forward更新と呼ばれる。

リモートの状態から新たに「Fix something-3」をコミットしてgit pull --rebaseを実行するとローカルの状態になる。

figure-6.png

この時のsomething-3ブランチのリモートとローカルの履歴は枝分かれするように異なっており、ローカルのコミットとリモートのコミットは子孫関係ではなくなってしまっている。よってfast-forwardによる更新ができず、先に挙げたエラーが出ている。

figure-7.png

こういう状況が発生した時、リモートの「Fix something-3」を他の誰かがフェッチしていない時に限り、git push --forceを実行してリモートの履歴をローカルの履歴で上書きすることができる。
状況は違うけれどgit push --helpには以下のように書いてあった。

// git push --helpから引用
There is another common situation where you may encounter non-fast-forward rejection when you try to push, and it is possible even when you are pushing into a repository nobody else pushes into.After you push commit A yourself (in the first picture in this section), replace it with "git commit --amend" to produce commit B, and you try to push it out, because forgot that you have pushed A out already. In such a case, and only if you are certain that nobody in the meantime fetched your earlier commit A (and started building on top of it), you can run "git push --force" to overwrite it. In other words, "git push --force" is a method reserved for a case where you do mean to lose history.
他の人が同じレポジトリにプッシュしていない場合でもnon-fast-forward拒否に出くわすことがある。
コミットAをプッシュした後、それを忘れていて"git commit —-amend"でコミットBを作ってプッシュしようとした時である。そのような時、誰も以前のコミットAを取得していないことが確信できる場合のみに、"git push --force"を実行してそれを上書きすることができる。言い換えれば"git push --force"は履歴を失うことを意味するようなことをする時のために用意された方法である。

git push --forceとgit push --force-with-leaseの違い

どちらも履歴を上書きして強制的にプッシュするという点は変わりない。例えば、feature/something-3で他の人も作業していて「Fix something-3 by other person」をコミットしリモートにプッシュしたとする。自分のローカルではそのコミットがプッシュされる前にgit pull --rebaseをしている。この状況においてgit push --forceは実行できるが、git push --force-with-leaseは実行できない。仮にgit push --forceを実行するとリモートの「Fix something-3 by other person」のコミットは「Fix something-3」で上書きされなかったことになってしまう。git push --force-with-leaseではこれを防ぐことができるようだ。

figure-9.png

// git push --helpより引用
Usually, "git push" refuses to update a remote ref that is not an ancestor of the local ref used to overwrite it. This option overrides this restriction if the current value of the remote ref is the expected value. "git push" fails otherwise.
Imagine that you have to rebase what you have already published. You will have to bypass the "must fast-forward" rule in order to replace the history you originally published with the rebased history. If somebody else built on top of your original history while you are rebasing, the tip of the branch at the remote may advance with her commit, and blindly pushing with --force will lose her work.This option allows you to say that you expect the history you are updating is what you rebased and want to replace. If the remote ref still points at the commit you specified, you can be sure that no other people did anything to the ref. It is like taking a "lease" on the ref without explicitly locking it, and the remote ref is pdated only if the "lease" is still valid.

なんとなくforceのオプションは危ないし使っちゃダメ!っていう認識だったけれど、自分しか作業しないブランチとか必要な上書きしたい時とか適切な場面で使えばあんまり危なくなさそう、と感じた。

コンフリクトだ!

% git pull --rebase origin develop
From github.com:dys6/git-practice
 * branch            develop    -> FETCH_HEAD
First, rewinding head to replay your work on top of it...
Applying: Implement something-6
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
CONFLICT (add/add): Merge conflict in something-6.md
Auto-merging something-6.md
error: Failed to merge in the changes.
Patch failed at 0001 Implement something-6
hint: Use 'git am --show-current-patch' to see the failed patch
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort"

ちょこちょこコンフリクトを起こすが、手動で解消してgit addしてgit rebase --continueでOK。なんかやばそうやっぱりリベースやめたいっていう時はgit rebase --abortでリベース前に戻せる。git rebase --skipはコンフリクトを解消した結果リモートの最新コミットと差分がなくなりgit rebase --continueで進めなくなった際に使う。

% git add something-6.md
% git rebase --continue
Applying: Implement something-6
No changes - did you forget to use 'git add'?
If there is nothing left to stage, chances are that something else
already introduced the same changes; you might want to skip this patch.
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort"

最後に

理解したことを間違いがないように気をつけて書きましたが、もし間違っていたら優しく教えてください。

参考文献

rebase以前にmergeとfetchも正直よくわかっていなかったのですが、ここを見て理解できた気がします。わかりやすくて大変勉強になりました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Updates were rejected because the tip of your current branch is behind] エラー解説、解消法

Gitでバージョン管理をしていると、

! [rejected]

hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

こんな感じのエラーメッセージとともにpushが拒まれることがあります。

エラーの意味

このエラーは the tip of your current branch is behind この文章にもあるように、

プッシュしようとしている作業ブランチの最新のコミットが、リモートリポジトリの最新のコミットよりも後ろにある

という意味で、

最新のブランチをpullせずに編集してしまったり、
編集の際にgit reset などでどこかしらの過去のコミットの場面に戻って修正してしまっていたときに起こり得ます。

エラー解消法

解消法は2つあります。

git pullで最新の状態のブランチを取得してからpush

まず1つ目は Merge the remote changes (e.g. 'git pull') このヒントメッセージにもあるように、リモートリポジトリにある最新のブランチをpullするという方法です。

git pull (origin 作業ディレクトリ)

一旦最新のリポジトリを最新の状態にしてしまえば、このようなエラーが起きることはありません。

強制push(乱用注意)

めちゃくちゃ危険な方法ですが、以下のように今の状態で強制的にpushしてしまう方法もあります

git push -f リモートリポジトリ 作業ブランチ

これは共同開発をしている場合に乱用は避けなくてはいけませんが、 (他の人が、前の状態をpullしてた場合、強制pushの内容をpull出来なくなってしまったりして他の人にも影響が出る可能性もあるので)

1人で開発していて、他に迷惑がかかることがなかったり最新の状態をpullするとコンフリクトを起こすのをわかっていて解消がめんどくさいし不要というときなどに使うのかも

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む