用 GitHub Desktop 给论文安排版本控制
写在前面
写论文最痛苦的往往不是「写」本身,而是「改」。
特别是对于理工科学生,LaTex 是事实标准。但 LaTex 的纯文本特性加上长周期的写作过程,经常会出现非常蛋疼的情况,版本混乱、思绪错杂。
版本混乱:我和导师并行修改,文件传来传去,最后不知道哪个才是最新的,全靠肉眼一行行比对,极其消耗精力。而且针对不同的刊物和用途,一个文本又被修改为诸多不同的模式,让人头大。
思绪错杂:论文投出去,可能两三个月甚至更久才会有反馈。很多时候多防线出击也会导致,当我再次打开文档,面对那堆杂乱的草稿,完全想不起当时为什么要这么写,也忘了哪个版本是「废弃但有保留价值」的思路,最后,文本很多,不知道有啥用。
最近开始尝试使用 GitHub Desktop 来管理我的论文项目。核心目标只有一个:让写作进度「可分类」、「可回溯」,让我的论文像是代码工程一样进行科学管理。
如果不把论文当做纯文本,而是当做「代码」来管理,一切都会变得井井有条。
为什么 LaTex 需要「工作流」?
Word 是「所见即所得」,而 LaTex 是「所想即所得」。既然我们在写代码(LaTex 源码),就应该用管理代码的方式来管理它。
传统的文献修改最大的问题是它是线性的。但我们的写作往往是迭代的,引入 Git,就是为了解决这个非线性问题。
部署策略
GitHub Desktop 的可视化界面非常直观,足够应对论文管理。
思路如下:
1、建立论文仓库(如:my-papers文件夹),每一篇内容独立的论文用一个单独的文件夹保存。
2、每一篇的论文写作,均只从远程仓库pull当前论文的目录,不影响其他论文。
3、用至少三个分支进行写作。和程序开发相似,master分支的代码总是submission-ready的。每个合作者的修改单独用一个分支保存,比如co-author分支。收到新的修改后,放到co-author分支下,然后merge到当前写作分支,比如my-writing分支。
在代码托管网站上建论文仓库没什么可说的,这里只说一下如何每次只pull当前论文的目录。(ref:
http://blog.algony.net/2015/05/git-sparse-checkout/)
1 | $ git init <project> |
注意事项:
git pull操作相当于git fetch+git merge操作组合。因此如果你不止想pull远程仓库里的master分支,而是所有分支,上述命令中的第一个pull操作可以用git fetch origin代替,后者默认下载origin服务器上所有分支,所取回的更新。这些分支在本地主机上要用”远程主机名/分支名”的形式读取,如remotes/origin/master。git fetch之后本地是没有分支的,本地工作目录也为空。需要在远程分支基础上,使用
1 | $ git checkout -b <new_local_branch> origin/<remote_branch> |
创建一个本地分支<new_local_branch>。
- 或者执行
1 | $ git checkout merge origin/<remote_branch> |
将origin/<remote_branch>合并到本地master分支。如果当前没有分支,git merge会在本地创建master分支。
- 或先在本地
checkout一个分支,然后执行
1 | 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文件,内容为:
1 | *.tex ident |
- 然后在你要发给合作者的主文件里,如
main.tex,加上一句包含$Id$的注释,如:
1 | % DO NOT EDIT -> $Id$ <- DO NOT EDIT |
- 安装git-ident,并在你的论文根目录下添加它的post-commit hook:
1 | $ cd .git/hooks |
commit或者checkout你的论文,这时候Git会自动将$Id$替换为当前版本的SHA1值(并不是你commit的版本号)。
这时候你就可以把你最新的commit的版本发给合作者了,静静地等待他的修改,或者自己继续写下一章节。等他返回给你的时候,执行
1 | $ /path/to/git-ident/git-find-commit.pl <your_shared_tex_file>.tex |
脚本会输出你当时交给合作者的commit版本号,比如b2234fa5之类。根据这个版本号,你可以创建/转到你的co-author分支,保存他的修改,并merge到你的当前版本:
1 | $ git checkout -b co-author b2234fa5 |
当你完成你对合作者这次修改的内容的编辑之后,可以重复这个流程,开始下一轮的“写作-合作者审阅-讨论并修订”了。
注意:git-ident需要Perl环境,并安装IPC::Run和IPC::System::Simple模块。方式为:
- 执行
sudo cpan进入cpan环境 - 执行
install MODULE_NAME安装模块