#author("2024-11-10T21:56:25+09:00","default:okazaki","okazaki")
#author("2024-11-10T21:56:47+09:00","default:okazaki","okazaki")
#menu(howto/MenuBar)
* howto/git [#wa6b0ec5]
#contents
** 一行メモ [#b3117386]
- git clone コピーしたい作業ディレクトリ [ディレクトリ名]    #作業ディレクトリ内にあるローカルリポジトリをクローンし、現在のブランチをチェックアウトする(これでOKでしょう) --  &new{2020-11-05 (木) 04:48:32};
-- ブランチは、現在のブランチしか作られないので、必要に応じて作成する --  &new{2020-11-05 (木) 04:50:27};
-- 例) $ git clone gmx_chignolin gmx_chignolin2 --  &new{2020-11-05 (木) 04:53:01};
- git clone ssh://username@hostname/home/username/hogehoge/directory directory    #ベアリポジトリでない作業ディレクトリのあるリポジトリもクローンできるようである --  &new{2020-11-06 (金) 02:26:59};
-- cloneはできるが、pushは通常できない(する方法もあるようだ)。 --  &new{2020-11-17 (火) 15:31:12};
- ブランチ名を、hoge/fuge のように /を含めることができる(名前空間を切り分けできる)? リモートブランチ扱いになる?
- mergeしたときの変更内容の差分をみる --  &new{2023-01-13 (金) 09:39:12};
 git diff      eaad7c8 254b87b d3087fd  #changes when merging
 git diff -U10 eaad7c8 254b87b d3087fd  #changes when merging
 違いが出るのはなぜか?
- git subtree https://golden-lucky.hatenablog.com/entry/2020/05/04/154704 --  &new{2023-02-07 (火) 23:53:58};
- git fetch -p とは? --  &new{2023-09-14 (木) 10:27:27};
- ディレクトリ毎にgitconfigを変える https://maku77.github.io/p/hxyiu7g/ --  &new{2023-10-01 (日) 11:30:35};
- gitk  master_20220310..master_20230403 -- nameless_psiopt.c --  &new{2024-07-09 (火) 22:51:33};
- ファイルの各行を誰がいつ追加したかを調べる --  &new{2024-11-10 (日) 21:56:25};
 $ git blame -w make_widget.py
 $ gitk --all make_widget.py
 
 ファイルからファイルにコードが移動している場合などはこちらで。
 $ git blame -w -C -C -M make_widget.py  #-Cは1個は不十分、2個で事実上十分か。


#comment

//================================================================================
* はじめてのGit [#ec3c01dd]
こちらへ [[howto/git/forthefirsttime]]


//================================================================================
* COLOR(RED){リマインド}・COLOR(RED){注意事項} [#m116a942]
- [[コミットするときの注意点>#anchor1]]
- [[最初設定すると良いこと>#anchor2]]


//================================================================================
* The entire Pro Git book, written by Scott Chacon and Ben Straub [#se693a78]

https://git-scm.com/book/ja/v2

ここからダウンロード(2021/04/15)したPDF版のファイル
(ただしそのままでは不具合があるようだったので、macOSのプレビューで開いてから再度PDFで保存し作成した)。
2nd Edition (2014) のファイルです。
なお、旧版はDownloadsメニューにあるらしい。

このPDFは Scott Chacon and Ben Straubにより書かれたものであり、
ライセンスは [[CC BY-NC-SA 3.0>https://creativecommons.org/licenses/by-nc-sa/3.0/deed.ja]]です。

- 表紙と3章のみ
#ref(progit.chap3.pdf)
- 全て
#ref(progit.all.pdf)

以下は古いもの。
- 2016/12/19
#ref("progit-ja.1016.chap1-3,5.pdf")
- 2017/04/08
#ref(progit-ja.898.pdf)


//================================================================================
* ファイルの追加・名前変更・削除 [#q7d3dc85]

** ファイルを追加(追跡対象にする) [#b2f33658]
 例)
 git add .         # カレント以下のファイル(ディレクトリ含む)をステージに追加する
 git add ファイル  # ファイルをステージに追加する
 git add -u        # 追跡対象のファイルで修正があったものをステージに追加する

- 新規にファイルをステージに追加する(載せる)と、そのファイルは自動的に追跡対象になる。
- サブディレクトリに cdしてから addすることも可能。
- 空ディレクトリは addできないので習慣的に .gitkeepを置く。
   例) find . -type d -empty -not -path './.git*' -exec touch {}\/.gitkeep \;
- シンボリックリンクも addできる。
- add -f オプションは .gitignoreを無視してファイルを追加するときに指定。


** ファイル名変更 [#h7dc6c52]
 mv 変更前のファイル名 変更後のファイル名
 git add 変更前のファイル名  #削除した(無くなった)ことをステージに追加する
 git add 変更後のファイル名
 #gitとしては、削除されて、新しい名前で追加する形の操作となっている
 
 または
 git mv 変更前のファイル名 変更後のファイル名


** ファイルの削除 [#sf470bf1]
 rm ファイル名
 git add ファイル名
 #gitとしては、削除したファイル名、無いファイル、を追加する形の操作となっている
 
 または
 git rm ファイル名
 
 削除するファイルが、ステージに載っている場合は --cachedオプションが必要
 git rm --cached ファイル名  #ステージから削除し、ファイルを削除し、無いファイルを追加する形

- 補足

-- ステージから取り除く
 git reset HEAD ファイル名  #ステージのファイルが削除される形

-- ファイルの変更を取り消す(元に戻す)
 git checkout -- ファイル名  #作業ディレクトリのファイルが、チェックアウト時点に戻される

-- git rm を取り消す
 git reset HEAD ファイル名    #ステージのファイルが削除される形
 git checkout -- ファイル名   #作業ディレクトリのファイルが、チェックアウト時点に戻される

-- あるSHA1のファイルを作業ディレクトリに上書きして入れる
 git checkout sha1 -- hoge.c  #作業ディレクトリに上書き、ステージにものる。
 git reset HEAD hoge.c        #ステージのものを作業ディレクトリに上書き移動の形で消す。


//================================================================================
* ファイルの変更維持 [#o73f3ddf]

 ファイルを更新したが、その更新を維持したままにする(git pull等で更新されないようにする)
 git update-index --skip-worktree    ファイル名  # 更新されないように登録する
 
 git update-index --no-skip-worktree ファイル名   # 登録を解除する
 
 git ls-files -v | grep  ^S    # skip-worktreeしたファイルを表示する

- 補足
 git ls-files -o   #カレントディレクトリ以下で、追跡されていないファイルのリストを表示
 git ls-files -o --exclude-standard  #git ls-files -o のうち、.gitignoreで無視したファイルを除くリスト


//================================================================================
* git commit, reset, clean, merge [#o24116ff]

&aname(anchor1);
*** COLOR(RED){コミットするときの注意点} [#z10719ad]

-「5.1 Git での分散作業 - 分散作業の流れ」, Pro Git book 2nd Edition(2014) より https://git-scm.com/book/ja/v2

-- コミットの単位が論理的に独立した変更となるようにしましょう:~
 修正した問題ごとにコミットを分割し、それぞれに適切なコメントをつければいいのです。

    ⇒ むずかしいのでほどほどで対応すればよい。

-- コミットメッセージ:~
 最初には変更の概要を一行 (50 文字以内)+空行+詳しい説明、から成る。また、メッセージでは命令形、
現在形を使うようにしています。つまり “私は○○のテストを追加しました (I added tests for)” とか 
“○○のテストを追加します (Adding tests for,)” ではなく “○○のテストを追加 (COLOR(RED){Add tests for.})”
形式にする。 

    ⇒ COLOR(RED){コミットメッセージは英語}でする。日本語は使わないこと。~

&aname(anchor2);
*** COLOR(RED){最初設定すると良いこと} [#ya10522a]
 git config --global user.name   "yourname"   # 実行すると ~/.gitconfigに保存される
 git config --global user.email  "youremail"  # --global無しだとリポジトリ毎の.git/configに保存される
 git config --global core.editor emacs
 git config --global push.default simple      # simple(デフォルト)、matching(2.0以前のデフォルト)
 
 git config --global credential.helper 'cache --timeout=1800'  # 数分間だけ認証をキャッシュする。
                                                               #httpsのみ?sshだとパスワード無しの鍵を使う?

** commit [#ub1f9dca]
 git commit -m 注釈     # ステージ済みをコミットする
 git commit -a -m 注釈  # 追跡対象(のみ)をステージして、コミットする

- commit -m オプションが無いときは、エディタが立ち上がるので注釈を入力して保存・終了する。この場合、
注釈を「1行目をタイトル+空行+内容」とできる。-mを付けるとタイトルだけの注釈となる。

- コミットのやり直し
 git commit --amend -m 注釈  # 直前のコミットを上書きする(直前のコミットをやり直せる)。
                             # ただし、push前であること。push後だと mergeが必要となるのでしないこと。


** reset [#xf3a06e4]
 git checkout -- ファイル名   #作業ディレクトリのファイルが、チェックアウト時点に戻される
                              #(ファイルに変更があれば、失われる)
 
 git reset HEAD ファイル名    #ステージのファイルが削除される形
                              #(作業ディレクトリのファイルは、何もされない)
 
 git reset --hard    #現在のブランチを、チェックアウトの時点まで取り消す
                     #(ファイルに変更があれば、失われる)

- ローカルリポジトリを消して良い場合、再度cloneした方が楽な場合も多い。


** clean [#m0ed97d1]
- 作業ディレクトリ内で、カレントディレクトリにある不要なファイルを一括で削除するには次のようにする
 git clean -n で確認して、 git clean -f で追跡対象以外(無視するファイルは除く)を削除できる。
 なお、
 - カレントディレクトリ以下のディレクトリ内も対象にするときは -d を付ける
 - gitignoreで無視しているファイルも消すときは - xを付ける
 - 対話的に消したいときは -iを付ける


** merge [#m4be4fdd]

COLOR(RED){dry-run的なことをして確認後、マージをコミットする手法 ★お薦め}
 現在のブランチに、指定のブランチをマージ
 $ git merge --no-commit --no-ff [リモートリポジトリ/]ブランチ
             #コミットしない、fast forwardでもコミットしない。
 これで、マージされた結果はステージに載るので、
 $ git diff --cached
 で結果を確認できる。コンフリクト(競合)していたらその旨が git merge 時に出るので手動解決する。
 そして、
 (a) このマージを取り消したければ、git merge --abort をする。
 (b) そのままコミットしたければ git commit -m "merge branch hoge1 into hoge2" をする。
     又は git merge --abort 後に git merge [リモートリポジトリ/]ブランチ をしてもよい。


- マージは、ブランチに他のブランチを融合(マージ)する形である。
 git merge [リモートリポジトリ/]ブランチ    # 現在のブランチに、指定のブランチをマージする
                                            # (コンフリクトした場合は手動で解決する)
 よくある例)
   masterブランチを修正して commitした。pushすると、originの masterブランチが変更されて
   いたため rejectされた。この場合は次のようにする。
   $ git fetch
   $ git merge --no-commit --no-ff origin/master
   ここでコンフリクトを手動で解消
   $ git add 解消したファイル
   $ git commit -m "merge branch origin/master into master"
   $ git push

-- 補足
--- git merge はオプション無しだと、自動的にコミットされるので注意。


- コンフリクトの解消

-- COLOR(RED){こちらの修正方法が素直で単純}
 単純なコンフリクトの場合は、ファイルにコンフリクトの内容が、
 <<<<<<< HEAD
  ほげほげ
 =======
  ほげほげ
 >>>>>>> SHA1
 と書き込まれているので、エディタで修正して、保存する。その後、add, commit する。

-- 複雑なとき?
 コンフリクトが出たとき
 git show :1:ファイル名 > ファイル名.base
 git show :2:ファイル名 > ファイル名.ours
 git show :3:ファイル名 > ファイル名.theirs
 3つのファイルについて手動で修正し、
 git merge-file -p ファイル名.{ours,base,theirs} > ファイル名   #再度マージ?
 をして、問題無ければ add, commitする。 
   3方向のdiff(コンフリクトのマーカー含)のファイルを作りたいとき、
 git checkout --conflict=diff3 ファイル名  #デフォルトでは--conflict=merge

--- see [[7.8 Git のさまざまなツール - 高度なマージ手法>https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%81%95%E3%81%BE%E3%81%96%E3%81%BE%E3%81%AA%E3%83%84%E3%83%BC%E3%83%AB-%E9%AB%98%E5%BA%A6%E3%81%AA%E3%83%9E%E3%83%BC%E3%82%B8%E6%89%8B%E6%B3%95]]

-- 補足 
--- エディタで修正していたが、コンフリクトマーカーの入ったもとの状態に戻すとき
 git checkout --conflict=merge ファイル名


- git cherry-pick SHA1  # チェリーピック(つまみぐい)。特定のSHA1のコミットをマージして取り込む


- stash #変更を一時的に隠してpullしたいときに使う
 (実例)作業領域に更新があり、コミットしていなく、したくないが、リモートリポジトリの更新を pullしたいとき
 $ git stash 
 $ git pull
 $ git stash pop  # popする(applyして、リストからは削除)
 で良いらしい。
 
 --skip-worktreeしたファイルがコンフリクトしていた場合は、コンフリクトのためAbortするので、
 --no-skip-worktreeしてから git stashする。
 
 git stash [ save [--include-untracked]or?[--all] ["メッセージ(日付を入れると良い)"] ]
 git stash list
 git stash apply --index [ stash@{0} ]  #auto mergeなどされるようだ。
 git stash drop stash@{0}  #削除
 git stash pop  #変更をpopする(リストからは自動で削除される。コンフリクトがあると削除されない)
 今の作業ブランチにapplyやpopがされるようだ。
 logをみると stashしたものの SHA1がわかるので、ブランチ名をつけて作業してゆくこともできる。

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
** 作業ブランチへのマージの仕方(まとめ) [#sf4c03a0]

        +------>o fixブランチ
        |
 ------>o masterブランチ
        |
        +------------->o----> 作業ブランチ
                        ~~~~~
                        未コミット部分A
        o 印はコミット
 
 ブランチの分岐が上図のようにあり、
 作業ディレクトリのブランチに fixブランチをマージするには、
 次の(1)-(4)のいずれかの方法でできる。
 
 作業ブランチをコミット済みの状態にしておき、(2)の方法でコミット
 するのがいちばん素直だろう。
 (fixがmasterの進んだコミットであっても同じ)
 
 (1)
 git merge origin/fix
 これは自動的にコミットされる。
 たぶんA部分があればエラーとなる? もしくは、A部分もコミットされる? fixに関係
 するファイルだけか?
 コンフリクトしたら作業ディレクトリのファイルにその内容が入る。
 
 (2)
 git merge --no-commit --no-ff origin/fix
 自動的にコミットしない。
 コンフリクトしたら作業ディレクトリのファイルにその内容が入る。
 確認後、
 git merge --abort
 で取り消すか(取り消してから(1)でマージしてもよい)、または、
 コンフリクトがあれば修正して(無ければそのままで)、
 git commit -m "hogehoge"
 する。
 (なお、コミットする前にはコミットするファイルを取捨選択もできる)。
 
 (3)
 git cherry-pick SHA1
 fixのコミットのSHA1をマージする。master--->fixの間に複数のコミット
 があれば順に全てに対してこのコマンドでマージする。
 たぶんコンフリクトしたら作業ディレクトリにその内容が入る。
 
 (4)
 git stash
 --skip-worktreeしたファイルは無いことが望ましい。
 git merge origin/fix をする(または (1)-(3)のいずれかでもよい)。
 git stash pop
 たしか、コンフリクトしたら作業ディレクトリのファイルにその内容が入る。
 そして stashは消されずに残る。
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 


//================================================================================
* リポジトリの作成・移動 [#xc6654e5]

** ローカルリポジトリ [#b8b1cc71]

 ・作成方法1(新規作成)
 
 cd 作業ディレクトリ
 git init

 ・作成方法2(リモートリポジトリのクローン)
 
 git clone リモートリポジトリ [作業ディレクトリ]
   # リモートリポジトリは [ssh://[user@]host.xz[:port]]/path_to_repo.git などのurlで指定する。
   # 自動的に、origin という登録名でurlが登録され、リモートブランチ(origin/master)を追跡するブランチ master が作られる。
   # 作業ディレクトリを省略すると、リモートリポジトリのディレクトリ名(ディレクトリ名.gitの.gitの前の部分)となる。
   # 作業ディレクトリを指定する場合は、既存のディレクトリを使う場合はそれが空ディレクトリでなくてはならない。

** ベアリポジトリ [#bc35db61]

 ・作成方法1
 
 cd ディレクトリ.git  # 拡張子を慣習的につける
 git init --bare

** 空のリモートリポジトリ(ベアリポジトリ)にpushする(新規のベアリポジトリにファイルを追加する)例 [#l9f4e3a9]

 git add ファイル
 git remote add origin リモートリポジトリ  # リモートリポジトリを登録名originとして、登録する
 git remote -v ; git branch -vv
 git commit -m "注釈"
 git push --set-upstream origin master     # --set-upstreamは現在のプランチの追跡先を、origin/masterとする設定を登録する

** リモートリポジトリ自体の移動、それを追従しているリポジトリでの設定の変更 [#ofb17033]

 リモートリポジトリの移動自体は単にまるごとディレクトリを移動するだけである。

 これに追従しているリポジトリは
 git remote set-url origin 新しいリモートリポジトリのURL
 をして、登録名の内容を上書き更新する(originを消してから作り直してもよい)。
 
 又は、originを作り直す場合は
 git remote rm origin                   # リモートリポジトリの登録名originを削除
 git remote add origin リポジトリのURL  # URLでリモートリポジトリを登録名originで作成
 
 リモートリポジトリのURLの書式は、git remote -v の結果を見るとよい。

** リモートリポジトリの登録、変更、削除 [#vf8d3bdf]

 git remote add リモートリポジトリの登録名 リモートリポジトリのurl
 git remote rename リモートリポジトリの変更前の登録名 リモートリポジトリの変更後の登録名
 git remote rm 削除するリモートリポジトリの登録名

 git remote set-url origin 変更後のリポジトリのURL  # URLの上書き変更

 git remote [-v]  # リモートリポジトリの一覧表示


//================================================================================
* 新しいブランチを作り、追いかける手順の例(よく使う) [#p3057295]

** ローカルにブランチを作り、リモートリポジトリにpushする [#j28edecb]

 作業ディレクトリやステージは commit,push済であるとする
 $ git branch nwp      # 現在の状態で、ブランチnwpを作成
 $ git checkout nwp    # ブランチnwpに移動
 なお、ここの段階でファイルを変更して add,commitをしても良い。
 $ git push --set-upstream origin nwp  # リモートリポジトリoriginのブランチnwp(作成される)に pushする。
                                       # nwpは、origin/nwpを追跡するように設定される(オプション
                                       # --set-upstreamによる)。

** リモートリポジトリで、新たに作成されたブランチnwpを追いかける [#d10b5b2d]

 作業ディレクトリやステージは commit,push済であるとする
 $ git fetch --all --tags
 $ git checkout --track origin/nwp  # リモートリポジトリoriginのブランチnwpを追従するローカルのブランチnwp
                                    # を作成し、チェックアウトする。
                                    # git checkout -b nwp origin/nwp  でも同じ。

- 上の2つを組み合わせると、新しくブランチを作って複数のリポジトリで同期を取るようなことが出来る。

:|(例)同じリモートリポジトリoriginを上流にもつリポジトリAとBがあるとする。
Aで新しいブランチnbを作成し、originにpushした。
このpushされ作成されたブランチnbを、Bで追従するようにBでもブランチnbを作成する。
すると、AとBとの間でファイルの同期のようなことが出来る。
すなわち、Bのnbで変更した内容を、Aのnbに取り込んだり、その逆が可能。
なお、AとBとで同時に同じファイルの同じ箇所に対して異なる修正をした場合には、コンフリクト(競合)が発生する。
この場合はマージ作業を行う。


//================================================================================
* gitコマンド [#l406d658]

//--------------------------------------------------------------------------------
** status [#p519869b]
 git status [-s]    # -sは簡潔表示(非追跡対象のファイルは「?? ファイル名」で表示)
 git status -sb     # -sは簡潔表示、-bは現在のブランチを表示

 .gitignore ファイル
   このファイルを使って無視するファイルを指定できる(非追跡対象をstatusで表示させなくする等)。
   再帰的にディレクトリ階層に適用される。
   利用できる正規表現は git help gitignore や man gitignore を見よ。
   (例)作業ディレクトリに以下の内容で .gitignoreを置く
         *.x
         *.o
         *.a
         *~


//--------------------------------------------------------------------------------
** diff [#j1b1d251]
 git diff [--] [ファイル]           # 作業ディレクトリとステージの比較。ファイルを指定するとそのファイルだけ比較
 git diff --cached [--] [ファイル]  # ステージとコミット済みの比較。ファイルを指定するとそのファイルだけ比較
                                    # 引数の曖昧さを避ける場合は、ファイル名の引数の前に -- を付ける。
                                    # ファイルには、ディレクトリ名を与えることもできる。

 git diff A B    [ファイル]  # 差分をみる。AやBは SHA1や、ブランチ、タグなどで指定
 git diff A..B   [ファイル]  #     〃
 git diff A...B  [ファイル]  # AとBとの共通の親と, Bの差分
 (例)
 git diff master..origin/master   # リモートリポジトリのブランチ(origin/master)との差分をみる
                                  # origin/masterの位置は fetchなどで更新されるので、
                                  # しばらく fetchやpullをしていない場合には位置が古い場合も生じうる

 git diff A:file1 B:file2    # Aのfile1とBのfile2の比較

 git diff -U20 # 違いがあった所の前後20行を含めて差分を出力する

 git diff -w   # ファイル内のスペースの違いを無視、改行コードの違いも無視
 git diff -b   # ファイル内のスペースの数の違いを無視、改行コードの違いも無視

 git diff --name-only    # 差分のあったファイルのファイル名だけを表示
 
 git diff --name-status  # 差分のあったファイルのファイル名と、変更か追加か削除かも表示
                         # git status -s と同じ?

COLOR(RED){空白に関する警告を表示する(コミット前に予め確認すると良い)}
 $ git diff --check
 作業ディレクトリの変更について、空白に関する警告を表示する。
 デフォルトでは以下の場合に警告する(core.whitespaceの設定で変更可、詳細はググれ)。
   o 行末に空白がある
   o 空白を含む空行がある
   o ファイルの末尾に空行がある
   o タブの次に空白がある


//--------------------------------------------------------------------------------
** show [#baf2d096]
 git show オブジェクト[:ファイル]  # コミットやその内容(ファイル)を表示する。
                                   # オブジェクトは SHA1や、ブランチ、タグなどで指定
                                   # :ファイル 特定のファイルのみ表示


//--------------------------------------------------------------------------------
** log [#x099c249]
 コミットログを表示する
 git log -5 --oneline --decorate --graph --all [--stat] [-p] [レンジ] [--] [ファイル]
                                                              # --graphにより図形表示される
                                                              # --author=パタン でuser.nameの選択ができる
                                                              # --decorateによりHEADの位置がわかる
                                                              # -pはソースの差分が出る。
                                                              # --statはサマリーが出る。
                                                              # -5は 5件だけ表示
 
 git log --grep hogehoge  #ログ内容を検索
 
 git log -p -S'hogehoge' file  # コミットの中で、fileのhogehogeを含む行が追加or削除された
                               # コミットを検索する。
                               # -pオプションがあるのでそのコミット時の差分も表示(マージは対象外)。

 (その他のオプション)
 git log --since="50 minutes ago"    # 50分前からのログのみ表示
 git log --all --since="2023-03-17"  #2023-03-17以降のログのみ表示
 gitk  --all --since="2023-03-17"    #同様のことはgitkでも可

 
 git log --no-merges issue54..origin/master    #ログのフィルター記法(レンジで指定している)
  #origin/masterには含まれるが、issue54には含まれないコミットのログだけを表示
 
 git log contrib --not master  # masterに無くcontribブランチにあるコミットのログを表示

- git log でページャが動いたとき、hでヘルプが出る。いろいろなコマンドあり。

- ファイルを削除したコミットから、削除されたファイルのファイル名と内容も知ることが
出来る(たぶん自動的に前のコミットとの差分が取られるため)


//--------------------------------------------------------------------------------
** git log のフォーマット指定(log) [#oa806511]
 git log --oneline --decorate --graph --all --pretty=format:'%C(auto)%x09%h %d %s %C(blue)%cd %an%C(reset)' --date=short
   %C(色名)  文字の色を指定。autoだと項目により自動で色付けされる。resetは前の状態に戻す。
   %x09  タブ
   %h    sha1(ハッシュ値)
   %d    注釈
   %cd   日付。日付のフォーマットは別途 --date=shortで与えている。
   %an   コミットした人
   --date=iso だと "2015-03-21 16:24:43 +0900" となる。他の指定は git help log を見よ。
 
 ~/.gitconfig に次のものを入れておくと、git mylog --all とできる。
 [alias]
    mylog = log --oneline --decorate --graph  --pretty=format:'%C(auto)%x09%h %d %s %C(blue)%cd %an%C(reset)' --date=short

- git log のオプション等
-- 引数にファイル名(複数可)を与えると、そのファイルに関するログだけを表示する(gitkコマンドについても同様)
-- --follow でファイル名が変更されても追跡する(ただし引数に与えられるファイル名はひとつ)
-- --summary でリネームなどの追加情報を表示(リネームではその判断がファイル名と類似度で出力)
-- -M50% でリネームの判断の類似度の閾値を指定(デフォルトは50%)、変えないほうが良さげ。

- ファイルの更新内容をさかのぼる例(ファイル名変更も追跡)
 git mylog --all --follow --date=iso --summary -p  -- sampleinp/sample_H2mb64g64.out.chked
   #-pで差分を表示、--followでリネームに対応、--summaryでリネームなどの判断材料も表示
   #もし -pを付けないと1行のログだけで表示


//--------------------------------------------------------------------------------
** 行が追加・削除されたコミットを調べる(log) [#n3efc0b4]
 git log --cc --all -S"h5py.get_config().default_file_mode = 'a'" misc.py
   --ccで各コミットのdiff(実際はパッチ)をとる(マージコミットも対象とするために -pでない方がよい)
   -Sで検索にヒットしたコミットだけ表示する
 
 コミットのSHA1やコミットメッセージがわかるので、
 git show 287658 や、git log 287658 、
 gitkの Find欄にそのメッセージを入れて、コミットを検索表示できる(親や子のSHA1がわかる)

- log -p や --cc の挙動を調べる例
 /bin/rm -rf .git a b
 
 git init
 echo foo > a; git add a; git commit -m "add a"
 git branch other; git checkout other
 echo bar > b; git add b; git commit -m "add b"
 
 git checkout master
 git merge --no-commit --no-ff other
 echo bart > a; git add a
 git commit -m "merge branch other"
 
 echo "*** log"; git log -25  --oneline --decorate --graph --all
 echo "*** log -p";   git log -p
 echo "*** log --cc"; git log --cc

-- --cc は -p に加え、マージコミット(fast-forwardでないもの)の差分も
表示する。(--cc は -c より圧縮されたパッチを表示している)
-- -p はマージコミットを除き(fast-forwardは除かない)、各コミットの差分を表示する。
-- c.f. マージしたコミットのsha1があったとする(fast-forwardでないものとする)。
下記の showと log --ccではともにマージコミットの差分を表示する。
 git show sha1   #コミットを表示(差分も表示される)
 git log --cc sha1 sha1^..sha1   #親1からのログを表示(--ccなので差分も表示)
 git log --cc sha1 sha1^1..sha1  #親1からのログを表示
 git log --cc sha1 sha1^2..sha1  #親2からのログを表示
 なお、sha1^1か sha1^2かのどちらか一方だけが、マージコミットのログのみが表示される。


//--------------------------------------------------------------------------------
** blame [#pdb0922b]
 git blame [-C] -L n,m file    # fileに対して、n~m行目の最後のコミットをリストアップする
                               # -Cがあるとfile名の変更も追跡してコミットを探してくれる
                               # 行は1から数える。
 
 (例、どのコミットで追加されたものかを調べてる)
 $ git blame -C -L 22,24 makefile
 dfa94f47 (okazaki,i 2022-09-10 17:07:28 +0900 22) #DOPT = -O3 -ffast-math
 1e84d5f5 (okazaki,i 2018-03-19 20:13:09 +0900 23) DOPT = -Ofast
 059d6a13 (okazaki,i 2023-03-17 22:14:06 +0900 24) #DOPT = -g -Ofast
 $ git show 1e84d5f5 makefile  #さらに 1e84d5f5のコミットで makefileに関する内容を
                               #表示したいとき
 $ git diff -U 201e84d5f5..HEAD  makefile  #さらに HEADどの差分を表示したいとき(ファイルに
                                           #リダイレクションしてプリントアウトしてもよい)

 (他)
 gitに bisectというものもあり(二分探索によるデバッグ支援)。
 git log -p -S も見よ。
 
 問題がどのコミットで生じ始めたかを見つけるために、checkoutを利用して手動で
 二分探索するのが簡単だろう。


//--------------------------------------------------------------------------------
** grep [#t0001967]
 git grep --heading --line-number -3 '検索文字列' [sha1など]
   ファイル名を先頭に、行番号を行頭に表示して、検索文字列を検索する。検索文字列がマッチした
   とき、その行の前後各3行を表示する。
   作業ディレクトリ内で、カレントディレクトリ以下の追跡対象のファイルについて検索される。
   sha1などを指定するとそこで文字列検索する。
   他のオプション
     -w  単語境界での検索
     -F  正規表現を利用しない
     -l  マッチしたファイル名だけ表示

 git rev-list sha1など [-- パス] | cut -c -7 | xargs git grep '文字列'
   コミットのsha1やブランチ名などを指定(--allも可)し、そこから全てのコミットを遡って、
   文字列を検索する? パスを指定すると限定できる?
   なお、git grep の結果で、40桁のsha1の代わりに 7桁で出力させるため cut -c -7 をはさんでいる。
 
 (例)
 git rev-list --all | cut -c -7 | xargs git grep --heading --line-number -10 'rs+1'

 git grep -l 文字列 | xargs sed -i'' -e 's/文字列/置換文字列/g'
   カレントディレクトリ以下のファイルで、文字列、を含むファイルに対して、文字列を置換文字列
   に置き換える。
   なお、sedのオプション -i'' で検索されたファイルを直接書き換えて文字列置換をしている。
   -i'.old' とすると、置換前のファイルは、ファイル名.old でバックアップがとられる。


//--------------------------------------------------------------------------------
** branch [#cbfec91a]
 git branch -vv       #ブランチを表示
 git branch -a        #リポジトリにある全てのブランチを表示(リモートの◯/◯とローカルの◯ 両方)

 git branch ブランチ                            #ブランチを作成
 git branch -m 古いブランチ名 新しいブランチ名  #ブランチ名を変更する
 git branch -m 新しいブランチ名                 #現在のブランチ名を変更する

 git branch --merged       # 現在のブランチにマージされたブランチのリストを表示
 git branch --no-merged    # 現在のブランチにマージされていないブランチのリストを表示
 (使い終わったブランチはだいたい削除されるので、あまり意味はないかも)



 git branch -d     hoge         #ローカルリポジトリのブランチhoge(ローカルブランチ)を削除
                                #(予めhoge以外をカレントブランチにしておく) 
 
 git branch -d -r  origin/hoge  #ローカルリポジトリのブランチorigin/hoge(リモートブランチ)を削除
                                #(リモートリポジトリでも削除していない場合、fetchで再度作成される)
 
 git push origin --delete hoge  #リモートリポジトリoriginのブランチhogeを削除
 または git push origin :hoge
 または git push origin :refs/heads/hoge
 (git fetch するとローカルリポジトリのリモートブランチは自動的に削除される?)
 
- ブランチ削除
:|
自分がブランチを消したとしても、他の人が同じくcloneして使っているリポジトリでは、
その人が消さない限りその人のリポジトリには残る。
自分がリモートリポジトリのブランチを消したとしても、他の人が何らかのpushをすると
リモートリポジトリに再度、自分が消したブランチが作成され、自分がpullなどの作業をすると
自分のリポジトリにも再度、消したブランチが作成される。
(cloneした全員のリポジトリでブランチを消さないと、実質消すことはできない。)

- detached branchを作って、コミットし、通常のブランチにcheckoutした場合、
そのコミットした情報にはアクセスできなくなる?
リポジトリには残っていると思うが、.gitディレクトリを操作しないと見つけられない?

- 未マージのブランチ削除
git branch -d でブランチを削除しようとしても、そのブランチが未マージンであれば
エラーになる。この場合は -d の代わりに -D を使ってブランチを削除する。


//--------------------------------------------------------------------------------
** リモートリポジトリのブランチを作成や追跡(branch) [#g821c297]
リモートリポジトリにブランチが無い場合
 ローカルリポジトリのローカルブランチを、リモートリポジトリに送ってブランチを作り、追跡するように設定
 git push -u origin hogeB:hogeA  #hogeBブランチを、originのhogeAにpushする。
                                 #-u により hogeBはorigin/hogeAを追跡するようにも設定される。
                                 #(-uは--set-upstreamの短縮形)
 git push -u origin hogeA:hogeA  #同じブランチ名のときは、このように書けて、また、
 git push -u origin hogeA        #このようにも書ける(・これはよく使う・)

リモートリポジトリにブランチが有る場合
 git branch hogeB origin/hogeA    #リモートリポジトリのブランチorigin/hogeAを追跡するブランチhogeBを作る
 
 git branch --set-upstream=origin/hogeA hogeB  #リモートリポジトリのブランチorigin/hogeAを追跡するように
                                               #既存のブランチhogeBに設定をする
                                               #(hogeBを省略するとhogeBの所はカレントブランチが使われる?)


//--------------------------------------------------------------------------------
** fetch [#ld059d0d]
 git fetch --all         # 登録されている全てのリモートリポジトリから最新情報を取得する
 git fetch --all --tags  # 上記に加え、リモートリポジトリにあるタグも取得する
 
 git fetch [リモートリポジトリ] [ブランチ]


//--------------------------------------------------------------------------------
** pull [#v5544ac2]
 git pull [リモートリポジトリ]    # fetchし、mergeすることに相当


//--------------------------------------------------------------------------------
** push [#l21306b2]
 git push [リモートリポジトリ] [ブランチ] [--tags]
     # 競合が発生した場合は pushはできない(fetch, mergeし、競合解決してから pushする)
     # --tagsはタグもプッシュする


//--------------------------------------------------------------------------------
** tag [#nf4eb8ef]
 git tag タグ    #タグを付ける
 
 git tag [-a] タグ [SHA1など] [-m 注釈]  # タグを付ける
   # -aと-mで1行の注釈を入れれる。-aのみは注釈を入れるためにエディタが立ち上がる。
   # -sオプションで署名もできるらしい(詳しくは https://git-scm.com/book/ja/v2 の5章)

 git tag  # すべてのタグを表示

 git tag -d タグ名  #タグ名を削除する

 git fetch --tags   #リモートリポジトリのタグを取得する
 git push --tags                      #リモートリポジトリにタグを全て送る(タグだけが送られる)
 git push --delete リポジトリ タグ名  #リポジトリ(originなど)のタグ名を削除

- タグはsha1に紐づいている(タグが付いているところでコミットしてもタグは移動しない)。

//--------------------------------------------------------------------------------
** checkout [#b792a466]
 git checkout {ブランチ|タグ|sha1など}  #指定のものをチェックアウトする
   #非追跡対象のファイルなどは、そのまま残される。
   #sha1でチェックアウトした時、detached HEADになるが、普通どおり修正してコミット
   #できる(ブランチ名を新たにつける方が取り扱いやすい)。

 git checkout -b ブランチ {リモートリポジトリ/ブランチ|タグ|sha1など}  #指定したものをチェックアウトし、
                                                                       #-bで指定したブランチを作成する
   #リモートリポジトリ/ブランチを指定した場合、
   #作成したブランチはリモートブランチ(リモートリポジトリ/ブランチ)を追跡するように設定される。
 
   #ステージにファイルがあってもよい。ステージにあるファイルは、作成したブランチのステージに載り、
   #もとのブランチのステージからは取り除かれる。
 
 git checkout --track リモートリポジトリ/ブランチ  #git checkout -b ブランチ リモートリポジトリ/ブランチ
                                                   #と同じ
   #作成したブランチはリモートブランチ(リモートリポジトリ/ブランチ)を追跡するように設定される。

- リモートリポジトリのブランチ(◯/◯)やブランチ(◯)はマージするための、重要な概念である。
- チェックアウト(ブランチへの切り替え)ができない場合もある(作業ディレクトリやステージに未コミットがあるとき)
- gitではファイルのタイムスタンプは管理されない。checkoutした時刻がファイルに付く。

- detached HEAD の利用例
 detachして、追加修正した記録を残したいときの例
 $ git commit -m "a commit for debug tests"  #とりあえずコミットした
 $ git branch debug_hoge                          #ブランチにして、
 $ git tag debug_hoge_20230322                    #タグを付けて、
 $ git push --set-upstream origin debug_hoge      #リモートリポジトリにpush
 $ git push --tags                                #タグもpush
 $ git push origin --delete debug_hoge    #リモートブランチを削除
 $ git checkout master
 $ git branch -D debug_sotu_ngrid         #ローカルブランチを削除
 最後の三行は
 $ git checkout master
 $ git branch -D -r debug_sotu_ngrid
 か。


//================================================================================
* その他のコマンド等 [#e07d8302]

//--------------------------------------------------------------------------------
** gitk [#n2f45180]

 gitk [--date-order] [--all] [ファイル]   # git log などのGUIツール
                                    # --date-orderは日付順になるので付けた方がよい
 
 gitk --date-order master_20230403  #などある時点から過去のコミット表示も可


//--------------------------------------------------------------------------------
** git help [#k13d01b7]

 git help コマンド名    #gitのオンラインマニュアル


//--------------------------------------------------------------------------------
** パッチ [#z0a72181]
 git diff差分はそのままでパッチファイルになっている。
 パッチの当て方
 patch [--dry-run] -p1 < パッチファイル    #--dry-runは何が行われるかを表示するだけ。


//--------------------------------------------------------------------------------
** git config [#f5a49bb9]
 git config [--global] -l    # .git/configを表示
 git config [--global] -e    # .git/configを編集


//--------------------------------------------------------------------------------
** 表示の色変更(git config) [#kc89f85c]

- git log 出力のタグの色を変える
 git config --global color.decorate.tag "blue"    #青色にする(see: git help config)


//--------------------------------------------------------------------------------
** HEADなど(sha1の別名) [#d8daf63f]

 HEAD    #現在checkoutしているコミット(ブランチ等)のsha1を表す別名

 HEAD^   #HEADのひとつ前のコミットを表す
 HEAD^^  #HEADの前の前のコミットを表す
 (sha1の数字を使っても同じことを表せる。なお gitkでは利用できないようだ。)

 例)S=91dc63f git diff ${S}^..${S} sample_H2mb64g64.inp


//--------------------------------------------------------------------------------
** アーカイブを作る [#vad53986]

 git archive master --prefix='project/' --format=tar > git_master.tar  #masterブランチのアーカイブ

 git shortlog --no-merges master --not master_20220310  #ショートログを表示
                                                        #masterブランチに対してタグmaster_20220310から
                                                        #のぶんを表示する


//--------------------------------------------------------------------------------
** GitHub [#h01740d0]

- githubのリポジトリの作り方
 まず、ブラウザで名前を付けてリポジトリを作る。
 ここへのpushの仕方が表示されるので、ローカルに作っておいたリポジトリをpushする。


//--------------------------------------------------------------------------------
** 不具合対応 [#fa9b8e8e]

- ファイル名に日本語を使わない方が無難である。commitメッセージも日本語は避けておく。


- ブランチ名とタグ名が同じ場合に、どちらかを削除する方法(タグ名とブランチ名は同じにしない方が無難)
 tagを消すとき    git push origin :refs/tags/タグ名
 branchを消すとき git push origin :refs/heads/ブランチ名


- unable to read askpass のエラー対応
 $ git push
 (gnome-ssh-askpass:hogehoge): Gtk-WARNING **: cannot open display:
 error: unable to read askpass response from '/usr/libexec/openssh/gnome-ssh-askpass'
 Username for 'https://github.com':
 などと、表示される場合の対応。
 
 無視して、つづけて問題無い(GUI環境で無いために、パスワードキャッシュがうまく動かなかったらしい)
 unset SSH_ASKPASS をすると表示されなくなる


- git status で日本語のファイル名が化けているのを表示させる
 localな設定
   git config --local core.quotepath false    #.git/configに保存される
   git config --local --unset core.quotepath  #設定を元に戻す
 globalな設定
   git config --global core.quotepath false   #~/.gitconfigに保存される
   git config --global --unset core.quotepath


//================================================================================

* tips [#va6b12fc]

** 変更のあったファイルを emacsでいっきに開く [#x2ac1d78]
 emacs `git diff --name-only`


** 変更があったファイルで、行末に空白があるものを調べる [#b5e85e06]
 #タグmaster_20200619から変更があったファイルのうち、拡張子がc,h,l,yのファイルについて
 #行末にひとつ以上の空白があるもののファイル名を表示する
 for f in ` git diff --name-only  master_20200619  | grep '\.[chly]$' `; do \
   echo ==== $f ====; grep ' \+$' $f > /dev/null; [ $? == 0 ] && echo $f ; done | grep -v ^====


** 変更があったファイルで、タブを含むものを調べる [#qca5eb3f]
 #タグmaster_20200619から変更があったファイルのうち、拡張子がc,h,l,yのファイルについて
 #タブを含むファイルのファイル名を表示する
 for f in ` git diff --name-only  master_20200619  | grep '\.[chly]$' `; do \
   echo ==== $f ====; grep $'\t' $f > /dev/null; [ $? == 0 ] && echo $f ; done | grep -v ^====
                  #(注釈: ここで $'文字' はbashの機能。$'\t'はタブ。)
 (補足)
 $ grep $'\t' makefile
 と同じことは
 $ grep -P '\t' makefile  #perl互換の正規表現
 でできるが、
 デフォルトでは grepは \t をタブ文字コードと理解しない(ほとんどのメタ文字利用不可)。
 そのため bashで $'\t' によりタブ文字コードを grepに渡している形。
  さらに、bashではタブ文字コードを入力することが出来るので(Ctrl-v Tabで入力)
 タブ文字コードをシングルクォーテーションで括って grepに渡すこともできる。
 
 c.f.
   $ grep \\t    makefile  #これは'\t'の検索と同じ
   $ grep '\t'   makefile  #ただの tの検索になる
   $ grep '\\t'  makefile  #これは \tの2文字の検索
   $ grep '\\\t' makefile  #これも \tの2文字の検索
   $ grep '※ここでCtrl-v Tab' makefile  #これは$'\t'の検索と同じ

** HEADのsha1値と注釈を表示する [#k53e99a6]
 temp=`git rev-parse --short HEAD; git log -1 --pretty=%B` ; echo $temp


** 作業ディレクトリの変更の有無を表示する [#t122e849]
 if git diff --quiet >/dev/null 2>&1; then echo "--"; else echo "modified"; fi


** コミットの注釈が英語以外の場合にエラーにするフックの設置 [#p5ac1e54]

次の 1)または 2)のフックを作り、英語以外のコミット注釈をエラーにすることができる。~
 1) ベアリポジトリに pushされるときのフック、~
 2) ローカルリポジトリで commitするときのフック。~
なお、リポジトリを cloneしたときに、自動的に 2)のフックを設置することはできない。
そのため 2)は各ユーザが自分で手動でフックを設置しなければならない。
一方、1)は push時にエラーになるが、エラーとなったユーザはコミットを作り直さなくてはならない。~
(注) 2023/09/30に以下のフックを作ったが、バグが無いとも言い切れない。10/01若干修正済み。

フック:ソフトウェアに予め用意されている、特定のタイミングで実行する処理やその仕組み。

*** 1) ベアリポジトリに pushされるときにエラーにするフック [#j2acb99f]

hooksディレクトリに以下の内容を updateというファイル名で保存する。
 #!/bin/sh
 # 2023/09/30 okazaki,i
 MYDEBUG=0  #1 or 0
 
 #get the commit range to be push
 refname="$1"
 oldrev="$2"
 newrev="$3"
 
 #rearrange the commit range
 zero="0000000000000000000000000000000000000000"
 if [ $oldrev != $zero ]; then
     range="$oldrev..$newrev"
 else
     range="$newrev"
 fi
 
 #check charaset, then reject or continue to the commit.
 [ $MYDEBUG -ne 0 ] && echo "MY update: git rev-list" 2>&1
 [ $MYDEBUG -ne 0 ] && git rev-list $range 2>&1
 for c in `git rev-list $range`; do
     [ $MYDEBUG -ne 0 ] && echo "MY update: git cat-file, for $c"
     git cat-file commit $c | sed '1,/^$/d' > /tmp/hook.$$
     chara=`file -i /tmp/hook.$$ | sed 's/^.*charset=\([^;]*\)\(.*\)/\1/'`
     rm -f /tmp/hook.$$
     [ $MYDEBUG -ne 0 ] && echo "chara=$chara" 2>&1
     [ $MYDEBUG -ne 0 ] && echo "using /tmp/hook.$$" 2>&1
     #check charaset
     if [ x"$chara" != x"us-ascii" ]; then
         echo "REJECT your message which is not us-ascii." 2>&1
         exit 1
     fi
 done
 [ $MYDEBUG -ne 0 ] && echo "CONTINUE to commit." 2>&1
 exit 0

*** 2) ローカルリポジトリで commitするときにエラーにするフック [#m91a928c]

.git/hooksディレクトリに以下の内容を commit-msgというファイル名で保存する。
 #!/bin/sh
 # 2023/09/30 okazaki,i
 MYDEBUG=0  #1 or 0
 
 #get commit the message
 msg=`cat "$1"`
 [ $MYDEBUG -ne 0 ] && echo "MY commit-msg: $msg"
 
 #determine charaset
 echo "$msg" > /tmp/hook.$$
 chara=`file -i /tmp/hook.$$ | sed 's/^.*charset=\([^;]*\)\(.*\)/\1/'`
 rm -f /tmp/hook.$$
 [ $MYDEBUG -ne 0 ] && echo "MY commit-msg: chara=$chara" 2>&1
 [ $MYDEBUG -ne 0 ] && echo "using /tmp/hook.$$" 2>&1
 
 #check charaset, then reject or continue to the commit.
 if [ x"$chara" != x"us-ascii" ]; then
   echo "REJECT your message which is not us-ascii." 2>&1
   exit 1
 fi
 [ $MYDEBUG -ne 0 ] && echo "CONTINUE to commit." 2>&1
 exit 0


** マシン毎に user.name, user.emailの値を自動的に変更する [#fc2717dc]

.gitconfigに環境変数を利用することはできない。
しかし、user.name, user.emailに与えた値よりも、
環境変数 GIT_AUTHOR_NAME, GIT_AUTHOR_EMAILの値が優先される。
従って、.bashrcなどを利用してこの環境変数をマシン事に変えることで、
user.name, user.emailの値を自動的に変更できる。




//================================================================================

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS