Tag Archives: git

Up and running with Emacs and Git

I code in Emacs. Emacs has good support for version control tool interaction in the vc-mode package. I want to use git and have vc-mode work with git. I also want access to special git commands that the vc-mode does not export directly. The notes below are for Ubuntu Hardy, which is what I use all the time.

Install Git

First I made sure I had git installed:

$ sudo aptitude install git-core

(I use aptitude instead of apt when I want ‘recommended’ packages to be installed automatically. Basically, I am letting aptitude choose and install other packages which are not strictly needed to install git-core, but which Ubuntu/Debian packagers think might be useful for me ;-)).

Create a template python package

$ cd workspace
$ paster create hello

I am using Python paste, paste script and paste deploy tools. The documentation for these sucks. Basically, I just use paste script to create the template for a project that can be packaged with setuptools. I am also doing this in a virtualenv. More on them some other time.

I end up with this structure:

$ cd ~/workspace/hello
$ tree
|-- build
|   `-- lib
|       `-- hello
|           `-- __init__.py
|-- hello
|   `-- __init__.py
|-- hello.egg-info
|   |-- PKG-INFO
|   |-- SOURCES.txt
|   |-- dependency_links.txt
|   |-- entry_points.txt
|   |-- not-zip-safe
|   `-- top_level.txt
|-- setup.cfg
`-- setup.py

5 directories, 10 files

Now I can build a tarball or egg with:

$ python setup.py bdist_egg
$ python setup.py bdist

Users can take the output and install this with ‘python setup.py install’.

Initial git commit

$ git init

$ git add -a .

$ git commit # write a suitable commit message

$ git log

$ git status


On ubuntu, the git-core package contains emacs support files. I just add this to my .emacs
;; -----------------------------------------------------------------------------
;; Git support
;; -----------------------------------------------------------------------------
(load "/usr/share/doc/git-core/contrib/emacs/git.el")
(load "/usr/share/doc/git-core/contrib/emacs/git-blame.el")
(load "/usr/share/doc/git-core/contrib/emacs/vc-git.el")
(add-to-list 'vc-handled-backends 'GIT)

Now, basic vc-mode works:

C-x C-f ~/workspace/hello/setup.py
C-x v l # vc-print-log
C-x v g # vc-annotate

Direct git interaction

M-x git-status

git-status-mode is the major mode for interacting with git directly. Typing h in this mode bring up a menu of commands and keybindings.

Adding a file

I add two files: hello/speaker.py and hello/main.py.  Then:

M-x git-status

Navigate to speaker.py and main.py and type ‘a’ (for git-add-file) on both.

Type ‘c’ (for git-commit-file). Type in a log message and C-c C-c to finish the log message and commit.

Adding files to gitignore

I create a .gitignore file in the top level directory of my project and put this in it:


Now I go back to my git-status window and press g (git-status-refresh), and et voila, the clutter disappears.

Committing changes

However, I notice that the hello/main.py is marked as ‘Added’ but not Uptodate. What gives? It seems git-commit-file commits only one file. Oops.  What I should have done is what I do now:

  • ‘M’ to mark all files (.gitignore and hello/main.py)
  • c to commit all files. (Type in log message)
  • g to refresh. (Shows ‘No changes’ and no files are listed)
  • t u (git-toggle-show-uptodate; now lists all files, even those which are uptodate)
  • q (git-status-quit)

Diffing changes

I edit main.py and speaker.py. Going to git-status, and pressing ‘=’ over a file gives me the diff against the committed version.

I can also mark multiple files with ‘m’, or all files with ‘M’. Now pressing ‘=’ diffs all those files.

Committing selected changes

Let’s say I’ve added a bunch of code to some files that I want to commit. I’ve also toggled a gloabl DEBUG variable to aid in my own testing, and I don’t want to commit that particular change. In subversion or cvs I’d have to ensure that the flag was set to its proper state and then commit the whole thing together. However in git, I can commit some changes (patches) and ignore others. This cannot be done from within emacs though. I have to switch to the command line and do a ‘git add –interactive’, which gives me a menu system to add selective ‘hunks’.

Back to the command line, I do a git diff –cached to see what I am going to commit. I don’t like it. I want to start choosing the changes I want to commit again:

  • git reset –mixed (reset the index)
  • git add -i (choose what to add, again)
  • git diff –cached (see what I am gonna be committing0
  • git diff (see what else is there in my WC that I have not scheduled to be committed)
  • git commit (type log and commit)


Working of git-reset and git-diff

Working of git-reset and git-diff

Gotcha with git-svn dcommit?

http://kerneltrap.org/mailarchive/git/2007/8/21/255431: this should be kept in mind/verified when using git-svn.

Tracking multiple branches in svn repo with git-svn

For some time now, have wanted to use git and git-svn to track the subversion repo at work. That repo has lots of branches, and individual changesets are merged very often between branches, making it a nightmare to find out what has already been merged, and what not.
Used to keep track of all this in a text file. Its becoming tedious. Have been trying to use git-svn to ease the pain.
On the first try, git-svn gave up halfway during the clone operation with an error message about protocol/network error. This git-svn was the one in ubuntu repositories.
Since then, installed git from source.
First I tried this way. It works, but then gitk will only show me one branch: the one I have currently checked out.
Now, I am trying this other way. Hopefully this will work better.

Code merging

I needed to merge two branches of code, both of which have had concurrent commits going on them for some time. Code is kept in svn, so the branches actually live in separate directories. Most changesets have been merged back and forth, yet residual changes remain.

I get diff to tell the files which are different between the branches.

diff -q -r -x '\.svn' -x '*.pyx' -x '*~' branch1 branch2

This produces lines like:

Files zeroc-ice-python_3.2.1/config/Make.rules and zeroc-ice-python-3.2.1-new/config/Make.rules differ
Only in zeroc-ice-python-3.2.1-new/config: Make.rules.GNU
Only in zeroc-ice-python-3.2.1-new/config: Make.rules.GNU_kFreeBSD

I need to munge the output of diff. I want to have 4 columns: directory (sans the branchname), filename, link to the file in branch1 if it exists there, link to file in branch2 if it exists there. I want this ordered/sorted by directory, and I want the output in emacs org mode format. I filter the output of the above diff command through this perl script:

while (<>) {
    if (/^Only in/) {
        m/Only in (.*): (.*)$/; $dirname=$1; $fname=$2; # sep dirpath and filename
        ($branch = $dirname) =~ s/([^\/]+).*/$1/; # the branch name is the first part of the dirpath (e.g. branch-xy/dir/path/file)
        $dirname =~ s/$branch\/?(.*)/$1/; # remove the 'branch-xy' from dirpath to get the remaining dirpath
        $_ = "| $dirname/$fname | [[file:~/workspace/couffable/branches/$branch/$dirname/$fname][Only in: $branch]] |";

if (/^Files /) {
m/Files (.*) and (.*) differ/; $left=$1;
$left =~ m/release-3-11\/(.*\/)?([^\/]+)/;
if (defined($1)) { $dirname=$1; } else { $dirname = “”; }
$left=$2; # remove branch name, sep dir and file
$_ = “| $dirname$left | [[file:~/workspace/couffable/branches/release-3-11/$dirname$left][r311]] | [[file:~/workspace/couffable/branches/release-3-11-airtel/$dirname$left][r311-airtel]] |”;
print $_ . “\n”;