Show More
@@ -0,0 +1,105 b'' | |||||
|
1 | ||||
|
2 | $ cat >> $HGRCPATH << EOF | |||
|
3 | > [experimental] | |||
|
4 | > copies.write-to=changeset-only | |||
|
5 | > [alias] | |||
|
6 | > changesetcopies = log -r . -T 'files: {files} | |||
|
7 | > {extras % "{ifcontains("copies", key, "{key}: {value}\n")}"}' | |||
|
8 | > EOF | |||
|
9 | ||||
|
10 | Check that copies are recorded correctly | |||
|
11 | ||||
|
12 | $ hg init repo | |||
|
13 | $ cd repo | |||
|
14 | $ echo a > a | |||
|
15 | $ hg add a | |||
|
16 | $ hg ci -m initial | |||
|
17 | $ hg cp a b | |||
|
18 | $ hg cp a c | |||
|
19 | $ hg cp a d | |||
|
20 | $ hg ci -m 'copy a to b, c, and d' | |||
|
21 | $ hg changesetcopies | |||
|
22 | files: b c d | |||
|
23 | p1copies: b\x00a (esc) | |||
|
24 | c\x00a (esc) | |||
|
25 | d\x00a (esc) | |||
|
26 | ||||
|
27 | Check that renames are recorded correctly | |||
|
28 | ||||
|
29 | $ hg mv b b2 | |||
|
30 | $ hg ci -m 'rename b to b2' | |||
|
31 | $ hg changesetcopies | |||
|
32 | files: b b2 | |||
|
33 | p1copies: b2\x00b (esc) | |||
|
34 | ||||
|
35 | Rename onto existing file. This should get recorded in the changeset files list and in the extras, | |||
|
36 | even though there is no filelog entry. | |||
|
37 | ||||
|
38 | $ hg cp b2 c --force | |||
|
39 | $ hg st --copies | |||
|
40 | M c | |||
|
41 | b2 | |||
|
42 | $ hg debugindex c | |||
|
43 | rev linkrev nodeid p1 p2 | |||
|
44 | 0 1 b789fdd96dc2 000000000000 000000000000 | |||
|
45 | $ hg ci -m 'move b onto d' | |||
|
46 | $ hg changesetcopies | |||
|
47 | files: c | |||
|
48 | p1copies: c\x00b2 (esc) | |||
|
49 | $ hg debugindex c | |||
|
50 | rev linkrev nodeid p1 p2 | |||
|
51 | 0 1 b789fdd96dc2 000000000000 000000000000 | |||
|
52 | ||||
|
53 | Create a merge commit with copying done during merge. | |||
|
54 | ||||
|
55 | $ hg co 0 | |||
|
56 | 0 files updated, 0 files merged, 3 files removed, 0 files unresolved | |||
|
57 | $ hg cp a e | |||
|
58 | $ hg cp a f | |||
|
59 | $ hg ci -m 'copy a to e and f' | |||
|
60 | created new head | |||
|
61 | $ hg merge 3 | |||
|
62 | 3 files updated, 0 files merged, 0 files removed, 0 files unresolved | |||
|
63 | (branch merge, don't forget to commit) | |||
|
64 | File 'a' exists on both sides, so 'g' could be recorded as being from p1 or p2, but we currently | |||
|
65 | always record it as being from p1 | |||
|
66 | $ hg cp a g | |||
|
67 | File 'd' exists only in p2, so 'h' should be from p2 | |||
|
68 | $ hg cp d h | |||
|
69 | File 'f' exists only in p1, so 'i' should be from p1 | |||
|
70 | $ hg cp f i | |||
|
71 | $ hg ci -m 'merge' | |||
|
72 | $ hg changesetcopies | |||
|
73 | files: g h i | |||
|
74 | p1copies: g\x00a (esc) | |||
|
75 | i\x00f (esc) | |||
|
76 | p2copies: h\x00d (esc) | |||
|
77 | ||||
|
78 | Test writing to both changeset and filelog | |||
|
79 | ||||
|
80 | $ hg cp a j | |||
|
81 | $ hg ci -m 'copy a to j' --config experimental.copies.write-to=compatibility | |||
|
82 | $ hg changesetcopies | |||
|
83 | files: j | |||
|
84 | p1copies: j\x00a (esc) | |||
|
85 | $ hg debugdata j 0 | |||
|
86 | \x01 (esc) | |||
|
87 | copy: a | |||
|
88 | copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 | |||
|
89 | \x01 (esc) | |||
|
90 | a | |||
|
91 | ||||
|
92 | Test writing only to filelog | |||
|
93 | ||||
|
94 | $ hg cp a k | |||
|
95 | $ hg ci -m 'copy a to k' --config experimental.copies.write-to=filelog-only | |||
|
96 | $ hg changesetcopies | |||
|
97 | files: k | |||
|
98 | $ hg debugdata k 0 | |||
|
99 | \x01 (esc) | |||
|
100 | copy: a | |||
|
101 | copyrev: b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 | |||
|
102 | \x01 (esc) | |||
|
103 | a | |||
|
104 | ||||
|
105 | $ cd .. |
@@ -80,6 +80,13 b' def encodeextra(d):' | |||||
80 | ] |
|
80 | ] | |
81 | return "\0".join(items) |
|
81 | return "\0".join(items) | |
82 |
|
82 | |||
|
83 | def encodecopies(copies): | |||
|
84 | items = [ | |||
|
85 | '%s\0%s' % (k, copies[k]) | |||
|
86 | for k in sorted(copies) | |||
|
87 | ] | |||
|
88 | return "\n".join(items) | |||
|
89 | ||||
83 | def stripdesc(desc): |
|
90 | def stripdesc(desc): | |
84 | """strip trailing whitespace and leading and trailing empty lines""" |
|
91 | """strip trailing whitespace and leading and trailing empty lines""" | |
85 | return '\n'.join([l.rstrip() for l in desc.splitlines()]).strip('\n') |
|
92 | return '\n'.join([l.rstrip() for l in desc.splitlines()]).strip('\n') | |
@@ -533,7 +540,7 b' class changelog(revlog.revlog):' | |||||
533 | return l[3:] |
|
540 | return l[3:] | |
534 |
|
541 | |||
535 | def add(self, manifest, files, desc, transaction, p1, p2, |
|
542 | def add(self, manifest, files, desc, transaction, p1, p2, | |
536 | user, date=None, extra=None): |
|
543 | user, date=None, extra=None, p1copies=None, p2copies=None): | |
537 | # Convert to UTF-8 encoded bytestrings as the very first |
|
544 | # Convert to UTF-8 encoded bytestrings as the very first | |
538 | # thing: calling any method on a localstr object will turn it |
|
545 | # thing: calling any method on a localstr object will turn it | |
539 | # into a str object and the cached UTF-8 string is thus lost. |
|
546 | # into a str object and the cached UTF-8 string is thus lost. | |
@@ -562,6 +569,13 b' class changelog(revlog.revlog):' | |||||
562 | elif branch in (".", "null", "tip"): |
|
569 | elif branch in (".", "null", "tip"): | |
563 | raise error.StorageError(_('the name \'%s\' is reserved') |
|
570 | raise error.StorageError(_('the name \'%s\' is reserved') | |
564 | % branch) |
|
571 | % branch) | |
|
572 | if (p1copies or p2copies) and extra is None: | |||
|
573 | extra = {} | |||
|
574 | if p1copies: | |||
|
575 | extra['p1copies'] = encodecopies(p1copies) | |||
|
576 | if p2copies: | |||
|
577 | extra['p2copies'] = encodecopies(p2copies) | |||
|
578 | ||||
565 | if extra: |
|
579 | if extra: | |
566 | extra = encodeextra(extra) |
|
580 | extra = encodeextra(extra) | |
567 | parseddate = "%s %s" % (parseddate, extra) |
|
581 | parseddate = "%s %s" % (parseddate, extra) |
@@ -488,6 +488,9 b" coreconfigitem('experimental', 'copytrac" | |||||
488 | coreconfigitem('experimental', 'copies.read-from', |
|
488 | coreconfigitem('experimental', 'copies.read-from', | |
489 | default="filelog-only", |
|
489 | default="filelog-only", | |
490 | ) |
|
490 | ) | |
|
491 | coreconfigitem('experimental', 'copies.write-to', | |||
|
492 | default='filelog-only', | |||
|
493 | ) | |||
491 | coreconfigitem('experimental', 'crecordtest', |
|
494 | coreconfigitem('experimental', 'crecordtest', | |
492 | default=None, |
|
495 | default=None, | |
493 | ) |
|
496 | ) |
@@ -2324,7 +2324,8 b' class localrepository(object):' | |||||
2324 | """Returns the wlock if it's held, or None if it's not.""" |
|
2324 | """Returns the wlock if it's held, or None if it's not.""" | |
2325 | return self._currentlock(self._wlockref) |
|
2325 | return self._currentlock(self._wlockref) | |
2326 |
|
2326 | |||
2327 |
def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist |
|
2327 | def _filecommit(self, fctx, manifest1, manifest2, linkrev, tr, changelist, | |
|
2328 | includecopymeta): | |||
2328 | """ |
|
2329 | """ | |
2329 | commit an individual file as part of a larger transaction |
|
2330 | commit an individual file as part of a larger transaction | |
2330 | """ |
|
2331 | """ | |
@@ -2383,8 +2384,9 b' class localrepository(object):' | |||||
2383 |
|
2384 | |||
2384 | if cnode: |
|
2385 | if cnode: | |
2385 | self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(cnode))) |
|
2386 | self.ui.debug(" %s: copy %s:%s\n" % (fname, cfname, hex(cnode))) | |
2386 | meta["copy"] = cfname |
|
2387 | if includecopymeta: | |
2387 |
meta["copy |
|
2388 | meta["copy"] = cfname | |
|
2389 | meta["copyrev"] = hex(cnode) | |||
2388 | fparent1, fparent2 = nullid, newfparent |
|
2390 | fparent1, fparent2 = nullid, newfparent | |
2389 | else: |
|
2391 | else: | |
2390 | self.ui.warn(_("warning: can't find ancestor for '%s' " |
|
2392 | self.ui.warn(_("warning: can't find ancestor for '%s' " | |
@@ -2552,6 +2554,12 b' class localrepository(object):' | |||||
2552 | p1, p2 = ctx.p1(), ctx.p2() |
|
2554 | p1, p2 = ctx.p1(), ctx.p2() | |
2553 | user = ctx.user() |
|
2555 | user = ctx.user() | |
2554 |
|
2556 | |||
|
2557 | writecopiesto = self.ui.config('experimental', 'copies.write-to') | |||
|
2558 | writefilecopymeta = writecopiesto != 'changeset-only' | |||
|
2559 | p1copies, p2copies = None, None | |||
|
2560 | if writecopiesto in ('changeset-only', 'compatibility'): | |||
|
2561 | p1copies = ctx.p1copies() | |||
|
2562 | p2copies = ctx.p2copies() | |||
2555 | with self.lock(), self.transaction("commit") as tr: |
|
2563 | with self.lock(), self.transaction("commit") as tr: | |
2556 | trp = weakref.proxy(tr) |
|
2564 | trp = weakref.proxy(tr) | |
2557 |
|
2565 | |||
@@ -2585,7 +2593,8 b' class localrepository(object):' | |||||
2585 | else: |
|
2593 | else: | |
2586 | added.append(f) |
|
2594 | added.append(f) | |
2587 | m[f] = self._filecommit(fctx, m1, m2, linkrev, |
|
2595 | m[f] = self._filecommit(fctx, m1, m2, linkrev, | |
2588 |
trp, changed |
|
2596 | trp, changed, | |
|
2597 | writefilecopymeta) | |||
2589 | m.setflag(f, fctx.flags()) |
|
2598 | m.setflag(f, fctx.flags()) | |
2590 | except OSError: |
|
2599 | except OSError: | |
2591 | self.ui.warn(_("trouble committing %s!\n") % |
|
2600 | self.ui.warn(_("trouble committing %s!\n") % | |
@@ -2639,7 +2648,8 b' class localrepository(object):' | |||||
2639 | self.changelog.delayupdate(tr) |
|
2648 | self.changelog.delayupdate(tr) | |
2640 | n = self.changelog.add(mn, files, ctx.description(), |
|
2649 | n = self.changelog.add(mn, files, ctx.description(), | |
2641 | trp, p1.node(), p2.node(), |
|
2650 | trp, p1.node(), p2.node(), | |
2642 |
user, ctx.date(), ctx.extra().copy() |
|
2651 | user, ctx.date(), ctx.extra().copy(), | |
|
2652 | p1copies, p2copies) | |||
2643 | xp1, xp2 = p1.hex(), p2 and p2.hex() or '' |
|
2653 | xp1, xp2 = p1.hex(), p2 and p2.hex() or '' | |
2644 | self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1, |
|
2654 | self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1, | |
2645 | parent2=xp2) |
|
2655 | parent2=xp2) |
@@ -438,7 +438,7 b' and its ancestor by overriding "repo._fi' | |||||
438 | > def reposetup(ui, repo): |
|
438 | > def reposetup(ui, repo): | |
439 | > class legacyrepo(repo.__class__): |
|
439 | > class legacyrepo(repo.__class__): | |
440 | > def _filecommit(self, fctx, manifest1, manifest2, |
|
440 | > def _filecommit(self, fctx, manifest1, manifest2, | |
441 | > linkrev, tr, changelist): |
|
441 | > linkrev, tr, changelist, includecopymeta): | |
442 | > fname = fctx.path() |
|
442 | > fname = fctx.path() | |
443 | > text = fctx.data() |
|
443 | > text = fctx.data() | |
444 | > flog = self.file(fname) |
|
444 | > flog = self.file(fname) |
@@ -443,7 +443,7 b' and its ancestor by overriding "repo._fi' | |||||
443 | > def reposetup(ui, repo): |
|
443 | > def reposetup(ui, repo): | |
444 | > class legacyrepo(repo.__class__): |
|
444 | > class legacyrepo(repo.__class__): | |
445 | > def _filecommit(self, fctx, manifest1, manifest2, |
|
445 | > def _filecommit(self, fctx, manifest1, manifest2, | |
446 | > linkrev, tr, changelist): |
|
446 | > linkrev, tr, changelist, includecopymeta): | |
447 | > fname = fctx.path() |
|
447 | > fname = fctx.path() | |
448 | > text = fctx.data() |
|
448 | > text = fctx.data() | |
449 | > flog = self.file(fname) |
|
449 | > flog = self.file(fname) |
General Comments 0
You need to be logged in to leave comments.
Login now