##// END OF EJS Templates
bookmark: prevent crashing when a successor is unknown locally (issue3680)...
Pierre-Yves David -
r17865:daf32ebf stable
parent child Browse files
Show More
@@ -1,275 +1,276 b''
1 1 # Mercurial bookmark support code
2 2 #
3 3 # Copyright 2008 David Soria Parra <dsp@php.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from mercurial.i18n import _
9 9 from mercurial.node import hex
10 10 from mercurial import encoding, error, util, obsolete, phases
11 11 import errno, os
12 12
13 13 def read(repo):
14 14 '''Parse .hg/bookmarks file and return a dictionary
15 15
16 16 Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
17 17 in the .hg/bookmarks file.
18 18 Read the file and return a (name=>nodeid) dictionary
19 19 '''
20 20 bookmarks = {}
21 21 try:
22 22 for line in repo.opener('bookmarks'):
23 23 line = line.strip()
24 24 if not line:
25 25 continue
26 26 if ' ' not in line:
27 27 repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
28 28 continue
29 29 sha, refspec = line.split(' ', 1)
30 30 refspec = encoding.tolocal(refspec)
31 31 try:
32 32 bookmarks[refspec] = repo.changelog.lookup(sha)
33 33 except LookupError:
34 34 pass
35 35 except IOError, inst:
36 36 if inst.errno != errno.ENOENT:
37 37 raise
38 38 return bookmarks
39 39
40 40 def readcurrent(repo):
41 41 '''Get the current bookmark
42 42
43 43 If we use gittishsh branches we have a current bookmark that
44 44 we are on. This function returns the name of the bookmark. It
45 45 is stored in .hg/bookmarks.current
46 46 '''
47 47 mark = None
48 48 try:
49 49 file = repo.opener('bookmarks.current')
50 50 except IOError, inst:
51 51 if inst.errno != errno.ENOENT:
52 52 raise
53 53 return None
54 54 try:
55 55 # No readline() in osutil.posixfile, reading everything is cheap
56 56 mark = encoding.tolocal((file.readlines() or [''])[0])
57 57 if mark == '' or mark not in repo._bookmarks:
58 58 mark = None
59 59 finally:
60 60 file.close()
61 61 return mark
62 62
63 63 def write(repo):
64 64 '''Write bookmarks
65 65
66 66 Write the given bookmark => hash dictionary to the .hg/bookmarks file
67 67 in a format equal to those of localtags.
68 68
69 69 We also store a backup of the previous state in undo.bookmarks that
70 70 can be copied back on rollback.
71 71 '''
72 72 refs = repo._bookmarks
73 73
74 74 if repo._bookmarkcurrent not in refs:
75 75 setcurrent(repo, None)
76 76
77 77 wlock = repo.wlock()
78 78 try:
79 79
80 80 file = repo.opener('bookmarks', 'w', atomictemp=True)
81 81 for refspec, node in refs.iteritems():
82 82 file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
83 83 file.close()
84 84
85 85 # touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
86 86 try:
87 87 os.utime(repo.sjoin('00changelog.i'), None)
88 88 except OSError:
89 89 pass
90 90
91 91 finally:
92 92 wlock.release()
93 93
94 94 def setcurrent(repo, mark):
95 95 '''Set the name of the bookmark that we are currently on
96 96
97 97 Set the name of the bookmark that we are on (hg update <bookmark>).
98 98 The name is recorded in .hg/bookmarks.current
99 99 '''
100 100 current = repo._bookmarkcurrent
101 101 if current == mark:
102 102 return
103 103
104 104 if mark not in repo._bookmarks:
105 105 mark = ''
106 106
107 107 wlock = repo.wlock()
108 108 try:
109 109 file = repo.opener('bookmarks.current', 'w', atomictemp=True)
110 110 file.write(encoding.fromlocal(mark))
111 111 file.close()
112 112 finally:
113 113 wlock.release()
114 114 repo._bookmarkcurrent = mark
115 115
116 116 def unsetcurrent(repo):
117 117 wlock = repo.wlock()
118 118 try:
119 119 try:
120 120 util.unlink(repo.join('bookmarks.current'))
121 121 repo._bookmarkcurrent = None
122 122 except OSError, inst:
123 123 if inst.errno != errno.ENOENT:
124 124 raise
125 125 finally:
126 126 wlock.release()
127 127
128 128 def updatecurrentbookmark(repo, oldnode, curbranch):
129 129 try:
130 130 return update(repo, oldnode, repo.branchtip(curbranch))
131 131 except error.RepoLookupError:
132 132 if curbranch == "default": # no default branch!
133 133 return update(repo, oldnode, repo.lookup("tip"))
134 134 else:
135 135 raise util.Abort(_("branch %s not found") % curbranch)
136 136
137 137 def update(repo, parents, node):
138 138 marks = repo._bookmarks
139 139 update = False
140 140 cur = repo._bookmarkcurrent
141 141 if not cur:
142 142 return False
143 143
144 144 toupdate = [b for b in marks if b.split('@', 1)[0] == cur.split('@', 1)[0]]
145 145 for mark in toupdate:
146 146 if mark and marks[mark] in parents:
147 147 old = repo[marks[mark]]
148 148 new = repo[node]
149 149 if old.descendant(new) and mark == cur:
150 150 marks[cur] = new.node()
151 151 update = True
152 152 if mark != cur:
153 153 del marks[mark]
154 154 if update:
155 155 repo._writebookmarks(marks)
156 156 return update
157 157
158 158 def listbookmarks(repo):
159 159 # We may try to list bookmarks on a repo type that does not
160 160 # support it (e.g., statichttprepository).
161 161 marks = getattr(repo, '_bookmarks', {})
162 162
163 163 d = {}
164 164 for k, v in marks.iteritems():
165 165 # don't expose local divergent bookmarks
166 166 if '@' not in k or k.endswith('@'):
167 167 d[k] = hex(v)
168 168 return d
169 169
170 170 def pushbookmark(repo, key, old, new):
171 171 w = repo.wlock()
172 172 try:
173 173 marks = repo._bookmarks
174 174 if hex(marks.get(key, '')) != old:
175 175 return False
176 176 if new == '':
177 177 del marks[key]
178 178 else:
179 179 if new not in repo:
180 180 return False
181 181 marks[key] = repo[new].node()
182 182 write(repo)
183 183 return True
184 184 finally:
185 185 w.release()
186 186
187 187 def updatefromremote(ui, repo, remote, path):
188 188 ui.debug("checking for updated bookmarks\n")
189 189 rb = remote.listkeys('bookmarks')
190 190 changed = False
191 191 for k in rb.keys():
192 192 if k in repo._bookmarks:
193 193 nr, nl = rb[k], repo._bookmarks[k]
194 194 if nr in repo:
195 195 cr = repo[nr]
196 196 cl = repo[nl]
197 197 if cl.rev() >= cr.rev():
198 198 continue
199 199 if validdest(repo, cl, cr):
200 200 repo._bookmarks[k] = cr.node()
201 201 changed = True
202 202 ui.status(_("updating bookmark %s\n") % k)
203 203 else:
204 204 if k == '@':
205 205 kd = ''
206 206 else:
207 207 kd = k
208 208 # find a unique @ suffix
209 209 for x in range(1, 100):
210 210 n = '%s@%d' % (kd, x)
211 211 if n not in repo._bookmarks:
212 212 break
213 213 # try to use an @pathalias suffix
214 214 # if an @pathalias already exists, we overwrite (update) it
215 215 for p, u in ui.configitems("paths"):
216 216 if path == u:
217 217 n = '%s@%s' % (kd, p)
218 218
219 219 repo._bookmarks[n] = cr.node()
220 220 changed = True
221 221 ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
222 222 elif rb[k] in repo:
223 223 # add remote bookmarks for changes we already have
224 224 repo._bookmarks[k] = repo[rb[k]].node()
225 225 changed = True
226 226 ui.status(_("adding remote bookmark %s\n") % k)
227 227
228 228 if changed:
229 229 write(repo)
230 230
231 231 def diff(ui, dst, src):
232 232 ui.status(_("searching for changed bookmarks\n"))
233 233
234 234 smarks = src.listkeys('bookmarks')
235 235 dmarks = dst.listkeys('bookmarks')
236 236
237 237 diff = sorted(set(smarks) - set(dmarks))
238 238 for k in diff:
239 239 mark = ui.debugflag and smarks[k] or smarks[k][:12]
240 240 ui.write(" %-25s %s\n" % (k, mark))
241 241
242 242 if len(diff) <= 0:
243 243 ui.status(_("no changed bookmarks found\n"))
244 244 return 1
245 245 return 0
246 246
247 247 def validdest(repo, old, new):
248 248 """Is the new bookmark destination a valid update from the old one"""
249 249 if old == new:
250 250 # Old == new -> nothing to update.
251 251 return False
252 252 elif not old:
253 253 # old is nullrev, anything is valid.
254 254 # (new != nullrev has been excluded by the previous check)
255 255 return True
256 256 elif repo.obsstore:
257 257 # We only need this complicated logic if there is obsolescence
258 258 # XXX will probably deserve an optimised revset.
259
259 nm = repo.changelog.nodemap
260 260 validdests = set([old])
261 261 plen = -1
262 262 # compute the whole set of successors or descendants
263 263 while len(validdests) != plen:
264 264 plen = len(validdests)
265 265 succs = set(c.node() for c in validdests)
266 266 for c in validdests:
267 267 if c.phase() > phases.public:
268 268 # obsolescence marker does not apply to public changeset
269 269 succs.update(obsolete.allsuccessors(repo.obsstore,
270 270 [c.node()]))
271 validdests = set(repo.set('%ln::', succs))
271 known = (n for n in succs if nm.get(n) is not None)
272 validdests = set(repo.set('%ln::', known))
272 273 validdests.remove(old)
273 274 return new in validdests
274 275 else:
275 276 return old.descendant(new)
@@ -1,381 +1,382 b''
1 1 $ "$TESTDIR/hghave" serve || exit 80
2 2
3 3 $ cat << EOF >> $HGRCPATH
4 4 > [ui]
5 5 > logtemplate={rev}:{node|short} {desc|firstline}
6 6 > [phases]
7 7 > publish=False
8 8 > [extensions]
9 9 > EOF
10 10 $ cat > obs.py << EOF
11 11 > import mercurial.obsolete
12 12 > mercurial.obsolete._enabled = True
13 13 > EOF
14 14 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
15 15
16 16 initialize
17 17
18 18 $ hg init a
19 19 $ cd a
20 20 $ echo 'test' > test
21 21 $ hg commit -Am'test'
22 22 adding test
23 23
24 24 set bookmarks
25 25
26 26 $ hg bookmark X
27 27 $ hg bookmark Y
28 28 $ hg bookmark Z
29 29
30 30 import bookmark by name
31 31
32 32 $ hg init ../b
33 33 $ cd ../b
34 34 $ hg book Y
35 35 $ hg book
36 36 * Y -1:000000000000
37 37 $ hg pull ../a
38 38 pulling from ../a
39 39 requesting all changes
40 40 adding changesets
41 41 adding manifests
42 42 adding file changes
43 43 added 1 changesets with 1 changes to 1 files
44 44 updating bookmark Y
45 45 adding remote bookmark X
46 46 adding remote bookmark Z
47 47 (run 'hg update' to get a working copy)
48 48 $ hg bookmarks
49 49 X 0:4e3505fd9583
50 50 Y 0:4e3505fd9583
51 51 Z 0:4e3505fd9583
52 52 $ hg debugpushkey ../a namespaces
53 53 bookmarks
54 54 phases
55 55 namespaces
56 56 obsolete
57 57 $ hg debugpushkey ../a bookmarks
58 58 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
59 59 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
60 60 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
61 61 $ hg pull -B X ../a
62 62 pulling from ../a
63 63 no changes found
64 64 importing bookmark X
65 65 $ hg bookmark
66 66 X 0:4e3505fd9583
67 67 Y 0:4e3505fd9583
68 68 Z 0:4e3505fd9583
69 69
70 70 export bookmark by name
71 71
72 72 $ hg bookmark W
73 73 $ hg bookmark foo
74 74 $ hg bookmark foobar
75 75 $ hg push -B W ../a
76 76 pushing to ../a
77 77 searching for changes
78 78 no changes found
79 79 exporting bookmark W
80 80 [1]
81 81 $ hg -R ../a bookmarks
82 82 W -1:000000000000
83 83 X 0:4e3505fd9583
84 84 Y 0:4e3505fd9583
85 85 * Z 0:4e3505fd9583
86 86
87 87 delete a remote bookmark
88 88
89 89 $ hg book -d W
90 90 $ hg push -B W ../a
91 91 pushing to ../a
92 92 searching for changes
93 93 no changes found
94 94 deleting remote bookmark W
95 95 [1]
96 96
97 97 push/pull name that doesn't exist
98 98
99 99 $ hg push -B badname ../a
100 100 pushing to ../a
101 101 searching for changes
102 102 no changes found
103 103 bookmark badname does not exist on the local or remote repository!
104 104 [2]
105 105 $ hg pull -B anotherbadname ../a
106 106 pulling from ../a
107 107 abort: remote bookmark anotherbadname not found!
108 108 [255]
109 109
110 110 divergent bookmarks
111 111
112 112 $ cd ../a
113 113 $ echo c1 > f1
114 114 $ hg ci -Am1
115 115 adding f1
116 116 $ hg book -f @
117 117 $ hg book -f X
118 118 $ hg book
119 119 @ 1:0d2164f0ce0d
120 120 * X 1:0d2164f0ce0d
121 121 Y 0:4e3505fd9583
122 122 Z 1:0d2164f0ce0d
123 123
124 124 $ cd ../b
125 125 $ hg up
126 126 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
127 127 updating bookmark foobar
128 128 $ echo c2 > f2
129 129 $ hg ci -Am2
130 130 adding f2
131 131 $ hg book -f @
132 132 $ hg book -f X
133 133 $ hg book
134 134 @ 1:9b140be10808
135 135 * X 1:9b140be10808
136 136 Y 0:4e3505fd9583
137 137 Z 0:4e3505fd9583
138 138 foo -1:000000000000
139 139 foobar 1:9b140be10808
140 140
141 141 $ hg pull --config paths.foo=../a foo
142 142 pulling from $TESTTMP/a (glob)
143 143 searching for changes
144 144 adding changesets
145 145 adding manifests
146 146 adding file changes
147 147 added 1 changesets with 1 changes to 1 files (+1 heads)
148 148 divergent bookmark X stored as X@foo
149 149 updating bookmark Z
150 150 divergent bookmark @ stored as @foo
151 151 (run 'hg heads' to see heads, 'hg merge' to merge)
152 152 $ hg book
153 153 @ 1:9b140be10808
154 154 @foo 2:0d2164f0ce0d
155 155 * X 1:9b140be10808
156 156 X@foo 2:0d2164f0ce0d
157 157 Y 0:4e3505fd9583
158 158 Z 2:0d2164f0ce0d
159 159 foo -1:000000000000
160 160 foobar 1:9b140be10808
161 161 $ hg push -f ../a
162 162 pushing to ../a
163 163 searching for changes
164 164 adding changesets
165 165 adding manifests
166 166 adding file changes
167 167 added 1 changesets with 1 changes to 1 files (+1 heads)
168 168 $ hg -R ../a book
169 169 @ 1:0d2164f0ce0d
170 170 * X 1:0d2164f0ce0d
171 171 Y 0:4e3505fd9583
172 172 Z 1:0d2164f0ce0d
173 173
174 174 update a remote bookmark from a non-head to a head
175 175
176 176 $ hg up -q Y
177 177 $ echo c3 > f2
178 178 $ hg ci -Am3
179 179 adding f2
180 180 created new head
181 181 $ hg push ../a
182 182 pushing to ../a
183 183 searching for changes
184 184 adding changesets
185 185 adding manifests
186 186 adding file changes
187 187 added 1 changesets with 1 changes to 1 files (+1 heads)
188 188 updating bookmark Y
189 189 $ hg -R ../a book
190 190 @ 1:0d2164f0ce0d
191 191 * X 1:0d2164f0ce0d
192 192 Y 3:f6fc62dde3c0
193 193 Z 1:0d2164f0ce0d
194 194
195 195 diverging a remote bookmark fails
196 196
197 197 $ hg up -q 4e3505fd9583
198 198 $ echo c4 > f2
199 199 $ hg ci -Am4
200 200 adding f2
201 201 created new head
202 202 $ echo c5 > f2
203 203 $ hg ci -Am5
204 204 $ hg log -G
205 205 @ 5:c922c0139ca0 5
206 206 |
207 207 o 4:4efff6d98829 4
208 208 |
209 209 | o 3:f6fc62dde3c0 3
210 210 |/
211 211 | o 2:0d2164f0ce0d 1
212 212 |/
213 213 | o 1:9b140be10808 2
214 214 |/
215 215 o 0:4e3505fd9583 test
216 216
217 217
218 218 $ hg book -f Y
219 219
220 220 $ cat <<EOF > ../a/.hg/hgrc
221 221 > [web]
222 222 > push_ssl = false
223 223 > allow_push = *
224 224 > EOF
225 225
226 226 $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid
227 227 $ cat ../hg2.pid >> $DAEMON_PIDS
228 228
229 229 $ hg push http://localhost:$HGPORT2/
230 230 pushing to http://localhost:$HGPORT2/
231 231 searching for changes
232 232 abort: push creates new remote head c922c0139ca0!
233 233 (did you forget to merge? use push -f to force)
234 234 [255]
235 235 $ hg -R ../a book
236 236 @ 1:0d2164f0ce0d
237 237 * X 1:0d2164f0ce0d
238 238 Y 3:f6fc62dde3c0
239 239 Z 1:0d2164f0ce0d
240 240
241 241
242 242 Unrelated marker does not alter the decision
243 243
244 244 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
245 245 $ hg push http://localhost:$HGPORT2/
246 246 pushing to http://localhost:$HGPORT2/
247 247 searching for changes
248 248 abort: push creates new remote head c922c0139ca0!
249 249 (did you forget to merge? use push -f to force)
250 250 [255]
251 251 $ hg -R ../a book
252 252 @ 1:0d2164f0ce0d
253 253 * X 1:0d2164f0ce0d
254 254 Y 3:f6fc62dde3c0
255 255 Z 1:0d2164f0ce0d
256 256
257 257 Update to a successor works
258 258
259 259 $ hg id --debug -r 3
260 260 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
261 261 $ hg id --debug -r 4
262 262 4efff6d98829d9c824c621afd6e3f01865f5439f
263 263 $ hg id --debug -r 5
264 264 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
265 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f 4efff6d98829d9c824c621afd6e3f01865f5439f
265 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
266 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
266 267 $ hg push http://localhost:$HGPORT2/
267 268 pushing to http://localhost:$HGPORT2/
268 269 searching for changes
269 270 remote: adding changesets
270 271 remote: adding manifests
271 272 remote: adding file changes
272 273 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
273 274 updating bookmark Y
274 275 $ hg -R ../a book
275 276 @ 1:0d2164f0ce0d
276 277 * X 1:0d2164f0ce0d
277 278 Y 5:c922c0139ca0
278 279 Z 1:0d2164f0ce0d
279 280
280 281 hgweb
281 282
282 283 $ cat <<EOF > .hg/hgrc
283 284 > [web]
284 285 > push_ssl = false
285 286 > allow_push = *
286 287 > EOF
287 288
288 289 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
289 290 $ cat ../hg.pid >> $DAEMON_PIDS
290 291 $ cd ../a
291 292
292 293 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
293 294 bookmarks
294 295 phases
295 296 namespaces
296 297 obsolete
297 298 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
298 299 @ 9b140be1080824d768c5a4691a564088eede71f9
299 300 foo 0000000000000000000000000000000000000000
300 301 foobar 9b140be1080824d768c5a4691a564088eede71f9
301 302 Y c922c0139ca03858f655e4a2af4dd02796a63969
302 303 X 9b140be1080824d768c5a4691a564088eede71f9
303 304 Z 0d2164f0ce0d8f1d6f94351eba04b794909be66c
304 305 $ hg out -B http://localhost:$HGPORT/
305 306 comparing with http://localhost:$HGPORT/
306 307 searching for changed bookmarks
307 308 no changed bookmarks found
308 309 [1]
309 310 $ hg push -B Z http://localhost:$HGPORT/
310 311 pushing to http://localhost:$HGPORT/
311 312 searching for changes
312 313 no changes found
313 314 exporting bookmark Z
314 315 [1]
315 316 $ hg book -d Z
316 317 $ hg in -B http://localhost:$HGPORT/
317 318 comparing with http://localhost:$HGPORT/
318 319 searching for changed bookmarks
319 320 Z 0d2164f0ce0d
320 321 foo 000000000000
321 322 foobar 9b140be10808
322 323 $ hg pull -B Z http://localhost:$HGPORT/
323 324 pulling from http://localhost:$HGPORT/
324 325 no changes found
325 326 divergent bookmark @ stored as @1
326 327 adding remote bookmark foo
327 328 adding remote bookmark foobar
328 329 divergent bookmark X stored as X@1
329 330 adding remote bookmark Z
330 331 importing bookmark Z
331 332 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
332 333 requesting all changes
333 334 adding changesets
334 335 adding manifests
335 336 adding file changes
336 337 added 5 changesets with 5 changes to 3 files (+2 heads)
337 338 updating to branch default
338 339 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
339 340 $ hg -R cloned-bookmarks bookmarks
340 341 @ 1:9b140be10808
341 342 X 1:9b140be10808
342 343 Y 4:c922c0139ca0
343 344 Z 2:0d2164f0ce0d
344 345 foo -1:000000000000
345 346 foobar 1:9b140be10808
346 347
347 348 $ cd ..
348 349
349 350 Pushing a bookmark should only push the changes required by that
350 351 bookmark, not all outgoing changes:
351 352 $ hg clone http://localhost:$HGPORT/ addmarks
352 353 requesting all changes
353 354 adding changesets
354 355 adding manifests
355 356 adding file changes
356 357 added 5 changesets with 5 changes to 3 files (+2 heads)
357 358 updating to branch default
358 359 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
359 360 $ cd addmarks
360 361 $ echo foo > foo
361 362 $ hg add foo
362 363 $ hg commit -m 'add foo'
363 364 $ echo bar > bar
364 365 $ hg add bar
365 366 $ hg commit -m 'add bar'
366 367 $ hg co "tip^"
367 368 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
368 369 $ hg book add-foo
369 370 $ hg book -r tip add-bar
370 371 Note: this push *must* push only a single changeset, as that's the point
371 372 of this test.
372 373 $ hg push -B add-foo --traceback
373 374 pushing to http://localhost:$HGPORT/
374 375 searching for changes
375 376 remote: adding changesets
376 377 remote: adding manifests
377 378 remote: adding file changes
378 379 remote: added 1 changesets with 1 changes to 1 files
379 380 exporting bookmark add-foo
380 381
381 382 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now