Use vimdiff to resolve git/subversion/mercurial merge conflicts effectively

If you are already a vim master and you are impatient, please jump to the Quick start section at the end of this article.

In this post, I use git as an example of version control software. But you can use any other version control softwares instead.

The reasons to use vimdiff to do the merge?

  • vimdiff is free (vim)
  • vimdiff works on any OS
  • vimdiff works on non-GUI environment
  • Efficient, all the operations are finished by keyboard
  • light weight

Set up vimdiff

The vimdiff as a merge tool will display several buffers to show YOURS/THEIRS/ORIGINAL code.

First, add following code into your ~/.vimrc,


set laststatus=2 "show the status line

set statusline=%-10.3n  "buffer number

The purpose of above two lines is to display buffer number at the status line of vim. It's OPTIONAL. You don't need see the buffer number is you are familiar with the all the buffer's position. The left top is buffer number 2. The middle top is buffer number 3. The right top is buffer number 4.

Second, if you know the buffer number, you can use hot key like ",2" (press comma first, then press two key as quickly as possible) to pull change from buffer number 2. Add below code into your ~/.vimrc to set up hot keys:


map <silent> <leader>2 :diffget 2<CR> :diffupdate<CR>

map <silent> <leader>3 :diffget 3<CR> :diffupdate<CR>

map <silent> <leader>4 :diffget 4<CR> :diffupdate<CR>

Set up git

To use vimdiff as default merge tool:


git config --global merge.tool vimdiff

git config --global mergetool.prompt false

Create a git project which has conflicting merges

I already set up a "hello world" project at https://github.com/redguardtoo/test-git-mergetool for your practice.

It has three branches "master", "bob", and "chen".


git clone git://github.com/redguardtoo/test-git-mergetool.git

cd test-git-mergetool

git checkout -b bob origin/bob # create local mirror of bob branch

git checkout -b chen origin/chen # create local mirror of chen branch

Bob and Chen has edited same files. So please merge branch "bob" into "master" at first. Then merge from "chen". The merge conflicts will be created.


git branch # double check that we got three local branches: master, bob, chen

git checkout master # set master branch as main branch

git merge bob #this is ok, because bob is the first one to merge changes

git merge chen # now some conflicts created because Bob has already edited and merged same files

Resolve merge conflict

Now start merge tool:


git mergetool

Git will invoke vimdiff with the following window layout. There are four buffers in this layout: http://blog.binchen.org/wp-content/uploads/2013/06/wpid-git-merge-tool-nq8.png

Here is the explanation of each buffer:

Buffer Explanation Buffer Number
THEIRS (LOCAL) contents of the file on the current branch 2
BASE common base for the merge 3
YOURS (REMOTE) contents of the file to be merged. 4
MERGED The file containing the conflict markers. You need edit and commit this file. 1

Some people name THEIRS and YOURS buffer to LOCAL and REMOTE buffer. That's totally fine because names are just names. The point is that the top middle buffer is the BASE one which contains the original code before Bob and Chen committing any code. And the bottom buffer is the mess which contains resolved/unresolved conflicts where you actual editing work happens.

You could press hot key ",2" (comma + two), Then you pick the content from THEIRS buffer (the top left buffer). It means you will use the Bob's code and discard Chen's code in MERGED buffer.

You could press hot key ",3" (comma + three), Then you pick the content from BASE buffer (the top middle buffer). It means you will discard either Bob's code or Chen's code in MERGED buffer.

You could press hot key ",4" (comma + four), Then you pick the content from YOURS buffer (the top right buffer). It means you will use Chen's code and discard Bob code in MERGED buffer.

Or you can edit the content directly in MERGED buffer. Anyway, git only care about the file binding to MERGED buffer. Any other buffer will be ignored by git.

You can use hot key "[c language=" and "][/c]c" to navigate to previous/next conflict (including the conflict resolved by git automatically) in current file which is binding to MERGED buffer.

After finishing editing of the conflicting file in MERGED buffer, you can use hot key ":xa" to exit vimdiff. Git will open next conflicting file with vimdiff automatically.

