Org-mode, 用文本文件管理日常(十四)

13 篇文章 0 订阅
本文翻译自 http://doc.norang.ca/org-mode.html ,原文作者为Bernt Hansen 。由于原文较长,因此会分多篇文章来发布。转载请标记出处。

哪些我不再使用功能

下面列了些我知道的功能,但是我不怎么用它们。
org-mode 包含大量功能。有很多功能我还不清楚或者说还没有暴露出来,所以这个
列表暂时还没有完成。

归档相同层级的子树

这个是个可笑的想法,但是我觉得归档整个子树更合适些。我不介意大量任务被标记
DONE (还没有归档)。

处理字符格式

当从其它文件拷贝文本到org-mode文件中,字符中的格式会使得文档可读性很差。通过如下设置,可以移除字符中格式,这样看上
去更好点。

(setq org-emphasis-alist (quote (("*" bold "<b>" "</b>")
                                 ("/" italic "<i>" "</i>")
                                 ("_" underline "<span style=\"text-decoration:underline;\">" "</span>")
                                 ("=" org-code "<code>" "</code>" verbatim)
                                 ("~" org-verbatim "<code>" "</code>" verbatim))))

下标以及上标

我目前不会写些需要下标以及上标的文档。我通过下面方法禁用 _ 以及 ^ .

(setq org-use-sub-superscripts nil)

Yasnippet

Yasnippet 很棒,但是我不怎么用了。我使用 abbrev-mode
以及 skeletons 来替换yasnippet,并且他们默认在emacs中就可用的。

Yasnippet 包含很多代码语言片段。我在 org-mode 中使用少量的babel相关的代码片段。

我下载安装了非绑定的版本的yasnippet,这样我就可以编辑预定义的代码片段。我将yasnippet
解压到我的 ~/.emacs.d/plugins 目录,重命名 yasnippet0.5.10yasnippet
然后添加进我的 =.emacs=中。

我是这么使用代码片段:

  • begin 来生成 #+begin
  • dot 生成 graphviz
  • uml 生成 PlantUML图
  • sh 生成 bash shell 脚本
  • elisp 生成emacs lisp 脚本
  • 当开会是记录会议备忘时用来转换参会者全名

下面是 begin 代码块定义:
org-mode Yasnippet: ~/.emacs.d/plugins/yasnippet/snippets/text-mode/org-mode/begin

#name : #+begin_...#+end_
# --     
#+begin_$1 $2
$0
#+end_$1

我这样创建 #+begin_** 代码块

  • #+begin_example
  • #+begin_src
  • 其他。

输入 begin 然后按 TAB 然后 begin 文本就会替换成代码段。当输入
src TAB emacs-lisp TAB 代码块就完成了。我将这个流程缩减成 elisp TAB
因为我经常用。

