This repository has been archived by the owner on Dec 19, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmutable-branches.py
171 lines (146 loc) · 6.67 KB
/
mutable-branches.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
"""rename named branches
To start renaming branches, create a file called .hgbranches in the root of the working directory.
Each line in .hgbranches consists of a space-delimited pair such as oldBranch newBranch.
If the branch name contains spaces, it should be quoted.
Renamings can be kept local by putting them in .hg/.hgbranches.
.. container:: verbose
Mutable-branches merges the most recent .hgbranches from each head in the repository.
In case of conflict, the most recent head wins. .hg/.hgbranches always overrides the repository .hgbranches.
"""
import os, shlex
from mercurial import extensions, commands, changelog, localrepo, util
from mercurial.node import hex
def uisetup(ui):
extensions.wrapcommand(commands.table, 'branch', branch_wrapper)
def _parse(lines):
_deserialise(_hgbranches, lines)
def _serialise(hgbranches):
return '\n'.join('"%s" "%s"' % (old,new) for old, new in hgbranches.items())
def _deserialise(hgbranches, lines):
for line in lines:
items = shlex.split(line)
hgbranches[items[0]] = items[1]
def reposetup(ui, repo):
#only interested in local repositories
if not isinstance(repo, localrepo.localrepository): return
global _hgbranches
_hgbranches = {}
#attempt to read .hgbranches from cache
if repo.vfs.exists("cache/hgbranches"):
try:
cache = repo.vfs("cache/hgbranches").read().splitlines()
if cache[0] == hex(repo.changelog.tip()): _parse(cache[1:])
except Exception as e:
ui.debug('"%s" reading hgbranches cache\n' % e[0])
ui.warn("hgbranches cache corrupt, it will be recreated\n")
os.remove(repo.vfs.join("cache/hgbranches"))
#otherwise read .hgbranches from repo
#conflicting renames from newer heads overwrite older heads
if not _hgbranches:
for head in reversed(repo.heads()):
if '.hgbranches' in repo[head]:
try:
_parse(repo[head]['.hgbranches'].data().splitlines())
except Exception as e:
ui.warn('"%s" reading .hgbranches\n' % e[0])
raise util.Abort(".hgbranches is corrupt.\n"
"Please disable mutable-branches and fix it.")
#write .hgbranches cache
try:
with repo.vfs("cache/hgbranches", 'w') as cache:
cache.write(hex(repo.changelog.tip()) + '\n')
cache.write(_serialise(_hgbranches))
except Exception as e:
ui.debug('"%s" writing hgbranches cache\n' % e[0])
ui.warn("The hgbranches cache could not be written.\n")
#read .hg/.hgbranches
if repo.vfs.exists(".hgbranches"):
try:
_parse(repo.vfs(".hgbranches"))
except Exception as e:
ui.warn('"%s" reading local .hgbranches\n' % e[0])
raise util.Abort("local .hgbranches is corrupt")
#build a list of changes from the last run
if repo.vfs.exists("hgbranches-prev"):
changes = {}
previous = {}
try:
_deserialise(previous, repo.vfs("hgbranches-prev"))
except Exception as e:
ui.warn('"%s" reading .hg/.hgbranches-prev\n' % e[0])
raise util.Abort(".hg/hgbranches-prev is corrupt.\n"
"If you delete it, .hg/cache/branchheads* should also be deleted.\n"
"You may also need to reset the current branch.")
for old, new in _hgbranches.items():
if old in previous:
#has a previous renaming changed?
if previous[old] != new: changes[previous[old]] = new
else:
#new renaming
changes[old] = new
#check for deleted renamings
for old in previous:
if not old in _hgbranches:
changes[previous[old]] = old
else:
changes = _hgbranches
if changes:
repo.vfs.write("hgbranches-prev", _serialise(_hgbranches))
#update dirstate
dirstate = repo.dirstate
branch = dirstate.branch()
if branch in changes:
dirstate.setbranch(changes[branch])
#update branchheads
branchheads = ["branchheads"]
try:
#process the filtered branchhead caches introduced in 2.5
from mercurial import repoview
branchheads.extend("%s-%s" % (branchheads[0], filter) for filter in repoview.filtertable)
#process the new branchhead cache format introduced in 2.9
branchheads.extend("branch2-" + filter for filter in repoview.filtertable)
except ImportError:
pass
for file in ("cache/" + file for file in branchheads):
if not repo.vfs.exists(file): continue
new = []
with repo.vfs(file) as old:
new.append(old.next()) #tip tracker
for line in old:
if "branch2" in file:
rest1, rest2, branch = line.strip().split(" ", 2)
rest = rest1 + " " + rest2
else:
rest, branch = line.strip().split(" ", 1)
if branch in changes: branch = changes[branch]
new.append(rest + " " + branch + "\n")
repo.vfs.write(file, "".join(new))
#wrap changelog methods
extensions.wrapfunction(changelog.changelog, 'add', add_wrapper)
extensions.wrapfunction(changelog.changelog, 'read', read_wrapper)
def branch_wrapper(orig, ui, *args, **kwargs):
"""Remove the "branches are permanent and global" warning from branch's output."""
if len(args) > 1 and args[1] in _hgbranches:
raise util.Abort("branch " + args[1] + " has been renamed to " + _hgbranches[args[1]])
ui.pushbuffer()
ret = orig(ui, *args, **kwargs)
lines = ui.popbuffer().splitlines(True)
if lines[-1].startswith("(branches are permanent and global"): lines.pop()
ui.write(''.join(lines))
return ret
def add_wrapper(orig, ui, *args, **kwargs):
branch = args[8]['branch']
#map renamed branch to canonical name
for old, new in _hgbranches.items():
if branch == new:
args[8]['branch'] = old
break
return orig(ui, *args, **kwargs)
def read_wrapper(orig, ui, *args, **kwargs):
ret = orig(ui, *args, **kwargs)
branch = ret[5]['branch']
#rename branch
if branch in _hgbranches: ret[5]['branch'] = _hgbranches[branch]
return ret
testedwith = '2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0 3.1 3.2 3.3 3.4 3.5'
buglink = 'http://code.accursoft.com/mutable-branches/issues'