9 output
= fd
.readlines()
11 return [x
.strip() for x
in output
]
14 rc
= os
.system("%s > /dev/null 2>&1" % cmd
)
18 class YapError(Exception):
19 def __init__(self
, msg
):
25 def takes_options(options
):
27 func
.options
= options
32 def _add_new_file(self
, file):
33 repo
= get_output('git rev-parse --git-dir')[0]
34 dir = os
.path
.join(repo
, 'yap')
39 files
= self
._get
_new
_files
()
41 path
= os
.path
.join(dir, 'new-files')
42 pickle
.dump(files
, open(path
, 'w'))
44 def _get_new_files(self
):
45 repo
= get_output('git rev-parse --git-dir')[0]
46 path
= os
.path
.join(repo
, 'yap', 'new-files')
48 files
= pickle
.load(file(path
))
55 if get_output("git ls-files --cached '%s'" % f
) != []:
60 def _remove_new_file(self
, file):
61 files
= self
._get
_new
_files
()
62 files
= filter(lambda x
: x
!= file, files
)
64 repo
= get_output('git rev-parse --git-dir')[0]
65 path
= os
.path
.join(repo
, 'yap', 'new-files')
66 pickle
.dump(files
, open(path
, 'w'))
68 def _clear_new_files(self
):
69 repo
= get_output('git rev-parse --git-dir')[0]
70 path
= os
.path
.join(repo
, 'yap', 'new-files')
73 def _assert_file_exists(self
, file):
74 if not os
.access(file, os
.R_OK
):
75 raise YapError("No such file: %s" % file)
77 def _get_staged_files(self
):
78 if run_command("git rev-parse HEAD"):
79 files
= get_output("git ls-files --cached")
81 files
= get_output("git diff-index --cached --name-only HEAD")
84 def _get_unstaged_files(self
):
85 files
= self
._get
_new
_files
()
86 files
+= get_output("git ls-files -m")
89 def _delete_branch(self
, branch
, force
):
90 current
= get_output("git symbolic-ref HEAD")[0]
91 current
= current
.replace('refs/heads/', '')
93 raise YapError("Can't delete current branch")
95 ref
= get_output("git rev-parse 'refs/heads/%s'" % branch
)
97 raise YapError("No such branch: %s" % branch
)
98 os
.system("git update-ref -d 'refs/heads/%s' '%s'" % (branch
, ref
[0]))
101 name
= get_output("git name-rev --name-only '%s'" % ref
[0])[0]
102 if name
== 'undefined':
103 os
.system("git update-ref 'refs/heads/%s' '%s'" % (branch
, ref
[0]))
104 raise YapError("Refusing to delete leaf branch (use -f to force)")
105 def _get_pager_cmd(self
):
106 if 'YAP_PAGER' in os
.environ
:
107 return os
.environ
['YAP_PAGER']
108 elif 'GIT_PAGER' in os
.environ
:
109 return os
.environ
['GIT_PAGER']
110 elif 'PAGER' in os
.environ
:
111 return os
.environ
['PAGER']
115 def _add_one(self
, file):
116 self
._assert
_file
_exists
(file)
117 x
= get_output("git ls-files '%s'" % file)
119 raise YapError("File '%s' already in repository" % file)
120 self
._add
_new
_file
(file)
122 def _rm_one(self
, file):
123 self
._assert
_file
_exists
(file)
124 if get_output("git ls-files '%s'" % file) != []:
125 os
.system("git rm --cached '%s'" % file)
126 self
._remove
_new
_file
(file)
128 def _stage_one(self
, file):
129 self
._assert
_file
_exists
(file)
130 os
.system("git update-index --add '%s'" % file)
132 def _unstage_one(self
, file):
133 self
._assert
_file
_exists
(file)
134 if run_command("git rev-parse HEAD"):
135 os
.system("git update-index --force-remove '%s'" % file)
137 os
.system("git diff-index -p HEAD '%s' | git apply -R --cached" % file)
139 def _revert_one(self
, file):
140 self
._assert
_file
_exists
(file)
141 os
.system("git checkout-index -f '%s'" % file)
143 def _parse_commit(self
, commit
):
144 lines
= get_output("git cat-file commit '%s'" % commit
)
149 if mode
!= 'commit' and l
.strip() == "":
154 commit
['log'].append(l
)
161 commit
['log'] = '\n'.join(commit
['log'])
164 def _check_commit(self
, **flags
):
165 if '-a' in flags
and '-d' in flags
:
166 raise YapError("Conflicting flags: -a and -d")
168 if '-d' not in flags
and self
._get
_unstaged
_files
():
169 if '-a' not in flags
and self
._get
_staged
_files
():
170 raise YapError("Staged and unstaged changes present. Specify what to commit")
171 os
.system("git diff-files -p | git apply --cached 2>/dev/null")
172 for f
in self
._get
_new
_files
():
175 if not self
._get
_staged
_files
():
176 raise YapError("No changes to commit")
178 def _do_uncommit(self
):
179 commit
= self
._parse
_commit
("HEAD")
180 repo
= get_output('git rev-parse --git-dir')[0]
181 dir = os
.path
.join(repo
, 'yap')
186 msg_file
= os
.path
.join(dir, 'msg')
187 fd
= file(msg_file
, 'w')
188 print >>fd
, commit
['log']
191 tree
= get_output("git rev-parse HEAD^")
192 os
.system("git update-ref -m uncommit HEAD '%s'" % tree
[0])
194 def _do_commit(self
):
195 tree
= get_output("git write-tree")[0]
196 parent
= get_output("git rev-parse HEAD 2> /dev/null")[0]
198 if os
.environ
.has_key('YAP_EDITOR'):
199 editor
= os
.environ
['YAP_EDITOR']
200 elif os
.environ
.has_key('GIT_EDITOR'):
201 editor
= os
.environ
['GIT_EDITOR']
202 elif os
.environ
.has_key('EDITOR'):
203 editor
= os
.environ
['EDITOR']
207 fd
, tmpfile
= tempfile
.mkstemp("yap")
210 repo
= get_output('git rev-parse --git-dir')[0]
211 msg_file
= os
.path
.join(repo
, 'yap', 'msg')
212 if os
.access(msg_file
, os
.R_OK
):
214 fd2
= file(tmpfile
, 'w')
215 for l
in fd1
.xreadlines():
216 print >>fd2
, l
.strip()
220 if os
.system("%s '%s'" % (editor
, tmpfile
)) != 0:
221 raise YapError("Editing commit message failed")
223 commit
= get_output("git commit-tree '%s' -p '%s' < '%s'" % (tree
, parent
, tmpfile
))
225 commit
= get_output("git commit-tree '%s' < '%s'" % (tree
, tmpfile
))
227 raise YapError("Commit failed; no log message?")
229 os
.system("git update-ref HEAD '%s'" % commit
[0])
231 def _check_rebasing(self
):
232 repo
= get_output('git rev-parse --git-dir')[0]
233 dotest
= os
.path
.join(repo
, '.dotest')
234 if os
.access(dotest
, os
.R_OK
):
235 raise YapError("A git operation is in progress. Complete it first")
236 dotest
= os
.path
.join(repo
, '..', '.dotest')
237 if os
.access(dotest
, os
.R_OK
):
238 raise YapError("A git operation is in progress. Complete it first")
240 def cmd_clone(self
, url
, directory
=""):
242 # XXX: implement in terms of init + remote add + fetch
243 os
.system("git clone '%s' %s" % (url
, directory
))
246 os
.system("git init")
248 def cmd_add(self
, *files
):
257 def cmd_rm(self
, *files
):
266 def cmd_stage(self
, *files
):
275 def cmd_unstage(self
, *files
):
284 def cmd_status(self
):
285 branch
= get_output("git symbolic-ref HEAD")[0]
286 branch
= branch
.replace('refs/heads/', '')
287 print "Current branch: %s" % branch
289 print "Files with staged changes:"
290 files
= self
._get
_staged
_files
()
296 print "Files with unstaged changes:"
297 prefix
= get_output("git rev-parse --show-prefix")
298 files
= self
._get
_unstaged
_files
()
301 f
= os
.path
.join(prefix
[0], f
)
307 def cmd_revert(self
, *files
, **flags
):
310 os
.system("git checkout-index -f -a")
321 def cmd_commit(self
, **flags
):
322 self
._check
_rebasing
()
323 self
._check
_commit
(**flags
)
327 def cmd_uncommit(self
):
331 def cmd_version(self
):
332 print "Yap version 0.1"
335 def cmd_log(self
, *paths
, **flags
):
336 "[-r <rev>] <path>..."
337 rev
= flags
.get('-r', 'HEAD')
338 paths
= ' '.join(paths
)
339 os
.system("git log --name-status '%s' -- %s" % (rev
, paths
))
342 def cmd_diff(self
, **flags
):
344 if '-u' in flags
and '-d' in flags
:
345 raise YapError("Conflicting flags: -u and -d")
347 pager
= self
._get
_pager
_cmd
()
349 os
.system("git update-index -q --refresh")
351 os
.system("git diff-files -p | %s" % pager
)
353 os
.system("git diff-index --cached -p HEAD | %s" % pager
)
355 os
.system("git diff-index -p HEAD | %s" % pager
)
357 @takes_options("fd:")
358 def cmd_branch(self
, branch
=None, **flags
):
359 "[ [-f] -d <branch> | <branch> ]"
360 force
= '-f' in flags
362 self
._delete
_branch
(flags
['-d'], force
)
366 if branch
is not None:
367 ref
= get_output("git rev-parse HEAD")
369 raise YapError("No branch point yet. Make a commit")
370 os
.system("git update-ref 'refs/heads/%s' '%s'" % (branch
, ref
[0]))
372 current
= get_output("git symbolic-ref HEAD")[0]
373 branches
= get_output("git for-each-ref --format='%(refname)' 'refs/heads/*'")
379 b
= b
.replace('refs/heads/', '')
382 def cmd_switch(self
, branch
):
384 ref
= get_output("git rev-parse 'refs/heads/%s'" % branch
)
386 raise YapError("No such branch: %s" % branch
)
388 # XXX: support merging like git-checkout
389 if self
._get
_unstaged
_files
() or self
._get
_staged
_files
():
390 raise YapError("You have uncommitted changes. Commit them first")
392 os
.system("git symbolic-ref HEAD refs/heads/'%s'" % branch
)
393 os
.system("git read-tree HEAD")
394 os
.system("git checkout-index -f -a")
398 def cmd_point(self
, where
, **flags
):
400 head
= get_output("git rev-parse HEAD")
402 raise YapError("No commit yet; nowhere to point")
404 ref
= get_output("git rev-parse '%s'" % where
)
406 raise YapError("Not a valid ref: %s" % where
)
408 if self
._get
_unstaged
_files
() or self
._get
_staged
_files
():
409 raise YapError("You have uncommitted changes. Commit them first")
411 type = get_output("git cat-file -t '%s'" % ref
[0])
412 if type and type[0] == "tag":
413 tag
= get_output("git cat-file tag '%s'" % ref
[0])
414 ref
[0] = tag
[0].split(' ')[1]
416 os
.system("git update-ref HEAD '%s'" % ref
[0])
418 if '-f' not in flags
:
419 name
= get_output("git name-rev --name-only '%s'" % head
[0])[0]
420 if name
== "undefined":
421 os
.system("git update-ref HEAD '%s'" % head
[0])
422 raise YapError("Pointing there will lose commits. Use -f to force")
424 os
.system("git read-tree HEAD")
425 os
.system("git checkout-index -f -a")
426 os
.system("git update-index --refresh")
428 def cmd_history(self
, subcmd
, *args
):
429 "amend | drop <commit>"
431 if subcmd
not in ("amend", "drop", "continue", "skip"):
435 When you have resolved the conflicts run \"yap history continue\".
436 To skip the problematic patch, run \"yap history skip\"."""
438 if subcmd
== "continue":
439 os
.system("git am -r --resolvemsg='%s'" % resolvemsg
)
442 os
.system("git reset --hard")
443 os
.system("git am --skip --resolvemsg='%s'" % resolvemsg
)
446 if subcmd
== "amend":
447 flags
, args
= getopt
.getopt(args
, "ad")
457 if run_command("git rev-parse --verify '%s'" % commit
):
458 raise YapError("Not a valid commit: %s" % commit
)
460 self
._check
_rebasing
()
462 if subcmd
== "amend":
463 self
._check
_commit
(**flags
)
465 stash
= get_output("git stash create")
466 run_command("git reset --hard")
468 if subcmd
== "amend" and not stash
:
469 raise YapError("Failed to stash; no changes?")
471 fd
, tmpfile
= tempfile
.mkstemp("yap")
474 os
.system("git format-patch -k --stdout '%s' > %s" % (commit
, tmpfile
))
475 if subcmd
== "amend":
476 self
.cmd_point(commit
, **{'-f': True})
477 run_command("git stash apply --index %s" % stash
[0])
480 stash
= get_output("git stash create")
481 run_command("git reset --hard")
483 self
.cmd_point("%s^" % commit
, **{'-f': True})
485 stat
= os
.stat(tmpfile
)
488 rc
= os
.system("git am -3 --resolvemsg=\'%s\' %s" % (resolvemsg
, tmpfile
))
490 raise YapError("Failed to apply changes")
493 run_command("git stash apply %s" % stash
[0])
499 print >> sys
.stderr
, "usage: %s <command>" % sys
.argv
[0]
500 print >> sys
.stderr
, " valid commands: init add rm stage unstage status revert commit uncommit log diff branch switch point history version"
502 def main(self
, args
):
510 debug
= os
.getenv('YAP_DEBUG')
513 meth
= self
.__getattribute
__("cmd_"+command
)
515 if "options" in meth
.__dict
__:
516 flags
, args
= getopt
.getopt(args
, meth
.options
)
522 except (TypeError, getopt
.GetoptError
):
525 print "%s %s %s" % (sys
.argv
[0], command
, meth
.__doc
__)
527 print >> sys
.stderr
, e
529 except AttributeError: