git+latexdiff-vcで快適にHEADとの差分を確認しながら論文を執筆する for Windows

# Abstract

Latexで論文執筆して、先生と添削などを含む、内容のやり取りする際に綺麗な差分があると嬉しい気がします。
そこで本記事では

- 下記のような差分pdfファイルを本体のファイルを編集しながら生成する方法
![差分pdfの例](https://nekketsuuu.github.io/assets/entries/2017-01-27-latexdiff-vc/diff.png)
Gitで管理しているLaTeXのdiffをpdfで見る(TeXLive2015版), https://nekketsuuu.github.io/entries/2017/01/27/latexdiff-vc.html より
- windows特有と思われるエラーの対処

を示します。

# 環境

- windows 10
- texlive 2021
- perl v5.32.1

# latexdiff-vcって？

`latexdiff-vc`は`latexdiff`という、2つの`.tex`ファイルの差分を強調した`.tex`を出力させるCLIツールのラッパーです。
まぁオプションの処理とかを楽にしてくれるもの、という認識でよいでしょう。

`latexdiff`自体の機能は [天地有情 [LaTeX] latexdiff --- LaTeXファイルの重要な違いを特定してマークアップする](https://konoyonohana.blog.fc2.com/blog-entry-423.html) などがよいでしょう。



# latexdiff-vcの導入

近年のtexliveには`latexdiff`及び`latexdiff-vc`は標準で入っているようです。
必要なら[CTAN](https://ctan.org/pkg/latexdiff)からダウンロードしてあげましょう。

続いてperlをインストールします。
[Perl Download](https://www.perl.org/get.html)からインストーラーをダウンロードして実行します。
これでオッケーです。

# 差分取得

`git init`でリポジトリを初期化・`git add main.tex; git commit -m "initial commit"`などで作成した最初の`main.tex`をcommitします。その後、変更を行って次のコマンドを実行することで、diffフォルダの中に`main.tex`が生成されます。

```bash
latexdiff-vc -e utf8 --git --force -d diff -r HEAD main.tex
```

`-e`は文字コードの指定です。
`--git`はgitで管理されていること指定します。(なしでも推測してくれるらしい。)
`--force`は今回生成される差分ファイル(diff/main.tex)の上書きを強制します。
`-d`は差分ファイルの出力先を指定します。
`-r`は比較するcommitの指定になっていて、commit IDだけではなく、HEADなどで指定できます。なんと**タグも使えます**。

`--math-markup=3`を追加したほうが良いかも(デフォルトだと数式の変更の検知がちょっと強すぎた。)

## 多分エラーが出るのでその対処

### `2 and only 2 non-option arguments required.`と出てエラー

こんな感じのエラー

```log
Working on  main.tex 
patching file main.tex
Running: latexdiff  '-e' 'utf8' "main-oldtmp-30316.tex" "main.tex" > "diff/main.tex"
2 and only 2 non-option arguments required.  Write latexdiff -h to get help
C:\texlive\2021\bin\win32\runscript.tlu:915: command failed with exit code 2:
perl.exe c:\texlive\2021\texmf-dist\scripts\latexdiff\latexdiff.pl '-e' 'utf8' "main-oldtmp-30316.tex" "main.tex"
Something went wrong in latexdiff. Deleting diff/main.tex and abort
C:\texlive\2021\bin\win32\runscript.tlu:915: command failed with exit code 5:
perl.exe c:\texlive\2021\texmf-dist\scripts\latexdiff\latexdiff-vc.pl -e utf8 --git --force -d diff -r HEAD main.tex
PS C:\Users\takeMe\Labora
```

PowerShellの問題？Windowsの問題?
なんか`latex-diff`呼び出しの引数でエラーをだしていると思われるが…
[How should I pass latexdiff options to latexdiff-vc?](https://tex.stackexchange.com/questions/585964/how-should-i-pass-latexdiff-options-to-latexdiff-vc) に現れたコントリビューターによると「`'`, `"`が原因じゃないかな？」とのこと。「脆弱性のために`'`で囲ってるのよね。」とのこと。一理ある。

ということで下記のようにオプション引数の部分から引用符を除いてみました。(筆者は**この記事執筆時点で初めてperlを触っている**ので、間違いに注意。多分大丈夫だけど...シンプルにセキュリティ的な観点だとアレなので、自己責任でお願いします。)
`C:\texlive\2021\texmf-dist\scripts\latexdiff\latexdiff-vc.pl`の461行目付近で変更を加えます。

:::note warn
texlive 2022だと478行目でした。バージョンによっては少し違うかもなので注意。
:::


```diff
  if (scalar(@ldoptions) > 0 ) {
-   $options = "\'" . join("\' \'",@ldoptions) . "\'";
+   $options = join(" ",@ldoptions);
  } else {
    $options = "";
  }
```

:::note
476行目の`system(...)`で、ここでオプション引数である`-e utf8`の部分などが`'`で囲まれていると`latexfiff`の引数のバリデーションに引っかかるっぽい。
※`unless( system(...) == 0 ) { ... }`のブロックを削除して、`system(...)`だけ実行させても同じエラーがでるっぽい？
:::

## 編集しながら同時並行に差分ファイル出力させる

以下、ひとまずは`--flatten`を使用しない前提で記述します。
VScodeの最強のLatex環境であるLaTeX Workshopでは、レシピを使うことで保存しながらコンパイルができる優れものです。
このレシピに差分出力のコマンドをかませることで、作業と並行してHEADと差分を出力させようと思います。

ここではpLatexで、platex2pdfを用いてコンパイルを行います。
settings.jsonに下記のようにレシピとツールの設定を行います。

```jsonc:settings.json
{
	"latex-workshop.latex.outDir": "submit",    // main.texの出力ディレクトリ指定
	"latex-workshop.latex.tools": [
		{
			"command": "ptex2pdf",
			"args": [
				"-l",
				"-ot",
				"-kanji=utf8 -synctex=1",
				"-halt-on-error",
				"%DOC%",
				"-output-directory=%OUTDIR%"
			],
			"name": "ptex2pdf"
		},
		{
			"command": "ptex2pdf",
			"args": [
				"-l",
				"-ot",
				"-kanji=utf8 -synctex=1",
				"-halt-on-error",
				"diff/main.tex",
				"-output-directory=diff"
			],
			"name": "diff2pdf"
		},
		{
			"command": "latexdiff-vc",
			"args": [
				"-e",
				"utf8",
				"--git",
				"--force",
				"-d",
				"diff",
				"-r",
				"HEAD",
				"main.tex"
			],
			"name": "make diff"
		}
	],
	"latex-workshop.latex.recipes": [
		{
			"name": "ptex2pdf*1",
			"tools": [
				"ptex2pdf",
				"make diff",
				"diff2pdf"
			]
		},
		{
			"name": "ptex2pdf*2",
			"tools": [
				"ptex2pdf",
				"ptex2pdf",
				"make diff",
				"diff2pdf"
			]
		}
	],
}
```

このように設定することで、普段`main.tex`を執筆していると保存する度に自動でコンパイルが走りますし、いつでもHEADとの差分を確認することができます。

# Tips

小ネタを自分の備忘録として書いておきます。

## オプション`--flatten`が使えない

`\input{}`とかを使うときに必要なオプション引数`--flatten`が使えないっぽい。
[Gitで管理した論文の差分pdfをlatexdiffで作成する（latexdiff-vc –flattenでエラーが出た時の対応）](https://www.tate-blog.com/2021/04/08/git-latexdiff/) に考察が記されています。
> エラー内容を見ると、git archive –format=tar HEAD | ( cd ./latexdiff-vc-HEAD ; tar -xf -)が実行できていないようですが、本来は作成されたlatexdiff-vc-HEADのディレクトリにHEADのコミット分がtar.gz形式で保存されるはずです。

こちらの記事で提案している解決策は、指定したcommit IDから対象のtexファイルを引っ張て来てそれと`latexdiff`を使って差分ファイルを出力、という形になっています。(`latexdiff-vc`を経由しないという形ですね。)

ただし、自分の環境ではtarでやろうとすると解凍でエラーが発生しました。
加えて、bblファイルのディレクトリがtexファイルと同階層ではない場合は、perlをいじる必要がありそうでした。
よってここでは
- PowerShellで
- zipで書き出して
- bblを自分で差分生成

するような代替案を残しておきます。

```powershell
# 比べる対象
$ref = "HEAD"

# zipで書き出す
git archive $ref --output="$ref.zip"
Expand-Archive "$ref.zip"

# 差分ファイルをdiffフォルダに作る
latexdiff -e utf8 "$ref/paper.tex" .\paper.tex  > diff/diff.tex
latexdiff -e utf8 "$ref/out/paper.bbl" "out/paper.bbl" > "diff/diff.bbl"

# bblの差分は、そのまま使うと番号などが大変なことになるので、消した引用文献は表示しないようにする(Optional)
(Get-Content .\diff\diff.bbl -Encoding utf8) | ForEach-Object {
    if ($_.Contains('\DIFdelbegin')){
        # remove
    } else { 
        $_.Replace('\DIFdelend', '');
    }
} | Set-Content .\diff\diff.bbl -Encoding utf8
```

## 日本語などが文字化けする

自分は、lateffdiff-vcではなくlatexdiffを直接使う時に起きました。
PowerShellが`>`でリダイレクトするときにややこしいいことになっているっぽい。

対策: **コンソール出力のデフォルト値をUTF-8にする**。
~~てかずっとこれでいいので`$PROFILE`に書け~~

```powershell
[Console]::OutputEncoding = [Text.Encoding]::UTF8
```

で、`latefdiff`を使う。

```powershell
# Powershell 5.xなら
latexdiff -e utf-8 -t CFONT input1.tex input2.tex | Out-String | % { [Text.Encoding]::UTF8.GetBytes($_) } | Set-Content -Path "output.tex" -Encoding Byte

# Powershell 6以降
latexdiff -e utf-8 -t CFONT input1.tex input2.tex > "output.tex" 
```

参考:

https://waregawa-log.hatenablog.com/entry/2019/06/29/160241

## 行番号を差分ファイルにのみ表示する

プリアンブルにlinenoを追加することで、行番号を表示できます。

http://xyoshiki.web.fc2.com/tex/lineno.html

↓ これを差分ファイルにのみ追加したい

```latex
\usepackage{lineno}
\linenumbers
```

冒頭の`\documentclass{...}`の真下に追加すればよいので、強引に下記のようなスクリプトで実現できます。

```powershell
# 比べる対象
$ref = "HEAD"
# 差分を生成
latexdiff-vc -e utf8 --git --force -d diff -r $ref paper.tex
Move-Item diff/paper.tex diff/diff.tex

(Get-Content .\diff\diff.tex -Encoding utf8) | ForEach-Object {
        $_.Replace('\documentclass{...}','\documentclass{...}' + "`r`n" + '\usepackage{lineno}\linenumbers');
} | Set-Content .\diff\diff.tex -Encoding utf8
```

なお, \`r\`nは改行コードです。
ちなみに、最初の()は消すと動きません。
()で囲まないと、一行ごとに処理が次に進む(パイプライン)のですが、`Get-Content`が終わるまでは、ファイルへのアクセスがロックされています。その状態で`Set-Content`に到達するので、エラーを吐きます。

https://tex2e.github.io/blog/powershell/sed

## 増やしたところだけを表示する

消したところは表示せず、増やしたところだけを強調するやつです。
手動で対応できますが、やはり面倒なのでスクリプトを書きました。

```powershell
(Get-Content .\diff\diff.tex -Encoding utf8) | ForEach-Object {
    $_.Replace('\providecommand{\DIFadd}[1]{{\protect\color{blue}\uwave{#1}}}','\providecommand{\DIFadd}[1]{{\protect\color{red}#1}}');
} | ForEach-Object {
    $_.Replace('\providecommand{\DIFdel}[1]{{\protect\color{red}\sout{#1}}}','\providecommand{\DIFdel}[1]{{}}');
} | Set-Content .\diff\diff.tex -Encoding utf8
```

なお, PowerShellでは`""`の場合は変数の展開などが行われますが`''`の場合はいわゆるraw文字列で、展開などが行われません。

参考:

https://qiita.com/kazumiy/items/70df0c201e02a13efe94

## docmuteと併用すると壊れる

分割コンパイルで有名な手法の一つに、documuteを用いる方法がありますね。
`latexdiff`で`--flatten`オプションを用いると解決できそうですが、プリアンブルまで重複して`\input`を解決してしまいます。(`latexdiff-vc`を経由しない)
これはおそらく、latexdiffはinputマクロを実行しているわけではなく。あくまでファイルの内容を取り込んでいるだけだからだと思います。

したがって、分割コンパイルに必要なファイルの中身だけを切り出してから`--flatten`を行う必要があります。

例: src フォルダー下に main.tex (メインとなるファイル) があり、abstract.tex, introduction.tex, method.tex を`\input`で取り込んでいる。diffフォルダーに diff.tex を差分ファイルとして生成する。

※ main.tex 内で `\input{./abstract.tex}` などと相対パスで指定している想定です。

srcの親フォルダから下記のスクリプトを実行することで、docmuteを利用していても差分を生成できます。

```PowerShell: MakeDiff.ps1
Param(
    [string]$ref = "HEAD"
)
function Extract-Document-Content($texFile) {
    $file = @(Get-Content $texFile -Encoding utf8);
    $begin = $file.IndexOf('\begin{document}') + 1;
    $end = $file.IndexOf('\end{document}') - 1;
    $file[$begin..$end] | Set-Content $texFile -Encoding utf8
}

# 比較対象を解凍
git archive $ref --output="$ref.zip"
Expand-Archive "$ref.zip"

if (!(Test-Path .\temp)) {
    mkdir temp;
}
Copy-Item .\src\main.tex .\temp\main.tex
Copy-Item .\src\main.bbl .\temp\main.bbl

$includingFiles = @("abstract.tex", "introduction.tex", "method.tex")

$includingFiles | ForEach-Object {
    Copy-Item ./src/$_ ./temp/;
    Extract-Document-Content ./temp/$_;
}

# 比較対象もdocumentのみを抽出
$includingFiles | ForEach-Object {
    Extract-Document-Content ./$ref/src/$_;
}

latexdiff -e utf8 --math-markup=1 "$ref/src/main.tex" .\temp\main.tex > .\diff\diff.tex --flatten
latexdiff -e utf8 "$ref/src/main.bbl" .\temp\main.bbl > .\diff\diff.bbl

# 削除した引用文献はバグるので，その部分は消す．
if (Test-Path .\diff\diff.bbl) {
    (Get-Content .\diff\diff.bbl -Encoding utf8) | ForEach-Object {
        if ($_.Contains('\DIFdelbegin')){
            # remove
        } else { 
            $_.Replace('\DIFdelend', '');
        }
    } | Set-Content .\diff\diff.bbl -Encoding utf8
}

#! in diff folder
Set-Location .\diff;
function CompileDiffOnce {
    # ここは環境に応じて変更してください
    ptex2pdf -l -ot -kanji=utf8 -halt-on-error diff.tex
}

# 差分texをコンパイル
CompileDiffOnce
CompileDiffOnce

Set-Location $PSScriptRoot/../
#! in diff folder end

Remove-Item $ref -Recurse
Remove-Item "$ref.zip"
Remove-Item .\temp -Recurse
```