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-index', '--add', '--', fileName
])
93 f
.patch
= runProgram(['git-diff-cache', '-p', '--cached', 'HEAD', '--', fileName
])
94 runProgram(['git-update-index', '--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 if settings().gitShowUnknown
:
124 files
.extend(getUnknownFiles())
128 def getPatch(file, otherFile
= None):
130 f
= [file, otherFile
]
133 return runProgram(['git-diff-cache', '-p', '-M', '--cached', 'HEAD'] + f
)
135 def doUpdateCache(filename
):
136 runProgram(['git-update-index', '--remove', '--add', '--replace', '--', filename
])
138 def doCommit(filesToKeep
, filesToCommit
, msg
):
139 for file in filesToKeep
:
140 # If we have a new file in the cache which we do not want to
141 # commit we have to remove it from the cache. We will add this
142 # cache entry back in to the cache at the end of this
144 if file.change
== 'A':
145 runProgram(['git-update-index', '--force-remove',
147 elif file.change
== 'R':
148 runProgram(['git-update-index', '--force-remove',
150 runProgram(['git-update-index', '--add', '--replace',
151 '--cacheinfo', file.srcMode
, file.srcSHA
, file.srcName
])
152 elif file.change
== '?':
155 runProgram(['git-update-index', '--add', '--replace',
156 '--cacheinfo', file.srcMode
, file.srcSHA
, file.srcName
])
158 for file in filesToCommit
:
159 if file.change
== '?':
160 runProgram(['git-update-index', '--add', '--', file.dstName
])
162 tree
= runProgram(['git-write-tree'])
166 merge
= ['-p', 'MERGE_HEAD']
169 commit
= runProgram(['git-commit-tree', tree
, '-p', 'HEAD'] + merge
, msg
)
172 f
= open(os
.environ
['GIT_DIR'] + '/HEAD', 'w+')
176 raise CommitError('write to ' + os
.environ
['GIT_DIR'] + '/HEAD', e
.strerror
)
179 os
.unlink(os
.environ
['GIT_DIR'] + '/MERGE_HEAD')
183 for file in filesToKeep
:
184 # Don't add files that are going to be deleted back to the cache
185 if file.change
!= 'D' and file.change
!= '?':
186 runProgram(['git-update-index', '--add', '--replace', '--cacheinfo',
187 file.dstMode
, file.dstSHA
, file.dstName
])
189 if file.change
== 'R':
190 runProgram(['git-update-index', '--remove', '--', file.srcName
])
192 def discardFile(file):
193 runProgram(['git-read-tree', 'HEAD'])
195 if c
== 'M' or c
== 'T':
196 runProgram(['git-checkout-cache', '-f', '-q', '--', file.dstName
])
197 elif c
== 'A' or c
== 'C':
198 # The file won't be tracked by git now. We could unlink it
199 # from the working directory, but that seems a little bit
203 runProgram(['git-checkout-cache', '-f', '-q', '--', file.dstName
])
205 # Same comment applies here as to the 'A' or 'C' case.
206 runProgram(['git-checkout-cache', '-f', '-q', '--', file.srcName
])
208 def ignoreFile(file):
209 ignoreExpr
= re
.sub(r
'([][*?!\\])', r
'\\\1', file.dstName
)
211 excludefile
= settings().gitExcludeFile()
212 excludefiledir
= os
.path
.dirname(excludefile
)
213 if not os
.path
.exists(excludefiledir
):
214 os
.mkdir(excludefiledir
)
215 if not os
.path
.isdir(excludefiledir
):
217 exclude
= open(excludefile
, 'a')
218 print >> exclude
, ignoreExpr
225 os
.stat(os
.environ
['GIT_DIR'] + '/MERGE_HEAD')
231 return '''This is a merge commit if you do not want to commit a ''' + \
232 '''merge remove the file $GIT_DIR/MERGE_HEAD.'''