Gitで、add忘れ、commit忘れ、merge忘れ、push忘れを防止するための工夫 check-git-status

仕事でもgitを使い初めることになったけど、自分も含めてadd忘れ、commit忘れ、merge忘れ、push忘れが発生するだろうなと思っていた。

これらを無くすことは、結構難しい問題ではあるんだけど、自分のローカルのリポジトリの状態を一発で確認できるだけでも、対策になると考えた。

check git status

今回は、Ubuntu限定でシステムトレイのアイコンをクリックしたら、自分のローカルのgitリポジトリ一覧を表示して、tooltipでgit statusの結果が見れるという簡単なものを、とりあえず目指したので、gtk周りが簡単らしいという噂の、pygtkを使ってみた。

  • check-git-status.py
#!/usr/bin/python
import os, sys
import gtk
import egg.trayicon     # egg == python-gnome2-extras
import gitutils
 
if len(sys.argv) != 2:
    exit('ERROR: required argument search path.')
 
searchPath = sys.argv[1]
trayIconImage = os.path.dirname(__file__) + '/icon.png'
 
def callback(widget, event):
    repos = gitutils.notCleanRepogitoryInfos(searchPath)
    menu = gtk.Menu()
    tooltips = gtk.Tooltips()
    tooltips.enable()
    tooltips.set_delay(100)
    for r in repos:
        menuitem_x = gtk.ImageMenuItem(r['menuItem'])
        menuitem_x.set_image(gtk.image_new_from_file('%s/%s.png' % (os.path.dirname(__file__), r['flag'])))
        tooltips.set_tip(menuitem_x, r['path'] + "\n" + r['status'])
        menu.append(menuitem_x)
    menuitem_exit = gtk.MenuItem("Exit")
    menu.append(menuitem_exit)
    menuitem_exit.connect("activate", lambda x: gtk.main_quit())
    menu.show_all()
    menu.popup(None, None, None, event.button, event.time, tray)
 
tray = egg.trayicon.TrayIcon("TrayIcon")
box = gtk.EventBox()
img = gtk.Image()
img.set_from_file(trayIconImage)
box.add(img)
tray.add(box)
tray.show_all()
 
box.connect("button-press-event", callback)
 
gtk.main()
 
# vim: set ts=4 sw=4 sts=4 expandtab fenc=utf-8:

gtkに関する部分は、このファイルだけで完結してしまった。簡単でいい。

  • gitutils.py
#!/usr/bin/python
import os, sys
import subprocess
 
def findRepogitories(baseDir, acc):
    for filename in os.listdir(baseDir):
        fullPath = os.path.join(baseDir, filename)
        if os.path.isdir(fullPath) and filename == '.git':
            acc.append(baseDir)
        if os.path.isdir(fullPath):
            findRepogitories(fullPath, acc)
    return acc
 
def execCommand(cmd, path):
    p = subprocess.Popen(cmd, shell=True, cwd=path, stdin=subprocess.PIPE,
        stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
        close_fds=True)
    return p.stdout.read().strip()
 
def createRepoInfo(path):
    branch = execCommand('git symbolic-ref HEAD', path)
    status = execCommand('git status', path)
    flag = 'C' if status.find('Changes not staged for commit:') > -1 else '_'
    flag += 'U' if status.find('Untracked files:') > -1 else '_'
    name = path.split('/')[-1] 
    menuItem = name 
    if branch != u'refs/heads/master':
        menuItem += ' (' + branch + ')'
    return {
        'name': name,
        'path': path,
        'branch': branch,
        'status': status,
        'flag': flag,
        'menuItem': menuItem
    }
 
def getRepogitoryInfos(repogitories):
    return map(createRepoInfo, repogitories)
 
def repogitoryInfos(path):
    repoPaths = findRepogitories(path, [])
    return getRepogitoryInfos(repoPaths)
 
def notCleanRepogitoryInfos(path):
    return filter(lambda x: x['status'].find('working directory clean') == -1, repogitoryInfos(path))
 
if __name__ == '__main__':
    path = sys.argv[1]
    print 'begin test.'
    repoPaths = findRepogitories(path, [])
    repos = getRepogitoryInfos(repoPaths)
    print repos
    print 'completed test.'
 
# vim: set ts=4 sw=4 sts=4 expandtab fenc=utf-8:

こっちは、gitリポジトリを探したり、statusを取得したりするユーティリティを格納してるファイル。 外部コマンド呼んでるだけです。

使い方

  • 第一引数に、gitリポジトリの検索対象となるディレクトリを指定します。
$ ./check-git-status.py ~/work
  • システムトレイに、アイコンが表示されるのでクリックすると、変更があるリポジトリ一覧が表示されます。
  • メニュー項目のリポジトリ名にマウスオーバーすると、git statusの結果がtooltipで表示されます。

今後の展開

このツールは、仕事でも使っていくし、add忘れ、commit忘れ、merge忘れ、push忘れの全ての対策もできてないので、ちょこちょこ更新していこうと思います。

Unityで使うには

  • まず、メニュー項目にアイコンを表示する設定にする。( http://blog.starbug1.com/archives/899 を参考に)
  • Unityで、システムトレイに表示するアプリリストに、’check-git-status’を追加する。
gsettings set com.canonical.Unity.Panel systray-whitelist "['JavaEmbeddedFrame', 'Mumble', 'Wine', 'Skype', 'hp-systray', 'scp-dbus-service', 'Dropbox', 'check-git-status']"

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.