Show More
@@ -0,0 +1,231 b'' | |||||
|
1 | # exchange.py - utily to exchange data between repo. | |||
|
2 | # | |||
|
3 | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | |||
|
4 | # | |||
|
5 | # This software may be used and distributed according to the terms of the | |||
|
6 | # GNU General Public License version 2 or any later version. | |||
|
7 | ||||
|
8 | from i18n import _ | |||
|
9 | from node import hex | |||
|
10 | import errno | |||
|
11 | import util, scmutil, changegroup | |||
|
12 | import discovery, phases, obsolete, bookmarks | |||
|
13 | ||||
|
14 | def push(repo, remote, force=False, revs=None, newbranch=False): | |||
|
15 | '''Push outgoing changesets (limited by revs) from a local | |||
|
16 | repository to remote. Return an integer: | |||
|
17 | - None means nothing to push | |||
|
18 | - 0 means HTTP error | |||
|
19 | - 1 means we pushed and remote head count is unchanged *or* | |||
|
20 | we have outgoing changesets but refused to push | |||
|
21 | - other values as described by addchangegroup() | |||
|
22 | ''' | |||
|
23 | if remote.local(): | |||
|
24 | missing = set(repo.requirements) - remote.local().supported | |||
|
25 | if missing: | |||
|
26 | msg = _("required features are not" | |||
|
27 | " supported in the destination:" | |||
|
28 | " %s") % (', '.join(sorted(missing))) | |||
|
29 | raise util.Abort(msg) | |||
|
30 | ||||
|
31 | # there are two ways to push to remote repo: | |||
|
32 | # | |||
|
33 | # addchangegroup assumes local user can lock remote | |||
|
34 | # repo (local filesystem, old ssh servers). | |||
|
35 | # | |||
|
36 | # unbundle assumes local user cannot lock remote repo (new ssh | |||
|
37 | # servers, http servers). | |||
|
38 | ||||
|
39 | if not remote.canpush(): | |||
|
40 | raise util.Abort(_("destination does not support push")) | |||
|
41 | unfi = repo.unfiltered() | |||
|
42 | def localphasemove(nodes, phase=phases.public): | |||
|
43 | """move <nodes> to <phase> in the local source repo""" | |||
|
44 | if locallock is not None: | |||
|
45 | phases.advanceboundary(repo, phase, nodes) | |||
|
46 | else: | |||
|
47 | # repo is not locked, do not change any phases! | |||
|
48 | # Informs the user that phases should have been moved when | |||
|
49 | # applicable. | |||
|
50 | actualmoves = [n for n in nodes if phase < repo[n].phase()] | |||
|
51 | phasestr = phases.phasenames[phase] | |||
|
52 | if actualmoves: | |||
|
53 | repo.ui.status(_('cannot lock source repo, skipping local' | |||
|
54 | ' %s phase update\n') % phasestr) | |||
|
55 | # get local lock as we might write phase data | |||
|
56 | locallock = None | |||
|
57 | try: | |||
|
58 | locallock = repo.lock() | |||
|
59 | except IOError, err: | |||
|
60 | if err.errno != errno.EACCES: | |||
|
61 | raise | |||
|
62 | # source repo cannot be locked. | |||
|
63 | # We do not abort the push, but just disable the local phase | |||
|
64 | # synchronisation. | |||
|
65 | msg = 'cannot lock source repository: %s\n' % err | |||
|
66 | repo.ui.debug(msg) | |||
|
67 | try: | |||
|
68 | repo.checkpush(force, revs) | |||
|
69 | lock = None | |||
|
70 | unbundle = remote.capable('unbundle') | |||
|
71 | if not unbundle: | |||
|
72 | lock = remote.lock() | |||
|
73 | try: | |||
|
74 | # discovery | |||
|
75 | fci = discovery.findcommonincoming | |||
|
76 | commoninc = fci(unfi, remote, force=force) | |||
|
77 | common, inc, remoteheads = commoninc | |||
|
78 | fco = discovery.findcommonoutgoing | |||
|
79 | outgoing = fco(unfi, remote, onlyheads=revs, | |||
|
80 | commoninc=commoninc, force=force) | |||
|
81 | ||||
|
82 | ||||
|
83 | if not outgoing.missing: | |||
|
84 | # nothing to push | |||
|
85 | scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded) | |||
|
86 | ret = None | |||
|
87 | else: | |||
|
88 | # something to push | |||
|
89 | if not force: | |||
|
90 | # if repo.obsstore == False --> no obsolete | |||
|
91 | # then, save the iteration | |||
|
92 | if unfi.obsstore: | |||
|
93 | # this message are here for 80 char limit reason | |||
|
94 | mso = _("push includes obsolete changeset: %s!") | |||
|
95 | mst = "push includes %s changeset: %s!" | |||
|
96 | # plain versions for i18n tool to detect them | |||
|
97 | _("push includes unstable changeset: %s!") | |||
|
98 | _("push includes bumped changeset: %s!") | |||
|
99 | _("push includes divergent changeset: %s!") | |||
|
100 | # If we are to push if there is at least one | |||
|
101 | # obsolete or unstable changeset in missing, at | |||
|
102 | # least one of the missinghead will be obsolete or | |||
|
103 | # unstable. So checking heads only is ok | |||
|
104 | for node in outgoing.missingheads: | |||
|
105 | ctx = unfi[node] | |||
|
106 | if ctx.obsolete(): | |||
|
107 | raise util.Abort(mso % ctx) | |||
|
108 | elif ctx.troubled(): | |||
|
109 | raise util.Abort(_(mst) | |||
|
110 | % (ctx.troubles()[0], | |||
|
111 | ctx)) | |||
|
112 | newbm = repo.ui.configlist('bookmarks', 'pushing') | |||
|
113 | discovery.checkheads(unfi, remote, outgoing, | |||
|
114 | remoteheads, newbranch, | |||
|
115 | bool(inc), newbm) | |||
|
116 | ||||
|
117 | # TODO: get bundlecaps from remote | |||
|
118 | bundlecaps = None | |||
|
119 | # create a changegroup from local | |||
|
120 | if revs is None and not (outgoing.excluded | |||
|
121 | or repo.changelog.filteredrevs): | |||
|
122 | # push everything, | |||
|
123 | # use the fast path, no race possible on push | |||
|
124 | bundler = changegroup.bundle10(repo, bundlecaps) | |||
|
125 | cg = repo._changegroupsubset(outgoing, | |||
|
126 | bundler, | |||
|
127 | 'push', | |||
|
128 | fastpath=True) | |||
|
129 | else: | |||
|
130 | cg = repo.getlocalbundle('push', outgoing, bundlecaps) | |||
|
131 | ||||
|
132 | # apply changegroup to remote | |||
|
133 | if unbundle: | |||
|
134 | # local repo finds heads on server, finds out what | |||
|
135 | # revs it must push. once revs transferred, if server | |||
|
136 | # finds it has different heads (someone else won | |||
|
137 | # commit/push race), server aborts. | |||
|
138 | if force: | |||
|
139 | remoteheads = ['force'] | |||
|
140 | # ssh: return remote's addchangegroup() | |||
|
141 | # http: return remote's addchangegroup() or 0 for error | |||
|
142 | ret = remote.unbundle(cg, remoteheads, 'push') | |||
|
143 | else: | |||
|
144 | # we return an integer indicating remote head count | |||
|
145 | # change | |||
|
146 | ret = remote.addchangegroup(cg, 'push', repo.url()) | |||
|
147 | ||||
|
148 | if ret: | |||
|
149 | # push succeed, synchronize target of the push | |||
|
150 | cheads = outgoing.missingheads | |||
|
151 | elif revs is None: | |||
|
152 | # All out push fails. synchronize all common | |||
|
153 | cheads = outgoing.commonheads | |||
|
154 | else: | |||
|
155 | # I want cheads = heads(::missingheads and ::commonheads) | |||
|
156 | # (missingheads is revs with secret changeset filtered out) | |||
|
157 | # | |||
|
158 | # This can be expressed as: | |||
|
159 | # cheads = ( (missingheads and ::commonheads) | |||
|
160 | # + (commonheads and ::missingheads))" | |||
|
161 | # ) | |||
|
162 | # | |||
|
163 | # while trying to push we already computed the following: | |||
|
164 | # common = (::commonheads) | |||
|
165 | # missing = ((commonheads::missingheads) - commonheads) | |||
|
166 | # | |||
|
167 | # We can pick: | |||
|
168 | # * missingheads part of common (::commonheads) | |||
|
169 | common = set(outgoing.common) | |||
|
170 | nm = repo.changelog.nodemap | |||
|
171 | cheads = [node for node in revs if nm[node] in common] | |||
|
172 | # and | |||
|
173 | # * commonheads parents on missing | |||
|
174 | revset = unfi.set('%ln and parents(roots(%ln))', | |||
|
175 | outgoing.commonheads, | |||
|
176 | outgoing.missing) | |||
|
177 | cheads.extend(c.node() for c in revset) | |||
|
178 | # even when we don't push, exchanging phase data is useful | |||
|
179 | remotephases = remote.listkeys('phases') | |||
|
180 | if (repo.ui.configbool('ui', '_usedassubrepo', False) | |||
|
181 | and remotephases # server supports phases | |||
|
182 | and ret is None # nothing was pushed | |||
|
183 | and remotephases.get('publishing', False)): | |||
|
184 | # When: | |||
|
185 | # - this is a subrepo push | |||
|
186 | # - and remote support phase | |||
|
187 | # - and no changeset was pushed | |||
|
188 | # - and remote is publishing | |||
|
189 | # We may be in issue 3871 case! | |||
|
190 | # We drop the possible phase synchronisation done by | |||
|
191 | # courtesy to publish changesets possibly locally draft | |||
|
192 | # on the remote. | |||
|
193 | remotephases = {'publishing': 'True'} | |||
|
194 | if not remotephases: # old server or public only repo | |||
|
195 | localphasemove(cheads) | |||
|
196 | # don't push any phase data as there is nothing to push | |||
|
197 | else: | |||
|
198 | ana = phases.analyzeremotephases(repo, cheads, remotephases) | |||
|
199 | pheads, droots = ana | |||
|
200 | ### Apply remote phase on local | |||
|
201 | if remotephases.get('publishing', False): | |||
|
202 | localphasemove(cheads) | |||
|
203 | else: # publish = False | |||
|
204 | localphasemove(pheads) | |||
|
205 | localphasemove(cheads, phases.draft) | |||
|
206 | ### Apply local phase on remote | |||
|
207 | ||||
|
208 | # Get the list of all revs draft on remote by public here. | |||
|
209 | # XXX Beware that revset break if droots is not strictly | |||
|
210 | # XXX root we may want to ensure it is but it is costly | |||
|
211 | outdated = unfi.set('heads((%ln::%ln) and public())', | |||
|
212 | droots, cheads) | |||
|
213 | for newremotehead in outdated: | |||
|
214 | r = remote.pushkey('phases', | |||
|
215 | newremotehead.hex(), | |||
|
216 | str(phases.draft), | |||
|
217 | str(phases.public)) | |||
|
218 | if not r: | |||
|
219 | repo.ui.warn(_('updating %s to public failed!\n') | |||
|
220 | % newremotehead) | |||
|
221 | repo.ui.debug('try to push obsolete markers to remote\n') | |||
|
222 | obsolete.syncpush(repo, remote) | |||
|
223 | finally: | |||
|
224 | if lock is not None: | |||
|
225 | lock.release() | |||
|
226 | finally: | |||
|
227 | if locallock is not None: | |||
|
228 | locallock.release() | |||
|
229 | ||||
|
230 | bookmarks.updateremote(repo.ui, unfi, remote, revs) | |||
|
231 | return ret |
@@ -9,7 +9,7 b' from i18n import _' | |||||
9 | import peer, changegroup, subrepo, discovery, pushkey, obsolete, repoview |
|
9 | import peer, changegroup, subrepo, discovery, pushkey, obsolete, repoview | |
10 | import changelog, dirstate, filelog, manifest, context, bookmarks, phases |
|
10 | import changelog, dirstate, filelog, manifest, context, bookmarks, phases | |
11 | import lock as lockmod |
|
11 | import lock as lockmod | |
12 | import transaction, store, encoding |
|
12 | import transaction, store, encoding, exchange | |
13 | import scmutil, util, extensions, hook, error, revset |
|
13 | import scmutil, util, extensions, hook, error, revset | |
14 | import match as matchmod |
|
14 | import match as matchmod | |
15 | import merge as mergemod |
|
15 | import merge as mergemod | |
@@ -1750,223 +1750,7 b' class localrepository(object):' | |||||
1750 | pass |
|
1750 | pass | |
1751 |
|
1751 | |||
1752 | def push(self, remote, force=False, revs=None, newbranch=False): |
|
1752 | def push(self, remote, force=False, revs=None, newbranch=False): | |
1753 | '''Push outgoing changesets (limited by revs) from the current |
|
1753 | return exchange.push(self, remote, force, revs, newbranch) | |
1754 | repository to remote. Return an integer: |
|
|||
1755 | - None means nothing to push |
|
|||
1756 | - 0 means HTTP error |
|
|||
1757 | - 1 means we pushed and remote head count is unchanged *or* |
|
|||
1758 | we have outgoing changesets but refused to push |
|
|||
1759 | - other values as described by addchangegroup() |
|
|||
1760 | ''' |
|
|||
1761 | if remote.local(): |
|
|||
1762 | missing = set(self.requirements) - remote.local().supported |
|
|||
1763 | if missing: |
|
|||
1764 | msg = _("required features are not" |
|
|||
1765 | " supported in the destination:" |
|
|||
1766 | " %s") % (', '.join(sorted(missing))) |
|
|||
1767 | raise util.Abort(msg) |
|
|||
1768 |
|
||||
1769 | # there are two ways to push to remote repo: |
|
|||
1770 | # |
|
|||
1771 | # addchangegroup assumes local user can lock remote |
|
|||
1772 | # repo (local filesystem, old ssh servers). |
|
|||
1773 | # |
|
|||
1774 | # unbundle assumes local user cannot lock remote repo (new ssh |
|
|||
1775 | # servers, http servers). |
|
|||
1776 |
|
||||
1777 | if not remote.canpush(): |
|
|||
1778 | raise util.Abort(_("destination does not support push")) |
|
|||
1779 | unfi = self.unfiltered() |
|
|||
1780 | def localphasemove(nodes, phase=phases.public): |
|
|||
1781 | """move <nodes> to <phase> in the local source repo""" |
|
|||
1782 | if locallock is not None: |
|
|||
1783 | phases.advanceboundary(self, phase, nodes) |
|
|||
1784 | else: |
|
|||
1785 | # repo is not locked, do not change any phases! |
|
|||
1786 | # Informs the user that phases should have been moved when |
|
|||
1787 | # applicable. |
|
|||
1788 | actualmoves = [n for n in nodes if phase < self[n].phase()] |
|
|||
1789 | phasestr = phases.phasenames[phase] |
|
|||
1790 | if actualmoves: |
|
|||
1791 | self.ui.status(_('cannot lock source repo, skipping local' |
|
|||
1792 | ' %s phase update\n') % phasestr) |
|
|||
1793 | # get local lock as we might write phase data |
|
|||
1794 | locallock = None |
|
|||
1795 | try: |
|
|||
1796 | locallock = self.lock() |
|
|||
1797 | except IOError, err: |
|
|||
1798 | if err.errno != errno.EACCES: |
|
|||
1799 | raise |
|
|||
1800 | # source repo cannot be locked. |
|
|||
1801 | # We do not abort the push, but just disable the local phase |
|
|||
1802 | # synchronisation. |
|
|||
1803 | msg = 'cannot lock source repository: %s\n' % err |
|
|||
1804 | self.ui.debug(msg) |
|
|||
1805 | try: |
|
|||
1806 | self.checkpush(force, revs) |
|
|||
1807 | lock = None |
|
|||
1808 | unbundle = remote.capable('unbundle') |
|
|||
1809 | if not unbundle: |
|
|||
1810 | lock = remote.lock() |
|
|||
1811 | try: |
|
|||
1812 | # discovery |
|
|||
1813 | fci = discovery.findcommonincoming |
|
|||
1814 | commoninc = fci(unfi, remote, force=force) |
|
|||
1815 | common, inc, remoteheads = commoninc |
|
|||
1816 | fco = discovery.findcommonoutgoing |
|
|||
1817 | outgoing = fco(unfi, remote, onlyheads=revs, |
|
|||
1818 | commoninc=commoninc, force=force) |
|
|||
1819 |
|
||||
1820 |
|
||||
1821 | if not outgoing.missing: |
|
|||
1822 | # nothing to push |
|
|||
1823 | scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded) |
|
|||
1824 | ret = None |
|
|||
1825 | else: |
|
|||
1826 | # something to push |
|
|||
1827 | if not force: |
|
|||
1828 | # if self.obsstore == False --> no obsolete |
|
|||
1829 | # then, save the iteration |
|
|||
1830 | if unfi.obsstore: |
|
|||
1831 | # this message are here for 80 char limit reason |
|
|||
1832 | mso = _("push includes obsolete changeset: %s!") |
|
|||
1833 | mst = "push includes %s changeset: %s!" |
|
|||
1834 | # plain versions for i18n tool to detect them |
|
|||
1835 | _("push includes unstable changeset: %s!") |
|
|||
1836 | _("push includes bumped changeset: %s!") |
|
|||
1837 | _("push includes divergent changeset: %s!") |
|
|||
1838 | # If we are to push if there is at least one |
|
|||
1839 | # obsolete or unstable changeset in missing, at |
|
|||
1840 | # least one of the missinghead will be obsolete or |
|
|||
1841 | # unstable. So checking heads only is ok |
|
|||
1842 | for node in outgoing.missingheads: |
|
|||
1843 | ctx = unfi[node] |
|
|||
1844 | if ctx.obsolete(): |
|
|||
1845 | raise util.Abort(mso % ctx) |
|
|||
1846 | elif ctx.troubled(): |
|
|||
1847 | raise util.Abort(_(mst) |
|
|||
1848 | % (ctx.troubles()[0], |
|
|||
1849 | ctx)) |
|
|||
1850 | newbm = self.ui.configlist('bookmarks', 'pushing') |
|
|||
1851 | discovery.checkheads(unfi, remote, outgoing, |
|
|||
1852 | remoteheads, newbranch, |
|
|||
1853 | bool(inc), newbm) |
|
|||
1854 |
|
||||
1855 | # TODO: get bundlecaps from remote |
|
|||
1856 | bundlecaps = None |
|
|||
1857 | # create a changegroup from local |
|
|||
1858 | if revs is None and not (outgoing.excluded |
|
|||
1859 | or self.changelog.filteredrevs): |
|
|||
1860 | # push everything, |
|
|||
1861 | # use the fast path, no race possible on push |
|
|||
1862 | bundler = changegroup.bundle10(self, bundlecaps) |
|
|||
1863 | cg = self._changegroupsubset(outgoing, |
|
|||
1864 | bundler, |
|
|||
1865 | 'push', |
|
|||
1866 | fastpath=True) |
|
|||
1867 | else: |
|
|||
1868 | cg = self.getlocalbundle('push', outgoing, bundlecaps) |
|
|||
1869 |
|
||||
1870 | # apply changegroup to remote |
|
|||
1871 | if unbundle: |
|
|||
1872 | # local repo finds heads on server, finds out what |
|
|||
1873 | # revs it must push. once revs transferred, if server |
|
|||
1874 | # finds it has different heads (someone else won |
|
|||
1875 | # commit/push race), server aborts. |
|
|||
1876 | if force: |
|
|||
1877 | remoteheads = ['force'] |
|
|||
1878 | # ssh: return remote's addchangegroup() |
|
|||
1879 | # http: return remote's addchangegroup() or 0 for error |
|
|||
1880 | ret = remote.unbundle(cg, remoteheads, 'push') |
|
|||
1881 | else: |
|
|||
1882 | # we return an integer indicating remote head count |
|
|||
1883 | # change |
|
|||
1884 | ret = remote.addchangegroup(cg, 'push', self.url()) |
|
|||
1885 |
|
||||
1886 | if ret: |
|
|||
1887 | # push succeed, synchronize target of the push |
|
|||
1888 | cheads = outgoing.missingheads |
|
|||
1889 | elif revs is None: |
|
|||
1890 | # All out push fails. synchronize all common |
|
|||
1891 | cheads = outgoing.commonheads |
|
|||
1892 | else: |
|
|||
1893 | # I want cheads = heads(::missingheads and ::commonheads) |
|
|||
1894 | # (missingheads is revs with secret changeset filtered out) |
|
|||
1895 | # |
|
|||
1896 | # This can be expressed as: |
|
|||
1897 | # cheads = ( (missingheads and ::commonheads) |
|
|||
1898 | # + (commonheads and ::missingheads))" |
|
|||
1899 | # ) |
|
|||
1900 | # |
|
|||
1901 | # while trying to push we already computed the following: |
|
|||
1902 | # common = (::commonheads) |
|
|||
1903 | # missing = ((commonheads::missingheads) - commonheads) |
|
|||
1904 | # |
|
|||
1905 | # We can pick: |
|
|||
1906 | # * missingheads part of common (::commonheads) |
|
|||
1907 | common = set(outgoing.common) |
|
|||
1908 | nm = self.changelog.nodemap |
|
|||
1909 | cheads = [node for node in revs if nm[node] in common] |
|
|||
1910 | # and |
|
|||
1911 | # * commonheads parents on missing |
|
|||
1912 | revset = unfi.set('%ln and parents(roots(%ln))', |
|
|||
1913 | outgoing.commonheads, |
|
|||
1914 | outgoing.missing) |
|
|||
1915 | cheads.extend(c.node() for c in revset) |
|
|||
1916 | # even when we don't push, exchanging phase data is useful |
|
|||
1917 | remotephases = remote.listkeys('phases') |
|
|||
1918 | if (self.ui.configbool('ui', '_usedassubrepo', False) |
|
|||
1919 | and remotephases # server supports phases |
|
|||
1920 | and ret is None # nothing was pushed |
|
|||
1921 | and remotephases.get('publishing', False)): |
|
|||
1922 | # When: |
|
|||
1923 | # - this is a subrepo push |
|
|||
1924 | # - and remote support phase |
|
|||
1925 | # - and no changeset was pushed |
|
|||
1926 | # - and remote is publishing |
|
|||
1927 | # We may be in issue 3871 case! |
|
|||
1928 | # We drop the possible phase synchronisation done by |
|
|||
1929 | # courtesy to publish changesets possibly locally draft |
|
|||
1930 | # on the remote. |
|
|||
1931 | remotephases = {'publishing': 'True'} |
|
|||
1932 | if not remotephases: # old server or public only repo |
|
|||
1933 | localphasemove(cheads) |
|
|||
1934 | # don't push any phase data as there is nothing to push |
|
|||
1935 | else: |
|
|||
1936 | ana = phases.analyzeremotephases(self, cheads, remotephases) |
|
|||
1937 | pheads, droots = ana |
|
|||
1938 | ### Apply remote phase on local |
|
|||
1939 | if remotephases.get('publishing', False): |
|
|||
1940 | localphasemove(cheads) |
|
|||
1941 | else: # publish = False |
|
|||
1942 | localphasemove(pheads) |
|
|||
1943 | localphasemove(cheads, phases.draft) |
|
|||
1944 | ### Apply local phase on remote |
|
|||
1945 |
|
||||
1946 | # Get the list of all revs draft on remote by public here. |
|
|||
1947 | # XXX Beware that revset break if droots is not strictly |
|
|||
1948 | # XXX root we may want to ensure it is but it is costly |
|
|||
1949 | outdated = unfi.set('heads((%ln::%ln) and public())', |
|
|||
1950 | droots, cheads) |
|
|||
1951 | for newremotehead in outdated: |
|
|||
1952 | r = remote.pushkey('phases', |
|
|||
1953 | newremotehead.hex(), |
|
|||
1954 | str(phases.draft), |
|
|||
1955 | str(phases.public)) |
|
|||
1956 | if not r: |
|
|||
1957 | self.ui.warn(_('updating %s to public failed!\n') |
|
|||
1958 | % newremotehead) |
|
|||
1959 | self.ui.debug('try to push obsolete markers to remote\n') |
|
|||
1960 | obsolete.syncpush(self, remote) |
|
|||
1961 | finally: |
|
|||
1962 | if lock is not None: |
|
|||
1963 | lock.release() |
|
|||
1964 | finally: |
|
|||
1965 | if locallock is not None: |
|
|||
1966 | locallock.release() |
|
|||
1967 |
|
||||
1968 | bookmarks.updateremote(self.ui, unfi, remote, revs) |
|
|||
1969 | return ret |
|
|||
1970 |
|
1754 | |||
1971 | def changegroupinfo(self, nodes, source): |
|
1755 | def changegroupinfo(self, nodes, source): | |
1972 | if self.ui.verbose or source == 'bundle': |
|
1756 | if self.ui.verbose or source == 'bundle': |
General Comments 0
You need to be logged in to leave comments.
Login now