##// END OF EJS Templates
Add instructions how to redo/finish failed merge with local working directory....
Thomas Arendsen Hein -
r3869:ad6f34c8 default
parent child Browse files
Show More
@@ -0,0 +1,50 b''
1 #!/bin/sh
2
3 hg init
4
5 echo "# revision 0"
6 echo "unchanged" > unchanged
7 echo "remove me" > remove
8 echo "copy me" > copy
9 echo "move me" > move
10 for i in 1 2 3 4 5 6 7 8 9; do
11 echo "merge ok $i" >> zzz1_merge_ok
12 done
13 echo "merge bad" > zzz2_merge_bad
14 hg ci -Am "revision 0" -d "1000000 0"
15
16 echo "# revision 1"
17 hg rm remove
18 hg mv move moved
19 hg cp copy copied
20 echo "added" > added
21 hg add added
22 echo "new first line" > zzz1_merge_ok
23 hg cat zzz1_merge_ok >> zzz1_merge_ok
24 echo "new last line" >> zzz2_merge_bad
25 hg ci -m "revision 1" -d "1000000 0"
26
27 echo "# local changes to revision 0"
28 hg co 0
29 echo "new last line" >> zzz1_merge_ok
30 echo "another last line" >> zzz2_merge_bad
31 hg diff --nodates | grep "^[+-][^<>]"
32 hg st
33
34 echo "# local merge with bad merge tool"
35 HGMERGE=false hg co
36 hg co 0
37 hg diff --nodates | grep "^[+-][^<>]"
38 hg st
39
40 echo "# local merge with conflicts"
41 HGMERGE=merge hg co
42 hg co 0
43 hg diff --nodates | grep "^[+-][^<>]"
44 hg st
45
46 echo "# local merge without conflicts"
47 hg revert zzz2_merge_bad
48 HGMERGE=merge hg co
49 hg diff --nodates | grep "^[+-][^<>]"
50 hg st
@@ -0,0 +1,67 b''
1 # revision 0
2 adding copy
3 adding move
4 adding remove
5 adding unchanged
6 adding zzz1_merge_ok
7 adding zzz2_merge_bad
8 # revision 1
9 # local changes to revision 0
10 4 files updated, 0 files merged, 3 files removed, 0 files unresolved
11 --- a/zzz1_merge_ok
12 +++ b/zzz1_merge_ok
13 +new last line
14 --- a/zzz2_merge_bad
15 +++ b/zzz2_merge_bad
16 +another last line
17 M zzz1_merge_ok
18 M zzz2_merge_bad
19 # local merge with bad merge tool
20 merging zzz1_merge_ok
21 merging zzz1_merge_ok failed!
22 merging zzz2_merge_bad
23 merging zzz2_merge_bad failed!
24 3 files updated, 0 files merged, 2 files removed, 2 files unresolved
25 There are unresolved merges with locally modified files.
26 You can redo the full merge using:
27 hg update 0
28 hg update 1
29 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
30 --- a/zzz1_merge_ok
31 +++ b/zzz1_merge_ok
32 +new last line
33 --- a/zzz2_merge_bad
34 +++ b/zzz2_merge_bad
35 +another last line
36 M zzz1_merge_ok
37 M zzz2_merge_bad
38 # local merge with conflicts
39 merge: warning: conflicts during merge
40 merging zzz1_merge_ok
41 merging zzz2_merge_bad
42 merging zzz2_merge_bad failed!
43 3 files updated, 1 files merged, 2 files removed, 1 files unresolved
44 There are unresolved merges with locally modified files.
45 You can finish the partial merge using:
46 hg update 0
47 hg update 1
48 2 files updated, 0 files merged, 3 files removed, 0 files unresolved
49 --- a/zzz1_merge_ok
50 +++ b/zzz1_merge_ok
51 +new first line
52 +new last line
53 --- a/zzz2_merge_bad
54 +++ b/zzz2_merge_bad
55 +another last line
56 +=======
57 +new last line
58 M zzz1_merge_ok
59 M zzz2_merge_bad
60 # local merge without conflicts
61 merging zzz1_merge_ok
62 4 files updated, 1 files merged, 2 files removed, 0 files unresolved
63 --- a/zzz1_merge_ok
64 +++ b/zzz1_merge_ok
65 +new last line
66 M zzz1_merge_ok
67 ? zzz2_merge_bad.orig
@@ -1,271 +1,279 b''
1 1 # hg.py - repository classes for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 5 #
6 6 # This software may be used and distributed according to the terms
7 7 # of the GNU General Public License, incorporated herein by reference.
8 8
9 9 from node import *
10 10 from repo import *
11 11 from demandload import *
12 12 from i18n import gettext as _
13 13 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
14 14 demandload(globals(), "errno lock os shutil util merge@_merge verify@_verify")
15 15
16 16 def _local(path):
17 17 return (os.path.isfile(util.drop_scheme('file', path)) and
18 18 bundlerepo or localrepo)
19 19
20 20 schemes = {
21 21 'bundle': bundlerepo,
22 22 'file': _local,
23 23 'hg': httprepo,
24 24 'http': httprepo,
25 25 'https': httprepo,
26 26 'old-http': statichttprepo,
27 27 'ssh': sshrepo,
28 28 'static-http': statichttprepo,
29 29 }
30 30
31 31 def _lookup(path):
32 32 scheme = 'file'
33 33 if path:
34 34 c = path.find(':')
35 35 if c > 0:
36 36 scheme = path[:c]
37 37 thing = schemes.get(scheme) or schemes['file']
38 38 try:
39 39 return thing(path)
40 40 except TypeError:
41 41 return thing
42 42
43 43 def islocal(repo):
44 44 '''return true if repo or path is local'''
45 45 if isinstance(repo, str):
46 46 try:
47 47 return _lookup(repo).islocal(repo)
48 48 except AttributeError:
49 49 return False
50 50 return repo.local()
51 51
52 52 repo_setup_hooks = []
53 53
54 54 def repository(ui, path='', create=False):
55 55 """return a repository object for the specified path"""
56 56 repo = _lookup(path).instance(ui, path, create)
57 57 for hook in repo_setup_hooks:
58 58 hook(ui, repo)
59 59 return repo
60 60
61 61 def defaultdest(source):
62 62 '''return default destination of clone if none is given'''
63 63 return os.path.basename(os.path.normpath(source))
64 64
65 65 def clone(ui, source, dest=None, pull=False, rev=None, update=True,
66 66 stream=False):
67 67 """Make a copy of an existing repository.
68 68
69 69 Create a copy of an existing repository in a new directory. The
70 70 source and destination are URLs, as passed to the repository
71 71 function. Returns a pair of repository objects, the source and
72 72 newly created destination.
73 73
74 74 The location of the source is added to the new repository's
75 75 .hg/hgrc file, as the default to be used for future pulls and
76 76 pushes.
77 77
78 78 If an exception is raised, the partly cloned/updated destination
79 79 repository will be deleted.
80 80
81 81 Arguments:
82 82
83 83 source: repository object or URL
84 84
85 85 dest: URL of destination repository to create (defaults to base
86 86 name of source repository)
87 87
88 88 pull: always pull from source repository, even in local case
89 89
90 90 stream: stream raw data uncompressed from repository (fast over
91 91 LAN, slow over WAN)
92 92
93 93 rev: revision to clone up to (implies pull=True)
94 94
95 95 update: update working directory after clone completes, if
96 96 destination is local repository
97 97 """
98 98 if isinstance(source, str):
99 99 src_repo = repository(ui, source)
100 100 else:
101 101 src_repo = source
102 102 source = src_repo.url()
103 103
104 104 if dest is None:
105 105 dest = defaultdest(source)
106 106 ui.status(_("destination directory: %s\n") % dest)
107 107
108 108 def localpath(path):
109 109 if path.startswith('file://'):
110 110 return path[7:]
111 111 if path.startswith('file:'):
112 112 return path[5:]
113 113 return path
114 114
115 115 dest = localpath(dest)
116 116 source = localpath(source)
117 117
118 118 if os.path.exists(dest):
119 119 raise util.Abort(_("destination '%s' already exists") % dest)
120 120
121 121 class DirCleanup(object):
122 122 def __init__(self, dir_):
123 123 self.rmtree = shutil.rmtree
124 124 self.dir_ = dir_
125 125 def close(self):
126 126 self.dir_ = None
127 127 def __del__(self):
128 128 if self.dir_:
129 129 self.rmtree(self.dir_, True)
130 130
131 131 dir_cleanup = None
132 132 if islocal(dest):
133 133 dir_cleanup = DirCleanup(dest)
134 134
135 135 abspath = source
136 136 copy = False
137 137 if src_repo.local() and islocal(dest):
138 138 abspath = os.path.abspath(source)
139 139 copy = not pull and not rev
140 140
141 141 src_lock, dest_lock = None, None
142 142 if copy:
143 143 try:
144 144 # we use a lock here because if we race with commit, we
145 145 # can end up with extra data in the cloned revlogs that's
146 146 # not pointed to by changesets, thus causing verify to
147 147 # fail
148 148 src_lock = src_repo.lock()
149 149 except lock.LockException:
150 150 copy = False
151 151
152 152 if copy:
153 153 def force_copy(src, dst):
154 154 try:
155 155 util.copyfiles(src, dst)
156 156 except OSError, inst:
157 157 if inst.errno != errno.ENOENT:
158 158 raise
159 159
160 160 src_store = os.path.realpath(src_repo.spath)
161 161 if not os.path.exists(dest):
162 162 os.mkdir(dest)
163 163 dest_path = os.path.realpath(os.path.join(dest, ".hg"))
164 164 os.mkdir(dest_path)
165 165 if src_repo.spath != src_repo.path:
166 166 dest_store = os.path.join(dest_path, "store")
167 167 os.mkdir(dest_store)
168 168 else:
169 169 dest_store = dest_path
170 170 # copy the requires file
171 171 force_copy(src_repo.join("requires"),
172 172 os.path.join(dest_path, "requires"))
173 173 # we lock here to avoid premature writing to the target
174 174 dest_lock = lock.lock(os.path.join(dest_store, "lock"))
175 175
176 176 files = ("data",
177 177 "00manifest.d", "00manifest.i",
178 178 "00changelog.d", "00changelog.i")
179 179 for f in files:
180 180 src = os.path.join(src_store, f)
181 181 dst = os.path.join(dest_store, f)
182 182 force_copy(src, dst)
183 183
184 184 # we need to re-init the repo after manually copying the data
185 185 # into it
186 186 dest_repo = repository(ui, dest)
187 187
188 188 else:
189 189 dest_repo = repository(ui, dest, create=True)
190 190
191 191 revs = None
192 192 if rev:
193 193 if 'lookup' not in src_repo.capabilities:
194 194 raise util.Abort(_("src repository does not support revision "
195 195 "lookup and so doesn't support clone by "
196 196 "revision"))
197 197 revs = [src_repo.lookup(r) for r in rev]
198 198
199 199 if dest_repo.local():
200 200 dest_repo.clone(src_repo, heads=revs, stream=stream)
201 201 elif src_repo.local():
202 202 src_repo.push(dest_repo, revs=revs)
203 203 else:
204 204 raise util.Abort(_("clone from remote to remote not supported"))
205 205
206 206 if src_lock:
207 207 src_lock.release()
208 208
209 209 if dest_repo.local():
210 210 fp = dest_repo.opener("hgrc", "w", text=True)
211 211 fp.write("[paths]\n")
212 212 fp.write("default = %s\n" % abspath)
213 213 fp.close()
214 214
215 215 if dest_lock:
216 216 dest_lock.release()
217 217
218 218 if update:
219 219 _update(dest_repo, dest_repo.changelog.tip())
220 220 if dir_cleanup:
221 221 dir_cleanup.close()
222 222
223 223 return src_repo, dest_repo
224 224
225 225 def _showstats(repo, stats):
226 226 stats = ((stats[0], _("updated")),
227 227 (stats[1], _("merged")),
228 228 (stats[2], _("removed")),
229 229 (stats[3], _("unresolved")))
230 230 note = ", ".join([_("%d files %s") % s for s in stats])
231 231 repo.ui.status("%s\n" % note)
232 232
233 233 def _update(repo, node): return update(repo, node)
234 234
235 235 def update(repo, node):
236 236 """update the working directory to node, merging linear changes"""
237 pl = repo.parents()
237 238 stats = _merge.update(repo, node, False, False, None, None)
238 239 _showstats(repo, stats)
239 240 if stats[3]:
240 241 repo.ui.status(_("There are unresolved merges with"
241 242 " locally modified files.\n"))
243 if stats[1]:
244 repo.ui.status(_("You can finish the partial merge using:\n"))
245 else:
246 repo.ui.status(_("You can redo the full merge using:\n"))
247 # len(pl)==1, otherwise _merge.update() would have raised util.Abort:
248 repo.ui.status(_(" hg update %s\n hg update %s\n")
249 % (pl[0].rev(), repo.changectx(node).rev()))
242 250 return stats[3]
243 251
244 252 def clean(repo, node, wlock=None, show_stats=True):
245 253 """forcibly switch the working directory to node, clobbering changes"""
246 254 stats = _merge.update(repo, node, False, True, None, wlock)
247 255 if show_stats: _showstats(repo, stats)
248 256 return stats[3]
249 257
250 258 def merge(repo, node, force=None, remind=True, wlock=None):
251 259 """branch merge with node, resolving changes"""
252 260 stats = _merge.update(repo, node, True, force, False, wlock)
253 261 _showstats(repo, stats)
254 262 if stats[3]:
255 263 pl = repo.parents()
256 264 repo.ui.status(_("There are unresolved merges,"
257 265 " you can redo the full merge using:\n"
258 266 " hg update -C %s\n"
259 267 " hg merge %s\n")
260 268 % (pl[0].rev(), pl[1].rev()))
261 269 elif remind:
262 270 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
263 271 return stats[3]
264 272
265 273 def revert(repo, node, choose, wlock):
266 274 """revert changes to revision in node without updating dirstate"""
267 275 return _merge.update(repo, node, False, True, choose, wlock)[3]
268 276
269 277 def verify(repo):
270 278 """verify the consistency of a repository"""
271 279 return _verify.verify(repo)
@@ -1,34 +1,37 b''
1 1 1:f248da0d4c3e
2 2 0:9eca13a34789
3 3 f248da0d4c3e tip
4 4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
5 5 9eca13a34789
6 6 9eca13a34789+
7 7 reverting file1
8 8 9eca13a34789
9 9 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
10 10 f248da0d4c3e tip
11 11 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 12 merge: warning: conflicts during merge
13 13 merging file1
14 14 merging file1 failed!
15 15 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
16 16 There are unresolved merges with locally modified files.
17 You can redo the full merge using:
18 hg update 0
19 hg update 1
17 20 diff -r f248da0d4c3e file1
18 21 --- a/file1
19 22 +++ b/file1
20 23 @@ -1,3 +1,7 @@ added file1
21 24 added file1
22 25 another line of text
23 26 +<<<<<<<
24 27 +changed file1 different
25 28 +=======
26 29 changed file1
27 30 +>>>>>>>
28 31 M file1
29 32 f248da0d4c3e+ tip
30 33 reverting file1
31 34 f248da0d4c3e tip
32 35 f248da0d4c3e tip
33 36 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
34 37 f248da0d4c3e tip
General Comments 0
You need to be logged in to leave comments. Login now