1 """Performs a 3-way merge for GIT files
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
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):
36 autoimerge = ConfigOption('stgit', 'autoimerge')
37 keeporig = ConfigOption('stgit', 'keeporig')
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
54 extensions = file_extensions()
55 line = MRun('git', 'checkout-index', '--stage=all', '--', filename
57 stages, path = line.split('\t')
58 stages = dict(zip(['ancestor', 'current', 'patched'],
61 for stage, fn in stages.iteritems():
62 if stages[stage] == '.':
65 newname = filename + extensions[stage]
66 if os.path.exists(newname):
67 # remove the stage if it is already checked out
69 os.rename(stages[stage], newname)
70 stages[stage] = newname
74 def __remove_stages(filename):
75 """Remove the merge stages from the working directory
77 extensions = file_extensions()
78 for ext in extensions.itervalues():
80 if os.path.isfile(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().
88 stages = __checkout_stages(filename)
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']:
97 files_dict = {'branch1': stages['current'],
98 'ancestor': stages['ancestor'],
99 'branch2': stages['patched'],
101 imerger = config.get('stgit.i3merge')
104 files_dict = {'branch1': stages['current'],
105 'branch2': stages['patched'],
107 imerger = config.get('stgit.i2merge')
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)
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
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)
136 """Merge one file if interactive is allowed or check out the stages
139 if str(autoimerge) == 'yes':
141 interactive_merge(filename)
142 except GitMergeException, ex:
147 if str(keeporig) == 'yes':
148 __checkout_stages(filename)