為什么用Latex寫論文需要設計“工作流程”
Latex是理工類論文的事實標準,但是對于“寫作-合作者審閱-討論并修訂-寫作新內容”這樣迭代式的寫作流程來說,它缺少清晰的修訂模式。尤其當作者和合作者并行修改論文時,這個缺陷會經常導致內容沖突、遺失等問題,全靠肉眼一行行地修訂非常費時間。
第二個問題是論文是會被拒的。在快則個把月,慢則長達一年的評閱時間之后,再度撿起論文草稿,很難記起當時的寫作思路和版本迭代思路。
采用Git管理論文工作流程可以保存所有歷史版本、簡化內容合并過程。
整體思路
- 在code hosting網站上建立你的論文倉庫(如:my-papers文件夾),每一篇內容獨立的論文用一個單獨的文件夾保存。網站選擇的原則是隨時可訪問和支持私有庫。
- 每一篇的論文寫作,均只從遠程倉庫
pull
當前論文的目錄,不影響其他論文。 - 用至少三個分支進行寫作。和程序開發相似,
master
分支的代碼總是submission-ready的。每個合作者的修改單獨用一個分支保存,比如co-author
分支。收到新的修改后,放到co-author
分支下,然后merge
到當前寫作分支,比如my-writing
分支。
具體流程
利用Git的sparse-checkout功能建立論文倉庫
在代碼托管網站上建論文倉庫沒什么可說的,這里只說一下如何每次只pull
當前論文的目錄。
ref: http://blog.algony.net/2015/05/git-sparse-checkout/
$ git init <project>
$ cd <project>
$ git remote add origin <your_remote_paper_repo>
$ git config core.sparsecheckout true # 打開sparse-checkout模式
$ echo "<current_paper_dir>" >> .git/info/sparse-checkout # 設置需要pull的論文目錄
$ git pull origin master
$ <edit_your_paper> # 修修補補你的寶貝論文
$ git pull origin master # 如果遠程倉庫有新的修改,則pull下來合并,可選
$ git push --set-upstream-to origin master # 第一次push需要設置本地分支和
# 遠程分支的跟蹤關系,之后直接
# "git push origin"就可以
注意事項:
-
git pull
操作相當于git fetch
+git merge
操作組合。因此如果你不止想pull
遠程倉庫里的master
分支,而是所有分支,上述命令中的第一個pull
操作可以用git fetch origin
代替,后者默認下載origin
服務器上所有分支,所取回的更新。這些分支在本地主機上要用"遠程主機名/分支名"的形式讀取,如remotes/origin/master
。 -
git fetch
之后本地是沒有分支的,本地工作目錄也為空。需要在遠程分支基礎上,使用$ git checkout -b <new_local_branch> origin/<remote_branch>
創建一個本地分支<new_local_branch>
。
- 或者執行
$ git checkout merge origin/<remote_branch>
將origin/<remote_branch>
合并到本地master
分支。如果當前沒有分支,git merge
會在本地創建master
分支。
- 或先在本地
checkout
一個分支,然后執行git branch --set-upstream-to=origin/<branch>
指定當前本地分支跟蹤遠程分支
Git寫Latex的流程
ref: https://stackoverflow.com/questions/6188780/git-latex-workflow
- 最好每一章一個單獨的tex文件,因為論文不中改投的時候可能需要換模板。這時候將每一章直接
\input
到模板文件里就可以。 - 每句話一段。Git的
diff
默認以行為單位,而Latex只有在每段話前面有空行時才認為是新的段落,所以每句話一段不影響Latex排版,又可以方便Git做diff
。你甚至可以git diff --color-words
打開逐字比對的效果。 - 為每個
\section
開一個分支。合作論文最常見的情況就是一個人寫一章,合作者寫/改另一章。為每一章開一個單獨的分支,方便跟蹤進度和合并。 - 全局性的修改,如將整個論文中的“Section”改成“Chapter”,做成單獨的
commit
。導言區的修改也是如此。這樣無論是diff
或者patch
都很清楚做了什么修改,不會和內容修改相混淆。
和不使用Git的合作者合作
ref: http://www.math.cmu.edu/~gautam/sj/blog/20130929-git-quickstart.html
你沒有辦法讓你的合作者為了改你的論文在學會了Latex之后還專門去學Git,尤其是那些德高望重的合作者,比如你的導師。而他們的意見和修改,一般來說是非聽不可的。這時我們面臨的主要問題有兩個:
- 記住交給合作者的論文版本
- 合作者返回時,快速定位修改位置,并和你的當前版本合并。
- 而你的當前版本,可能已經不是當初交給合作者的版本了,甚至
push
到遠程服務器了。(這不算第三個問題。)
有時候你會覺得合作者的意見并不那么靠譜,但是溝通還得繼續,他仍然是你的導師,你必須得讓他知道你欣然接受了他的意見。這時候可以新開一個分支,用于接受合作者的意見并和他繼續往來溝通,而論文的實際發布版本則在另一個分支上。
總之,我們需要一個叫做git-ident的Perl腳本幫我們自動化這些事情。步驟如下:
- 在你打算
commit
一個版本,并將這個版本發給合作者之前,在當前論文的根目錄下創建/修改.gitattributes
文件,內容為:*.tex ident *.bib ident
- 然后在你要發給合作者的主文件里,如
main.tex
,加上一句包含$Id$
的注釋,如:% DO NOT EDIT -> $Id$ <- DO NOT EDIT
- 安裝git-ident,并在你的論文根目錄下添加它的post-commit hook:
$ cd .git/hooks $ ln -s /path/to/git-ident/post-commit
-
commit
或者checkout
你的論文,這時候Git會自動將$Id$
替換為當前版本的SHA1值(并不是你commit的版本號)。
這時候你就可以把你最新的commit
的版本發給合作者了,靜靜地等待他的修改,或者自己繼續寫下一章節。等他返回給你的時候,執行
$ /path/to/git-ident/git-find-commit.pl <your_shared_tex_file>.tex
腳本會輸出你當時交給合作者的commit版本號,比如b2234fa5
之類。根據這個版本號,你可以創建/轉到你的co-author分支,保存他的修改,并merge
到你的當前版本:
$ git checkout -b co-author b2234fa5
$ <save_his_file> # Save his file over yours
$ git commit --author 'Co Author <who@doesnt.use.git>'
$ git checkout my-writing
$ git merge co-author
當你完成你對合作者這次修改的內容的編輯之后,可以重復這個流程,開始下一輪的“寫作-合作者審閱-討論并修訂”了。
注意:git-ident
需要Perl環境,并安裝IPC::Run
和IPC::System::Simple
模塊。方式為:
- 執行
sudo cpan
進入cpan環境 - 執行
install MODULE_NAME
安裝模塊
查看修訂
第一種辦法是用git diff
,實現在tex源代碼上顯示差異。缺陷就是Git以行為單位做diff
,而且tex源碼上看diff
非常不清晰。
第二種辦法是使用\usepackage{changes}
,這需要需要合作者在修訂的時候自己加標注,實在難為人。
第三種辦法就是用latexdiff
工具,一般TeX版本都自帶,方法為:
$ latexdiff <old_tex_file>.tex <new_tex_file>.tex > <diff_file>.tex
當我們采用每章一個tex文件的方式組織論文時,latexdiff
需要加上--flatten
參數以展開main.tex
中的各種\input
,但效果并不好。可以試試git-latexdiff工具
另外,latexdiff
得到的<diff_file>.tex
經常容易出錯,尤其在新老文件的\section
、\subsection
等名字不一致的時候。自己手動把兩個文檔改成一樣的就行了。