输入 C-c SingleQuote(') 就会插入你需要的emacs-lisp 代码。
当进入这个块中,你就可以在一种你知道的格式以及相应着色的emacs lisp 脚本,非常
好。 C-c SingleQuote(‘) 退回到org-mode。它可以识别任何emacs编辑模式,
所以你只需要输入合适的mode名称即可。

dot

#dot : #+begin_src dot ... #+end_src
# --
#+begin_src dot :file $1 :cmdline -Kdot -Tpng
$0
#+end_src

uml

#uml : #+begin_src plantuml ... #+end_src
# --
#+begin_src plantuml :file $1
$0
#+end_src

sh

#sh: #+begin_src sh ... #+end_src
# --
#+begin_src sh :results output
$0
#+end_src

elisp

#elisp : #+begin_src emacs-lisp ...#+end_src emacs-lisp
# --
#+begin_src emacs-lisp
$0
#+end_src

节省了非常多时间。

只在奇数行或者偶数行显示标题

这个我已经使用org-indent-mode替换了。

我使用函数 org-convert-to-odd-levels 以及 org-covert-to-oddeven-levels 函数
转换成奇数行或者偶数行。我最终会设置为奇偶行,这样可以减少任务上的空格。我不觉得只显示奇数行
很好,当然全部显示也不是特别好。

同时设置父任务STARTED状态

我曾经既使用 STARTED 又使用 NEXT 状态。大部分情况来说他俩一个意思,唯一区别就是
STARTED 可以表示任务刚开始计时。然后我就会将该任务设置成 NEXT 状态。

下面代码用来将 STARTED 状态同时应用在父任务上,但是目前我不这么用了。

当一个任务被标记成 STARTED 状态(不管是手工还是计时器),那么它的父任务如果在 TODO
或者 NEXT 时候就会被标记成 STARTED 状态。只要对第一个 NEXT 子任务标记成 STARTED
那么父任务也会一样被标记。这能帮我跟踪正在进行的任务。

下面是这个功能的实现:

;; Mark parent tasks as started
(defvar bh/mark-parent-tasks-started nil)

(defun bh/mark-parent-tasks-started ()
  "Visit each parent task and change TODO states to STARTED"
  (unless bh/mark-parent-tasks-started
    (when (equal org-state "STARTED")
      (let ((bh/mark-parent-tasks-started t))
        (save-excursion
          (while (org-up-heading-safe)
            (when (member (nth 2 (org-heading-components)) (list "TODO" "NEXT"))
              (org-todo "STARTED"))))))))

(add-hook 'org-after-todo-state-change-hook 'bh/mark-parent-tasks-started 'append)

自动计时任务

我曾经在开源软件BZFlag做贡献。当需要发布时候,我会对测试BZFlag客户端计时。

我有个按键绑定在我的窗口管理器中,当执行该快捷键,就会对测试任务计时,并运行
BZFlag客户端,当完成,恢复之前的任务计时。

(defun bh/clock-in-bzflagt-task ()
  (interactive)
  (bh/clock-in-task-by-id "dcf55180-2a18-460e-8abb-a9f02f0893be"))

该函数通过shell脚本调用:

,#!/bin/sh
emacsclient -e '(bh/clock-in-bzflagt-task)'
~/git/bzflag/trunk/bzflag/src/bzflag/bzflag -directory ~/git/bzflag/trunk/bzflag/data $*
emacsclient -e '(bh/resume-clock)'

恢复计时函数将计时器应用到之前的计时任务上。

(defun bh/resume-clock ()
  (interactive)
  (if (marker-buffer org-clock-interrupted-task)
      (org-with-point-at org-clock-interrupted-task
        (org-clock-in))
    (org-clock-out)))

如果没有任务通过 bh/resume-clock 停止计时。

按键q隐藏agenda视图缓冲区

通过 Sticky Agendas ,q键默认行为就是退出缓冲区,因此这个目前
暂时不需要了。

我修改了agenda中 q 按键,替换关闭agenda 缓冲区,取而代之将其放入buffer list最尾端。
这样我就可以使用 q 来快速将agenda视图切换回来,或者通过 =f9 f9=重新生成agenda视图。

(add-hook 'org-agenda-mode-hook
          (lambda ()
            (define-key org-agenda-mode-map "q" 'bury-buffer))
          'append)

任务优先级

我使用agenda来选择下个需要做的任务。通常我不会用优先级来处理,但是在工作中
我会从我的经理那边获取有些优先级搞的任务。这种情况我会将这个任务标记成高优先级。
这时会让agenda按照优先级来显示任务。

我使用A-E来标记任务优先级,没有明确标记的任务使用最低的E优先级。

#+beginsrc emacs-lisp
(setq org-enable-priority-commands t)
(setq org-default-priority ?E)
(setq org-lowest-priority ?E)
#+endsr

使用git来同步历史,备份以及同步数据

修改org文件中折叠区域非常危险。我的做法是将org文件纳入 git 管理。

我的设置会每个小时保存所有的org文件,并自动创建commit。这方便我
及时回退并查看我的org状态,在文档生命周期期间。我使用这种方法来恢复
我不小心删除的折叠区域的数据。

自动定时提交

我的emacs设置一小时前1分钟开始保存所有org缓冲区。代码包含在 .emacs

(run-at-time "00:59" 3600 'org-save-all-org-buffers)

cron 任务任务每小时通过 org-save-all-org-buffers 保存所有缓冲区。
我使用脚本来提交代码,因此我可以按需要提交代码,并当从一台机器移动到
另一台机器,方便拉取所有修改。

crontab 细节:

0 * * * * ~/bin/org-git-sync.sh >/dev/null

~/bin/org-git-sync.sh

下面是shell脚本来创建所有的 git 提交。该循环遍历多个仓库,提交所有修改的文件。我
有如下几个org-mode仓库。

下面脚本不会创建空commit - git 只会对有修改的才会创建commit.

,#!/bin/sh
# Add org file changes to the repository
REPOS="org doc.norang.ca www.norang.ca"

for REPO in $REPOS
do
    echo "Repository: $REPO"
    cd ~/git/$REPO
    # Remove deleted files
    git ls-files --deleted -z | xargs -0 git rm >/dev/null 2>&1
    # Add new files
    git add . >/dev/null 2>&1
    git commit -m "$(date)"
done

我使用如下的 .gitignore 文件来在我的 git 仓库中排除自动生成的文件。
如果需要导入ditaa或者graphviz我将会将其加到我的repo总。默认情况下所有
PNG图像都被忽略(默认情况下我认为他们都是由 ditaa生成)

core
core.*
*.html
*~
.#*
\#*\#
*.txt
*.tex
*.aux
*.dvi
*.log
*.out
*.ics
*.pdf
*.xml
*.org-source
*.png
*.toc

Git- 让编辑更加自信

我在我所有目录下都用git,这样我所有的更新都可以通过git来跟踪。

这意味着我可以很自信的修改文件。改文件,破坏文件对我来说也不是问题。
通常我也非常方便回退到之前版本,来查看修改了那些东西。当修改配置文件时候,也非常
方便(例如 apache webserver,bind9 DNS配置等。)

这非常方便,修改文件可能让破坏文件,但是如果有 git 来跟踪修改,那么你会很方便
快速找回之前版本。当然包更新也是可以用git来管理。

我将每个修改都保存在我的 git 仓库中。

git仓库同步

我买了台 Eee PC 1000 HE作为我的主要工作电脑来替换我的6年的 Toshiba Tecra S1.

我有个LAN服务器作为我所有项目的git仓库。现在唯一的问题是我需要保留5分钟来保证我
所有的文档更新到最新,当我把它带出去(没有网络连接)。

为解决这个问题,我在上面建了个bare仓库。它包含我的org-mode仓库以及其他感兴趣的仓库。

当我需要出去,我会通过 git-sync 脚本将我的工作站的bare git仓库更新到我的
Eee PC上。对于merge冲突的仓库我先手动解决冲突,然后重新运行
git-sync 直到没有报错。这可能会花一到两分钟。然后我就可以带着我的Eee PC
离开。当我在路上,我就有所有的历史以及git仓库。

git-sync 脚本替换我之前的脚本,它里边包含所有用到的工具。它能够完成如下工作:

  • 对于当前系统上的每个仓库
    • 从远程获取对象
    • 对每个分支跟踪远程分支
      • 检查ref能不能移动
      • 如果本地仓库比较老执行快速merge
      • 如果已经是最新的,什么都不需要处理
      • 提交修改到远程仓库
      • 当本地和远程有分支时,报错

这会使得我电脑上35个仓库自动更新,用最少的干预。我需要人工处理的只剩我在Eee PC以及
我的工作站修改冲突– 这样就可以merge了。

下面是我的 git-sync 脚本。

,#!/bin/sh
#

# Local bare repository name
syncrepo=norang
reporoot=~/git

# Display repository name only once
log_repo() {
  [ "x$lastrepo" == "x$repo" ] || {
    printf "\nREPO: ${repo}\n"
    lastrepo="$repo"
  }
}

# Log a message for a repository
log_msg() {
  log_repo
  printf "  $1\n"
}

# fast-forward reference $1 to $syncrepo/$1
fast_forward_ref() {
  log_msg "fast-forwarding ref $1"
  current_ref=$(cat .git/HEAD)
  if [ "x$current_ref" = "xref: refs/heads/$1" ]
  then
    # Check for dirty index
    files=$(git diff-index --name-only HEAD --)
    git merge refs/remotes/$syncrepo/$1
  else
    git branch -f $1 refs/remotes/$syncrepo/$1
  fi
}

# Push reference $1 to $syncrepo
push_ref() {
  log_msg "Pushing ref $1"
  if ! git push --tags $syncrepo $1
  then
    exit 1
  fi
}

# Check if a ref can be moved
#   - fast-forwards if behind the sync repo and is fast-forwardable
#   - Does nothing if ref is up to date
#   - Pushes ref to $syncrepo if ref is ahead of syncrepo and fastforwardable
#   - Fails if ref and $syncrop/ref have diverged
check_ref() {
  revlist1=$(git rev-list refs/remotes/$syncrepo/$1..$1)
  revlist2=$(git rev-list $1..refs/remotes/$syncrepo/$1)
  if [ "x$revlist1" = "x" -a "x$revlist2" = "x" ]
  then
    # Ref $1 is up to date.
    :
  elif [ "x$revlist1" = "x" ]
  then
    # Ref $1 is behind $syncrepo/$1 and can be fast-forwarded.
    fast_forward_ref $1 || exit 1
  elif [ "x$revlist2" = "x" ]
  then
    # Ref $1 is ahead of $syncrepo/$1 and can be pushed.
    push_ref $1 || exit 1
  else
    log_msg "Ref $1 and $syncrepo/$1 have diverged."
    exit 1
  fi
}

# Check all local refs with matching refs in the $syncrepo
check_refs () {
  git for-each-ref refs/heads/* | while read sha1 commit ref
  do
    ref=${ref/refs\/heads\//}
    git for-each-ref refs/remotes/$syncrepo/$ref | while read sha2 commit ref2
    do
      if [ "x$sha2" != "x" -a "x$sha2" != "x" ]
      then
        check_ref $ref || exit 1
      fi
    done
  done
}

# For all repositories under $reporoot
#   Check all refs matching $syncrepo and fast-forward, or push as necessary
#   to synchronize the ref with $syncrepo
#   Bail out if ref is not fastforwardable so user can fix and rerun
    time {
  retval=0
  if find $reporoot -type d -name '*.git' | { 
      while read repo
      do
        repo=${repo/\/.git/}
        cd ${repo}
        upd=$(git remote update $syncrepo 2>&1 || retval=1)
        [ "x$upd" = "xFetching $syncrepo" ] || {
          log_repo
          printf "$upd\n"
        }
        check_refs || retval=1
      done
      exit $retval
    }
  then
    printf "\nAll done.\n"
  else
    printf "\nFix and redo.\n"
  fi
}

exit $retval
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值