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'])):
30 print "Git archive not found."
31 print "Make sure that the current working directory contains a '.git' directory, or\nthat GIT_DIR is set appropriately."
34 files
= runProgram(['git-diff-files', '--name-only', '-z']).split('\0')
36 runXargsStyle(['git-update-index', '--remove', '--'], files
)
38 parseDiffRE
= re
.compile(':([0-9]+) ([0-9]+) ([0-9a-f]{40}) ([0-9a-f]{40}) ([MCRNADUT])([0-9]*)')
40 inp
= runProgram(prog
)
43 recs
= inp
.split("\0")
44 recs
.pop() # remove last entry (which is '')
48 m
= parseDiffRE
.match(rec
)
51 print "Unknown output from " + str(prog
) + "!: " + rec
+ "\n"
55 f
.srcMode
= m
.group(1)
56 f
.dstMode
= m
.group(2)
64 f
.srcName
= f
.dstName
= it
.next()
66 if f
.change
== 'C' or f
.change
== 'R':
68 f
.patch
= getPatch(f
.srcName
, f
.dstName
)
70 f
.patch
= getPatch(f
.srcName
)
77 def runXargsStyle(origProg
, args
):
78 steps
= range(10, len(args
), 10)
82 prog
.extend(args
[prev
:i
])
87 prog
.extend(args
[prev
:])
90 def getUnknownFiles():
93 if settings().gitExcludeFile():
94 if os
.path
.exists(settings().gitExcludeFile()):
95 args
.append('--exclude-from=' + settings().gitExcludeFile())
96 if settings().gitExcludeDir():
97 args
.append('--exclude-per-directory=' + settings().gitExcludeDir())
99 inp
= runProgram(['git-ls-files', '-z', '--others'] + args
)
100 files
= inp
.split("\0")
101 files
.pop() # remove last entry (which is '')
105 runXargsStyle(['git-update-index', '--add', '--'], files
)
106 for fileName
in files
:
108 f
.srcName
= f
.dstName
= fileName
110 f
.patch
= runProgram(['git-diff-cache', '-p', '--cached', 'HEAD', '--', fileName
])
111 fileObjects
.append(f
)
112 f
.text
= 'New file: ' + fileName
114 runXargsStyle(['git-update-index', '--force-remove', '--'], files
)
117 # HEAD is src in the returned File objects. That is, srcName is the
118 # name in HEAD and dstName is the name in the cache.
120 files
= parseDiff('git-diff-files -z')
122 doUpdateCache(f
.srcName
)
124 files
= parseDiff('git-diff-cache -z -M --cached HEAD')
128 f
.text
= 'Copy from ' + f
.srcName
+ ' to ' + f
.dstName
130 f
.text
= 'Rename from ' + f
.srcName
+ ' to ' + f
.dstName
132 f
.text
= 'New file: ' + f
.srcName
134 f
.text
= 'Deleted file: ' + f
.srcName
136 f
.text
= 'Type change: ' + f
.srcName
140 if settings().showUnknown
:
141 files
.extend(getUnknownFiles())
145 def getPatch(file, otherFile
= None):
147 f
= [file, otherFile
]
150 return runProgram(['git-diff-cache', '-p', '-M', '--cached', 'HEAD'] + f
)
152 def doUpdateCache(filename
):
153 runProgram(['git-update-index', '--remove', '--add', '--replace', '--', filename
])
155 def doCommit(filesToKeep
, filesToCommit
, msg
):
156 for file in filesToKeep
:
157 # If we have a new file in the cache which we do not want to
158 # commit we have to remove it from the cache. We will add this
159 # cache entry back in to the cache at the end of this
161 if file.change
== 'A':
162 runProgram(['git-update-index', '--force-remove',
164 elif file.change
== 'R':
165 runProgram(['git-update-index', '--force-remove',
167 runProgram(['git-update-index', '--add', '--replace',
168 '--cacheinfo', file.srcMode
, file.srcSHA
, file.srcName
])
169 elif file.change
== '?':
172 runProgram(['git-update-index', '--add', '--replace',
173 '--cacheinfo', file.srcMode
, file.srcSHA
, file.srcName
])
175 for file in filesToCommit
:
176 if file.change
== '?':
177 runProgram(['git-update-index', '--add', '--', file.dstName
])
179 tree
= runProgram(['git-write-tree'])
183 merge
= ['-p', 'MERGE_HEAD']
186 commit
= runProgram(['git-commit-tree', tree
, '-p', 'HEAD'] + merge
, msg
)
189 f
= open(os
.environ
['GIT_DIR'] + '/HEAD', 'w+')
193 raise CommitError('write to ' + os
.environ
['GIT_DIR'] + '/HEAD', e
.strerror
)
196 os
.unlink(os
.environ
['GIT_DIR'] + '/MERGE_HEAD')
200 for file in filesToKeep
:
201 # Don't add files that are going to be deleted back to the cache
202 if file.change
!= 'D' and file.change
!= '?':
203 runProgram(['git-update-index', '--add', '--replace', '--cacheinfo',
204 file.dstMode
, file.dstSHA
, file.dstName
])
206 if file.change
== 'R':
207 runProgram(['git-update-index', '--remove', '--', file.srcName
])
209 def discardFile(file):
210 runProgram(['git-read-tree', 'HEAD'])
212 if c
== 'M' or c
== 'T':
213 runProgram(['git-checkout-cache', '-f', '-q', '--', file.dstName
])
214 elif c
== 'A' or c
== 'C':
215 # The file won't be tracked by git now. We could unlink it
216 # from the working directory, but that seems a little bit
220 runProgram(['git-checkout-cache', '-f', '-q', '--', file.dstName
])
222 # Same comment applies here as to the 'A' or 'C' case.
223 runProgram(['git-checkout-cache', '-f', '-q', '--', file.srcName
])
225 def ignoreFile(file):
226 ignoreExpr
= re
.sub(r
'([][*?!\\])', r
'\\\1', file.dstName
)
228 excludefile
= settings().gitExcludeFile()
229 excludefiledir
= os
.path
.dirname(excludefile
)
230 if not os
.path
.exists(excludefiledir
):
231 os
.mkdir(excludefiledir
)
232 if not os
.path
.isdir(excludefiledir
):
234 exclude
= open(excludefile
, 'a')
235 print >> exclude
, ignoreExpr
242 os
.stat(os
.environ
['GIT_DIR'] + '/MERGE_HEAD')
248 return '''This is a merge commit if you do not want to commit a ''' + \
249 '''merge remove the file $GIT_DIR/MERGE_HEAD.'''