When you have resolved all the conflicts, follow the hint of git to commit your changes.

Tips

  • A vim plugin called fugitive.vim (https://github.com/tpope/vim-fugitive) can do this too. Actually it can do much more git stuff than merge. I cannot write this article without reading its code.
  • You can use Emacs to do the similar job (http://stackoverflow.com/questions/1817370/using-ediff-as-git-mergetool). For me, Emacs start up time is too much for this task. Some people use emacsclient which has other overhead which I don't like.
  • If you prefer merge tool with GUI, you can use command line git mergetool -t gvimdiff instead.
  • The ":diffget" command is valid if and only if there are MARKED conflicts in merged buffer. If there are NOT any marked string like ">>>>" or "<<<<<" around current change (you can jump to previous/next change by press "[c language=" or "][/c]c"), it means the git have automatically resolved potential conflict for you. Review this kind of change is still wise because git is not as smart as human.
  • If you prefer navigating between the unresolved conflicts only, you can install Tim Pope's vim-unimpaired and use hot key "[n" and "]n" to do the navigation.
  • I map "[n" and "]n" to more handy hot keys:

map ]] ]n

map [[ [n

Quick start

You can use command line like "git mergetool -t vimdiff" to start vimdiff from git.

So the minimum set up is adding three lines of code into your ~/.vimrc:


map <silent> <leader>2 :diffget 2<CR> :diffupdate<CR>

map <silent> <leader>3 :diffget 3<CR> :diffupdate<CR>

map <silent> <leader>4 :diffget 4<CR> :diffupdate<CR>

Then you can press hot key ",2" ",3" ",4" in vimdiff to pull change from top three buffer. The bottom buffer is for editing the code with markers which is actually your only work space.

":help vimdiff" for other hot keys.

open README under git root directory in Emacs

I often need update my README files at the root directory of my project which is always managed by git.

As a lazy guy, I' rather use some hotkey to open that README.

So if you add below elisp code into your ~/ .emacs, you can use hot key C-c C-f to open the README:

(defun open-readme-in-git-root-directory ()
  (interactive)
  (let (filename
        (root-dir (locate-dominating-file (file-name-as-directory (file-name-directory buffer-file-name)) ".git"))
        )
    ;; (message "root-dir=%s" root-dir)
    (and root-dir (file-name-as-directory root-dir))
    (setq filename (concat root-dir "README.org"))
    (if (not (file-exists-p filename))
        (setq filename (concat root-dir "README.md"))
      )
    ;; (message "filename=%s" filename)
    (if (file-exists-p filename)
        (switch-to-buffer (find-file-noselect filename nil nil))
      (message "NO README.org or README.md found!"))
    ))
(global-set-key (kbd "C-c C-f") 'open-readme-in-git-root-directory)

,,,,

How to measure a Emacs geek

Me, for example.

The first day I changed my .emacs.d:

master $ git log --pretty=format:'%C(yellow)%h%Creset %ad %s %Cred(%an)%Creset' --date=short|grep "git config user.name"|tail -n1
11fd85f 2011-08-31 eim (Chinese input method) (chen bin)

How many times I modified my emacs.d since then:

master $ git log --pretty=format:'%C(yellow)%h%Creset %ad %s %Cred(%an)%Creset' --date=short|grep "git config user.name"|wc -l

672

My emacs.d at github: https://github.com/redguardtoo/emacs.d

The reliable way to access system clipboard from Emacs

CREATED: <2013-05-31>

UPDATED: <2015-11-20 Fri>

The Emacs clipboard questions has been asked so many times. Yet few give a complete and reliable solution.

Only a dedicated project maintained by professional developer could solve this issue once for all.

simpleclip is such a project.

I only use its APIs `simpleclip-get-contents` and `simpleclip-set-contents`.

Here is my setup:

(require 'simpleclip)
(defun copy-to-x-clipboard ()
  (interactive)
  (let ((thing (if (region-active-p)
                   (buffer-substring-no-properties (region-beginning) (region-end))
                 (thing-at-point 'symbol))))
    (simpleclip-set-contents thing)
    (message "thing => clipboard!")))

(defun paste-from-x-clipboard()
  "Paste string clipboard"
  (interactive)
  (insert (simpleclip-get-contents)))

;; Press `Alt-Y' to paste from clibpoard when in minibuffer
(defun my/paste-in-minibuffer ()
  (local-set-key (kbd "M-y") 'paste-from-x-clipboard))
(add-hook 'minibuffer-setup-hook 'my/paste-in-minibuffer)

How to refactor/rename a variable name in a function efficiently

As we dicussed in Emacs community at Google+. Although multiple-cursor is good for this task, I cannot use it because it conflicts with my favourite evil-mode (Vim simulation in Emacs).

There is another emacs plugin called iedit which could do the similar job (I usually use its command called iedit-mode-toggle-on-function)

Though iedit is good, there is still room for improvement. When using iedit-mode-toggle-on-function, we need press key twice to rename the variable name. Once to enable it, twice to disable it.

Magnar Sveen pointed out in the G+ discussion that the key point we need plugins like multiple-cursor or iedit-mode is that we can see all the changes while doing the editing.

This reminds me that evil-mode has a excellent regex string replacing mode which also show the changes when you are typing EACH character of new string.

So by using evil-mode's default regex replacing command, I can implement similar feature even more efficiently then iedit-mode!

Here is my elisp code:

(defun evilcvn-change-symbol-in-defun ()
  "mark the region in defun (definition of function) and use string replacing UI in evil-mode
to replace the symbol under cursor"
  (interactive)
  (let ((old (thing-at-point 'symbol)))
    (mark-defun)
    (unless (evil-visual-state-p)
      (evil-visual-state))
    (evil-ex (concat "'<,'>s/" (if (= 0 (length old)) "" "\<\(") old (if (= 0 (length old)) "" "\)\>/"))))
  )
(global-set-key (kbd "C-c ; s") 'evilcvn-change-symbol-in-defun)

Put you cursor above a symbol/variable and press hot key "Ctrl-c ; s", then the regex to replace that symbol is automatically created and inserted into mini-buffer. Now you only need type new string and watch.

This is the screen shot (I renamed variable "count" into "cnt" in C++ function hello): image/evil-regex-replace-nq8.png

How to write javascript/html code quickly

mixing code of javascript and html is a big problem

If we mix the javascript code into html file as below:

<ul>
  <% for(var key in service.get('Detail')){ %>
    <% if(key!='creditcard_number){ %>
      <li>
        <label for='<%= key %>'><%= key %></label>
          <div id='<%=key %>'><%= service.get('Details')[key] %></div>
        </li>
    <% }%>
<% } %>

</ul>

Then it's hard to write/debug/fix the javascript code because:

  1. html code becomes noise for javascript development.
  2. The syntax checker for javascript will not work properly.

If we mix the html code into javascript file as below:

for(var key in service.get('Detail')){
  if(key!='creditcard_number'){
    htmlRender(formatString("<li><label for='%s'>%s</label><div id='%s'>%s</div>",key,key,service.get('Details')[key]));
  }
}

Now html code becomes hard to write/debug/fix because:

  1. javascript code becomes noise for html development.
  2. The syntax checker for html will not work properly.

In summary, mixing code in different language syntax will:

  1. make code hard to read
  2. make syntax checker dysfunctional

These are the top two causes why we can not code fast in real application.

Use functional programming to avoid mix the javascript and html

So here is our objective:

  1. We need put the business logic into javascript file. The logic usually contains "for/while/if" statements.
  2. Html tags like "<div>/<span>/<ul>/<li>" need be placed in html file.

The solution is actually simple. We just need apply a little bit of functional programming skill.

So here is our html file in functional programming style:

<% forEachAttributeInService(service,function(service,key) { %>
   <li>
     <label for='<%= key %>'><%= key %></label>
     <div id='<%=key %>'><%= formatServiceAttributes(service,key) %></div>
   </li>
<%  }); %>

Here is the javascript file:

function forEachAttributeInService(service,fn) {
  for(var key in service.get('Detail')){
    if(key!='creditcard_number'){
      fn(service,key)
    }
  }
}

function formatServiceAttributes(service,key) { return service.get('Details')[key]; }

Now let's check what happens.

In the html file, the business logic is converted into combination of functional calls.

In javascript file, all the code dealing with hard coded html tags are converted to the call of anonymous functions. Those anonymous function is basically dump of html tags which is defined in html file.

You can use this technique to convert any existing code into new style. For example, a simple "if else" statement could be re-written in new style:

/ code in old style /

if (true){ console.log('<li>YES</li>'); } else { console.log('<li>NO</li>'); }

/ code in new style /

function func_if_else(f1,f2,f3){ if(f1()){ f2(); } else { f3(); } }

/ the execution of func_if_else() /

func_if_else(function(){ return true }, function(){ console.log('<li>YES</li>'); }, function(){ console.log('<li>NO</li>'); });

This is a kind of radical example. I'm only demonstrating the power of new style. I'm not suggesting you should convert any logic statement into function call. New style is not a silver bullet. It's only a useful if used properly in right timing.

The most efficient way to `git add` files in dired-mode

Add following code into your .emacs, then use "/" to execute git command on your marked files in dired-mode:

(defun diredext-exec-git-command-in-shell (command &optional arg file-list)
  "Run a shell command git COMMAND' on the marked files.
if no files marked, always operate on current line in dired-mode

" (interactive (let ((files (dired-get-marked-files t current-prefix-arg))) (list ;; Want to give feedback whether this file or marked files are used: (dired-read-shell-command "git command on %s: " current-prefix-arg files) current-prefix-arg files))) (unless (string-match "[?][ \t]\'" command) (setq command (concat command " *"))) (setq command (concat "git " command)) (dired-do-shell-command command arg file-list) (message command))

(eval-after-load 'dired '(define-key dired-mode-map "/" 'diredext-exec-git-command-in-shell))

For example, say you set the alias "a" for git command "add" in your $HOME/.gitconfig.

In order to git add files, you marked files in dired-mode and press "/", then press "a". The command git a(dd) will be executed.

The reason to use git in dired-mode is simple. I'm working for some big enterprise application. You know the enterprise guys have the tendency to create lots of small modules/files for one feature. So I need do lots of cp files somewhere and git add files things these days.

Thanks all the Emacs geeks on the Google+ who enlighten me.

How to split album into different flac and tag them under Gentoo Linux

Install necessary CLI tools

sudo emerge media-sound/shntool
sudo emerge cuetools

Download the script

See https://bbs.archlinux.org/viewtopic.php?id=58766 for the source of bash script.

Here is the full content of the bash script:

#!/bin/bash

# split image file (flac, ape, wav, etc.) according to cue-file

if [ -f "$1" ]; then i=0 for cuefile in .cue; do i=$(($i + 1)) done if [ $i -eq 1 ]; then # precies 1 cuesheet gevonden if grep -q "INDEX 01 00:00:00" .cue ; then nice shntool split -t "%n %t" -f .cue "$1" else echo "The first track has a pre-gap. Shntool will cut that off and put it in a seperate file." echo "You don't want that. Please modify the cuesheet from:" grep -m1 "INDEX 00" .cue grep -m1 "INDEX 01" *.cue echo "to:" echo " INDEX 01 00:00:00" exit 1 fi elif [ $i -eq 0 ]; then echo "No cuesheet found in the current directory." exit 1 elif [ $i -gt 1 ]; then echo "$i cuesheets found in the current directory. Please remove the superfluous cuesheets." exit 1 fi else echo "Split image file (flac, ape, wav, etc.) according to cue-file." echo "Output files are in FLAC." echo "Usage: basename $0 <image-file>" exit 1 fi

echo

album=grep -m 1 TITLE *.cue | cut -d\" -f2

artist=grep -m 1 PERFORMER *.cue | cut -d\" -f2

for file in [0-9]*.wav; do echo "Encoding $file"

<span style="color: #b9ca4a;">if</span> [[ ${<span style="color: #e7c547;">file</span>:0:1} == 0 ]] ; <span style="color: #b9ca4a;">then</span>
    <span style="color: #e7c547;">tracknr</span>=${<span style="color: #e7c547;">file</span>:1:1}
<span style="color: #b9ca4a;">else</span>
    <span style="color: #e7c547;">tracknr</span>=${<span style="color: #e7c547;">file</span>:0:2}
<span style="color: #b9ca4a;">fi</span>
<span style="color: #e7c547;">title</span>=<span style="color: #c397d8;">`echo ${file:2} | sed -e "s/.wav$//"`</span>

nice flac -s -T <span style="color: #70c0b1;">"artist=$artist"</span> -T <span style="color: #70c0b1;">"album=$album"</span> -T <span style="color: #70c0b1;">"title=$title"</span> <span style="color: #70c0b1;">\</span>
-T <span style="color: #70c0b1;">"tracknumber=$tracknr"</span> <span style="color: #70c0b1;">"$file"</span> &amp;&amp; rm <span style="color: #70c0b1;">"$file"</span>

done

Put the script somewhere and name it to "cue2flac"

Do the real conversion

# convert the ape to corresponding wav file, for example, "a.ape" is converted to "a.ape.wav"

find . -name '*.ape'|xargs -I {} ffmpeg -i {} {}.wav

# split wav and convert it to flac, make sure the *.cue exists in current directory

cue2flac a.ape.wav

# do the cleaning (OPTIONAL)

rm .ape .wav

The minimum .emacs for debug purpose

I've installed more than 100 emacs plugins.

Since I publicized my .emacs at github, lots of people have copied my configuration. Now it becomes my obligation to solve all the plugin compatibility issue reported by my users.

To solve the issue, I need a minimum .emacs for testing the plugin.

So here is my minimum .emacs,


(show-paren-mode 1)

(eval-when-compile (require 'cl))



;; test elisps download from internet here

(setq test-elisp-dir "~/test-elisp/")

(if (not (file-exists-p (expand-file-name test-elisp-dir)))
    (make-directory (expand-file-name test-elisp-dir))
    )


(setq load-path
      (append
        (loop for dir in (directory-files test-elisp-dir)
              unless (string-match "^\\." dir)
              collecting (expand-file-name (concat test-elisp-dir dir)))
        load-path))


;; package repositories

(require 'package)

(add-to-list 'package-archives '("marmalade" .  "http://marmalade-repo.org/packages/") t)

(add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t)

(package-initialize)



;; ==== put your code below this line!

;;

The code is straight forward. I scan any sub-directories in ~/test-elisp and add them into load-path. I also add URLs of third party repositories so that I can download and install packages from internet conviniently.

I also upload the .emacs to dropbox, so you can use one bash liner to download it:


cd ~/;wget https://dl.dropboxusercontent.com/u/858862/emacs/.emacs;mkdir -p ~/test-elisp;

How to use keyboard to navigate feedly.com

Here is the detailed steps,

  1. Install keysnail (a firefox addon)
  2. Install HoK (no less than version 1.3.9) through keysnail
  3. Add below code into .keysnail.js
/* HoK /
key.setViewKey('e', function (aEvent, aArg) {
        ext.exec("hok-start-foreground-mode", aArg);
}, 'Hok - Foreground hint mode', true);

key.setViewKey('E', function (aEvent, aArg) { ext.exec("hok-start-background-mode", aArg); }, 'HoK - Background hint mode', true);

key.setViewKey(';', function (aEvent, aArg) { ext.exec("hok-start-extended-mode", aArg); }, 'HoK - Extented hint mode', true);

key.setViewKey(['C-c', 'C-e'], function (aEvent, aArg) { ext.exec("hok-start-continuous-mode", aArg); }, 'Start continuous HaH', true);

hook.addToHook('PluginLoaded', function () { if (!plugins.hok) return;

/ HoK 1.3.9+ requried / plugins.hok.pOptions.selector = plugins.hok.pOptions.selector / feedly / + ", [data-uri]" + ", [data-selector-toggle]" + ", [data-page-action]" + ", [data-app-action]" ; });

  1. Restart Firefox, logged into http://www.feedly.com and press hot key "e"
  2. Enjoy!