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 getUnknownFiles():
77 if settings().gitExcludeFile():
78 if os
.path
.exists(settings().gitExcludeFile()):
79 args
.append('--exclude-from=' + settings().gitExcludeFile())
80 if settings().gitExcludeDir():
81 args
.append('--exclude-per-directory=' + settings().gitExcludeDir())
83 inp
= runProgram(['git-ls-files', '-z', '--others'] + args
)
84 files
= inp
.split("\0")
85 files
.pop() # remove last entry (which is '')
88 for fileName
in files
:
90 f
.srcName
= f
.dstName
= fileName
92 runProgram(['git-update-cache', '--add', '--', fileName
])
93 f
.patch
= runProgram(['git-diff-cache', '-p', '--cached', 'HEAD', '--', fileName
])
94 runProgram(['git-update-cache', '--force-remove', '--', fileName
])
96 f
.text
= 'New file: ' + fileName
100 # HEAD is src in the returned File objects. That is, srcName is the
101 # name in HEAD and dstName is the name in the cache.
103 files
= parseDiff('git-diff-files -z')
105 doUpdateCache(f
.srcName
)
107 files
= parseDiff('git-diff-cache -z -M --cached HEAD')
111 f
.text
= 'Copy from ' + f
.srcName
+ ' to ' + f
.dstName
113 f
.text
= 'Rename from ' + f
.srcName
+ ' to ' + f
.dstName
115 f
.text
= 'New file: ' + f
.srcName
117 f
.text
= 'Deleted file: ' + f
.srcName
119 f
.text
= 'Type change: ' + f
.srcName
123 return files
+ getUnknownFiles()
125 def getPatch(file, otherFile
= None):
127 f
= [file, otherFile
]
130 return runProgram(['git-diff-cache', '-p', '-M', '--cached', 'HEAD'] + f
)
132 def doUpdateCache(filename
):
133 runProgram(['git-update-cache', '--remove', '--add', '--replace', '--', filename
])
135 def doCommit(filesToKeep
, filesToCommit
, msg
):
136 for file in filesToKeep
:
137 # If we have a new file in the cache which we do not want to
138 # commit we have to remove it from the cache. We will add this
139 # cache entry back in to the cache at the end of this
141 if file.change
== 'A':
142 runProgram(['git-update-cache', '--force-remove',
144 elif file.change
== 'R':
145 runProgram(['git-update-cache', '--force-remove',
147 runProgram(['git-update-cache', '--add', '--replace',
148 '--cacheinfo', file.srcMode
, file.srcSHA
, file.srcName
])
149 elif file.change
== '?':
152 runProgram(['git-update-cache', '--add', '--replace',
153 '--cacheinfo', file.srcMode
, file.srcSHA
, file.srcName
])
155 for file in filesToCommit
:
156 if file.change
== '?':
157 runProgram(['git-update-cache', '--add', '--', file.dstName
])
159 tree
= runProgram(['git-write-tree'])
163 merge
= ['-p', 'MERGE_HEAD']
166 commit
= runProgram(['git-commit-tree', tree
, '-p', 'HEAD'] + merge
, msg
)
169 f
= open(os
.environ
['GIT_DIR'] + '/HEAD', 'w+')
173 raise CommitError('write to ' + os
.environ
['GIT_DIR'] + '/HEAD', e
.strerror
)
176 os
.unlink(os
.environ
['GIT_DIR'] + '/MERGE_HEAD')
180 for file in filesToKeep
:
181 # Don't add files that are going to be deleted back to the cache
182 if file.change
!= 'D' and file.change
!= '?':
183 runProgram(['git-update-cache', '--add', '--replace', '--cacheinfo',
184 file.dstMode
, file.dstSHA
, file.dstName
])
186 if file.change
== 'R':
187 runProgram(['git-update-cache', '--remove', '--', file.srcName
])
189 def discardFile(file):
190 runProgram(['git-read-tree', 'HEAD'])
192 if c
== 'M' or c
== 'T':
193 runProgram(['git-checkout-cache', '-f', '-q', '--', file.dstName
])
194 elif c
== 'A' or c
== 'C':
195 # The file won't be tracked by git now. We could unlink it
196 # from the working directory, but that seems a little bit
200 runProgram(['git-checkout-cache', '-f', '-q', '--', file.dstName
])
202 # Same comment applies here as to the 'A' or 'C' case.
203 runProgram(['git-checkout-cache', '-f', '-q', '--', file.srcName
])
205 def ignoreFile(file):
206 ignoreExpr
= re
.sub(r
'([][*?!\\])', r
'\\\1', file.dstName
)
208 excludefile
= settings().gitExcludeFile()
209 excludefiledir
= os
.path
.dirname(excludefile
)
210 if not os
.path
.exists(excludefiledir
):
211 os
.mkdir(excludefiledir
)
212 if not os
.path
.isdir(excludefiledir
):
214 exclude
= open(excludefile
, 'a')
215 print >> exclude
, ignoreExpr
222 os
.stat(os
.environ
['GIT_DIR'] + '/MERGE_HEAD')
228 return '''This is a merge commit if you do not want to commit a ''' + \
229 '''merge remove the file $GIT_DIR/MERGE_HEAD.'''