1fe226e0fcb1bb824764b84609f12a975d19ba19
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
)