git checkout doesn't require explicit branch parameter
[matrix.git] / matrix / git.py
1 # Copyright (C) 2007-2008 Movial Oy
2 # Authors: Timo Savola <tsavola@movial.fi>
3 #          Toni Timonen
4
5 import os, sys
6 import bz2
7
8 import config
9
10 class Error(RuntimeError):
11         def __init__(self, args, workdir=None):
12                 command = ' '.join(args)
13
14                 if workdir:
15                         message = 'Failed in %s: %s' % (workdir, command)
16                 else:
17                         message = 'Failed: %s' % command
18
19                 RuntimeError.__init__(self, message)
20
21 def call(args, workdir=None, quiet=False, fail=False):
22         args = ['git'] + args
23
24         if config.debug:
25                 if workdir:
26                         print 'Executing in %s:' % workdir,
27                 else:
28                         print 'Executing:',
29
30                 print ' '.join(args)
31
32         pid = os.fork()
33         if pid == 0:
34                 try:
35                         if workdir:
36                                 os.chdir(workdir)
37
38                         if quiet:
39                                 fd = os.open('/dev/null', os.O_WRONLY)
40                                 os.dup2(fd, 1)
41                                 os.dup2(fd, 2)
42                                 if fd not in (1, 2):
43                                         os.close(fd)
44
45                         os.execvp(args[0], args)
46
47                 except Exception, e:
48                         print >>sys.stderr, e
49
50                 os._exit(127)
51
52         pid, status = os.waitpid(pid, 0)
53         if status != 0 and fail:
54                 raise Error(args, workdir)
55
56         return status
57
58 def call_output(args, workdir=None, fd=None, lines=True,wait=True):
59         args = ['git'] + args
60
61         if config.debug:
62                 if workdir:
63                         print 'Executing in %s:' % workdir,
64                 else:
65                         print 'Executing:',
66
67                 print ' '.join(args)
68
69         if fd is None:
70                 input, output = os.pipe()
71
72         pid = os.fork()
73         if pid == 0:
74                 try:
75                         if workdir:
76                                 os.chdir(workdir)
77
78                         if fd is not None:
79                                 if fd != 1:
80                                         os.dup2(fd, 1)
81                         else:
82                                 os.close(input)
83                                 if output != 1:
84                                         os.dup2(output, 1)
85                                         os.close(output)
86
87                         os.execvp(args[0], args)
88
89                 except Exception, e:
90                         print >>sys.stderr, e
91
92                 os._exit(127)
93
94         if fd is None:
95                 os.close(output)
96
97                 f = os.fdopen(input, 'r')
98                 if lines:
99                         contents = f.readlines()
100                 else:
101                         contents = f.read()
102                 f.close()
103
104         if not wait and fd is not None:
105                 return
106         pid, status = os.waitpid(pid, 0)
107         if status != 0:
108                 raise Error(args, workdir)
109
110         if fd is None:
111                 if lines:
112                         return [i.strip() for i in contents]
113                 else:
114                         return contents
115
116 def peek_remote(url, quiet=False):
117         args = ['peek-remote', url]
118         return call(args, quiet=quiet, fail=not quiet) == 0
119
120 def clone(name, url, checkout=True):
121         options = []
122         if not checkout:
123                 options = ['-n']
124
125         call(['clone'] + options + [url, name], fail=True)
126
127 def pull(name, url=None, refspec=None):
128         if url is None and refspec is None:
129                 cmd = ['pull']
130         else:
131                 cmd = ['pull',url,refspec]
132         if call(cmd, workdir=name) != 0:
133                 raise Error(cmd, name)
134
135 def create_branch(name, branch, point, checkout=False,link=False,force=False):
136         if checkout:
137                 command = ['checkout', '-b']
138         else:
139                 command = ['branch']
140
141         if not force:
142                 call(command + [branch, point], workdir=name, fail=True)
143         else:
144                 #If we need more force, branch to temprepo first and rename
145                 call(command + ['_matrix_tmp', point], workdir=name, fail=True)
146                 call(['branch','-M','_matrix_tmp',branch],workdir=name,fail=True)
147
148         if link:
149                 call(['config','branch.%s.merge'%branch,'refs/heads/%s'%branch],workdir=name)
150
151 def delete_branch(name, branch, force=False, quiet=False):
152         if force:
153                 command = '-D'
154         else:
155                 command = '-d'
156
157         return call(['branch', command, branch], name, quiet, not quiet) == 0
158
159 def checkout(name, branch=None):
160         cmd = ['checkout']
161         if branch:
162                 cmd.append(branch)
163
164         call(cmd, workdir=name, fail=True)
165
166 def rev_parse(name, arg):
167         lines = call_output(['rev-parse', arg], workdir=name)
168         if lines:
169                 return lines[0].strip()
170         else:
171                 return None
172
173 def getvar(name,var):
174         if not os.path.exists(name):
175                 return None
176         res=call_output(['config',var],workdir=name)
177         if res and len(res)==1:
178                 return res[0]
179         return None
180
181 def ls_tree(name, treeish, name_only=False, recursive=False):
182         options = []
183         if name_only:
184                 options.append('--name-only')
185         if recursive:
186                 options.append('-r')
187
188         return call_output(['ls-tree'] + options + [treeish], workdir=name)
189
190 def ls_files(name, options):
191         return call_output(['ls-files'] + options, workdir=name)
192
193
194 def archive(name, arch_name,prefix=None,branch='HEAD'):
195         cmd = ['archive','--format=tar']
196         if prefix:
197                 cmd.append('--prefix=%s'%prefix)
198
199         inp,out = os.pipe()
200         call_output(cmd+[branch],workdir=name,fd=out,wait=False)
201         os.close(out)
202         infile=os.fdopen(inp)
203         outfile=bz2.BZ2File(arch_name,'w')
204         data=infile.read(1024*50)
205         while data:
206                 outfile.write(data)
207                 data=infile.read(1024*50)
208         infile.close()
209         outfile.close()
210
211 def describe(name,branch='HEAD'):
212         cmd={'args':['describe',branch],'workdir':name}
213         try:
214                 call(quiet=True,fail=True,**cmd)
215                 res=call_output(**cmd)
216         except Error,e:
217                 return None
218
219         if res and len(res)==1:
220                 return res[0]
221         return None
222
223 def cat_file(name, hash, blob=False, size=False):
224         if blob:
225                 option = 'blob'
226         elif size:
227                 option = '-s'
228         else:
229                 return None
230
231         contents = call_output(['cat-file', option, hash], workdir=name,
232                                lines=False)
233
234         if size:
235                 return int(contents.strip())
236         else:
237                 return contents
238
239 def log(name, options, fd=None):
240         return call_output(['log'] + options + ['--'], workdir=name, fd=fd)