Do not mess-up with commit message formatting when sending email
[stgit/dwhite.git] / stgit / gitmergeonefile.py
blob1fe226e0fcb1bb824764b84609f12a975d19ba19
1 """Performs a 3-way merge for GIT files
2 """
4 __copyright__ = """
5 Copyright (C) 2006, Catalin Marinas <catalin.marinas@gmail.com>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as
9 published by the Free Software Foundation.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 """
21 import sys, os
22 from stgit.exception import *
23 from stgit import basedir
24 from stgit.config import config, file_extensions, ConfigOption
25 from stgit.utils import append_string
26 from stgit.out import *
27 from stgit.run import *
29 class GitMergeException(StgException):
30 pass
34 # Options
36 autoimerge = ConfigOption('stgit', 'autoimerge')
37 keeporig = ConfigOption('stgit', 'keeporig')
40 # Utility functions
42 def __str2none(x):
43 if x == '':
44 return None
45 else:
46 return x
48 class MRun(Run):
49 exc = GitMergeException # use a custom exception class on errors
51 def __checkout_stages(filename):
52 """Check-out the merge stages in the index for the give file
53 """
54 extensions = file_extensions()
55 line = MRun('git', 'checkout-index', '--stage=all', '--', filename
56 ).output_one_line()
57 stages, path = line.split('\t')
58 stages = dict(zip(['ancestor', 'current', 'patched'],
59 stages.split(' ')))
61 for stage, fn in stages.iteritems():
62 if stages[stage] == '.':
63 stages[stage] = None
64 else:
65 newname = filename + extensions[stage]
66 if os.path.exists(newname):
67 # remove the stage if it is already checked out
68 os.remove(newname)
69 os.rename(stages[stage], newname)
70 stages[stage] = newname
72 return stages
74 def __remove_stages(filename):
75 """Remove the merge stages from the working directory
76 """
77 extensions = file_extensions()
78 for ext in extensions.itervalues():
79 fn = filename + ext
80 if os.path.isfile(fn):
81 os.remove(fn)
83 def interactive_merge(filename):
84 """Run the interactive merger on the given file. Stages will be
85 removed according to stgit.keeporig. If successful and stages
86 kept, they will be removed via git.resolved().
87 """
88 stages = __checkout_stages(filename)
90 try:
91 # Check whether we have all the files for the merge.
92 if not (stages['current'] and stages['patched']):
93 raise GitMergeException('Cannot run the interactive merge')
95 if stages['ancestor']:
96 three_way = True
97 files_dict = {'branch1': stages['current'],
98 'ancestor': stages['ancestor'],
99 'branch2': stages['patched'],
100 'output': filename}
101 imerger = config.get('stgit.i3merge')
102 else:
103 three_way = False
104 files_dict = {'branch1': stages['current'],
105 'branch2': stages['patched'],
106 'output': filename}
107 imerger = config.get('stgit.i2merge')
109 if not imerger:
110 raise GitMergeException, 'No interactive merge command configured'
112 mtime = os.path.getmtime(filename)
114 out.start('Trying the interactive %s merge'
115 % (three_way and 'three-way' or 'two-way'))
116 err = os.system(imerger % files_dict)
117 out.done()
118 if err != 0:
119 raise GitMergeException, 'The interactive merge failed'
120 if not os.path.isfile(filename):
121 raise GitMergeException, 'The "%s" file is missing' % filename
122 if mtime == os.path.getmtime(filename):
123 raise GitMergeException, 'The "%s" file was not modified' % filename
124 finally:
125 # keep the merge stages?
126 if str(keeporig) != 'yes':
127 __remove_stages(filename)
129 def clean_up(filename):
130 """Remove merge conflict stages if they were generated.
132 if str(keeporig) == 'yes':
133 __remove_stages(filename)
135 def merge(filename):
136 """Merge one file if interactive is allowed or check out the stages
137 if keeporig is set.
139 if str(autoimerge) == 'yes':
140 try:
141 interactive_merge(filename)
142 except GitMergeException, ex:
143 out.error(str(ex))
144 return False
145 return True
147 if str(keeporig) == 'yes':
148 __checkout_stages(filename)
150 return False