1 # Copyright (c) 2005 Fredrik Kuivinen <freku045@student.liu.se>
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License version 2 as
5 # published by the Free Software Foundation.
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, write to the Free Software
14 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 if not os
.environ
.has_key('GIT_DIR'):
22 os
.environ
['GIT_DIR'] = '.git'
24 if not os
.environ
.has_key('GIT_OBJECT_DIRECTORY'):
25 os
.environ
['GIT_OBJECT_DIRECTORY'] = os
.environ
['GIT_DIR'] + '/objects'
27 if not (os
.path
.exists(os
.environ
['GIT_DIR']) and
28 os
.path
.exists(os
.environ
['GIT_DIR'] + '/refs') and
29 os
.path
.exists(os
.environ
['GIT_OBJECT_DIRECTORY']) and
30 os
.path
.exists(os
.environ
['GIT_OBJECT_DIRECTORY'] + '/00')):
31 print "Git archive not found."
32 print "Make sure that the current working directory contains a '.git' directory, or\nthat GIT_DIR is set appropriately."
35 parseDiffRE
= re
.compile(':([0-9]+) ([0-9]+) ([0-9a-f]{40}) ([0-9a-f]{40}) ([MCRNADUT])([0-9]*)')
37 inp
= runProgram(prog
)
40 recs
= inp
.split("\0")
41 recs
.pop() # remove last entry (which is '')
45 m
= parseDiffRE
.match(rec
)
48 print "Unknown output from " + str(prog
) + "!: " + rec
+ "\n"
52 f
.srcMode
= m
.group(1)
53 f
.dstMode
= m
.group(2)
61 f
.srcName
= f
.dstName
= it
.next()
63 if f
.change
== 'C' or f
.change
== 'R':
65 f
.patch
= getPatch(f
.srcName
, f
.dstName
)
67 f
.patch
= getPatch(f
.srcName
)
74 def runXargsStyle(origProg
, args
):
75 steps
= range(10, len(args
), 10)
79 prog
.extend(args
[prev
:i
])
84 prog
.extend(args
[prev
:])
87 def getUnknownFiles():
90 if settings().gitExcludeFile():
91 if os
.path
.exists(settings().gitExcludeFile()):
92 args
.append('--exclude-from=' + settings().gitExcludeFile())
93 if settings().gitExcludeDir():
94 args
.append('--exclude-per-directory=' + settings().gitExcludeDir())
96 inp
= runProgram(['git-ls-files', '-z', '--others'] + args
)
97 files
= inp
.split("\0")
98 files
.pop() # remove last entry (which is '')
102 runXargsStyle(['git-update-index', '--add', '--'], files
)
103 for fileName
in files
:
105 f
.srcName
= f
.dstName
= fileName
107 f
.patch
= runProgram(['git-diff-cache', '-p', '--cached', 'HEAD', '--', fileName
])
108 fileObjects
.append(f
)
109 f
.text
= 'New file: ' + fileName
111 runXargsStyle(['git-update-index', '--force-remove', '--'], files
)
114 # HEAD is src in the returned File objects. That is, srcName is the
115 # name in HEAD and dstName is the name in the cache.
117 files
= parseDiff('git-diff-files -z')
119 doUpdateCache(f
.srcName
)
121 files
= parseDiff('git-diff-cache -z -M --cached HEAD')
125 f
.text
= 'Copy from ' + f
.srcName
+ ' to ' + f
.dstName
127 f
.text
= 'Rename from ' + f
.srcName
+ ' to ' + f
.dstName
129 f
.text
= 'New file: ' + f
.srcName
131 f
.text
= 'Deleted file: ' + f
.srcName
133 f
.text
= 'Type change: ' + f
.srcName
137 if settings().gitShowUnknown
:
138 files
.extend(getUnknownFiles())
142 def getPatch(file, otherFile
= None):
144 f
= [file, otherFile
]
147 return runProgram(['git-diff-cache', '-p', '-M', '--cached', 'HEAD'] + f
)
149 def doUpdateCache(filename
):
150 runProgram(['git-update-index', '--remove', '--add', '--replace', '--', filename
])
152 def doCommit(filesToKeep
, filesToCommit
, msg
):
153 for file in filesToKeep
:
154 # If we have a new file in the cache which we do not want to
155 # commit we have to remove it from the cache. We will add this
156 # cache entry back in to the cache at the end of this
158 if file.change
== 'A':
159 runProgram(['git-update-index', '--force-remove',
161 elif file.change
== 'R':
162 runProgram(['git-update-index', '--force-remove',
164 runProgram(['git-update-index', '--add', '--replace',
165 '--cacheinfo', file.srcMode
, file.srcSHA
, file.srcName
])
166 elif file.change
== '?':
169 runProgram(['git-update-index', '--add', '--replace',
170 '--cacheinfo', file.srcMode
, file.srcSHA
, file.srcName
])
172 for file in filesToCommit
:
173 if file.change
== '?':
174 runProgram(['git-update-index', '--add', '--', file.dstName
])
176 tree
= runProgram(['git-write-tree'])
180 merge
= ['-p', 'MERGE_HEAD']
183 commit
= runProgram(['git-commit-tree', tree
, '-p', 'HEAD'] + merge
, msg
)
186 f
= open(os
.environ
['GIT_DIR'] + '/HEAD', 'w+')
190 raise CommitError('write to ' + os
.environ
['GIT_DIR'] + '/HEAD', e
.strerror
)
193 os
.unlink(os
.environ
['GIT_DIR'] + '/MERGE_HEAD')
197 for file in filesToKeep
:
198 # Don't add files that are going to be deleted back to the cache
199 if file.change
!= 'D' and file.change
!= '?':
200 runProgram(['git-update-index', '--add', '--replace', '--cacheinfo',
201 file.dstMode
, file.dstSHA
, file.dstName
])
203 if file.change
== 'R':
204 runProgram(['git-update-index', '--remove', '--', file.srcName
])
206 def discardFile(file):
207 runProgram(['git-read-tree', 'HEAD'])
209 if c
== 'M' or c
== 'T':
210 runProgram(['git-checkout-cache', '-f', '-q', '--', file.dstName
])
211 elif c
== 'A' or c
== 'C':
212 # The file won't be tracked by git now. We could unlink it
213 # from the working directory, but that seems a little bit
217 runProgram(['git-checkout-cache', '-f', '-q', '--', file.dstName
])
219 # Same comment applies here as to the 'A' or 'C' case.
220 runProgram(['git-checkout-cache', '-f', '-q', '--', file.srcName
])
222 def ignoreFile(file):
223 ignoreExpr
= re
.sub(r
'([][*?!\\])', r
'\\\1', file.dstName
)
225 excludefile
= settings().gitExcludeFile()
226 excludefiledir
= os
.path
.dirname(excludefile
)
227 if not os
.path
.exists(excludefiledir
):
228 os
.mkdir(excludefiledir
)
229 if not os
.path
.isdir(excludefiledir
):
231 exclude
= open(excludefile
, 'a')
232 print >> exclude
, ignoreExpr
239 os
.stat(os
.environ
['GIT_DIR'] + '/MERGE_HEAD')
245 return '''This is a merge commit if you do not want to commit a ''' + \
246 '''merge remove the file $GIT_DIR/MERGE_HEAD.'''