71 b'', |
75 b'', |
72 b'source', |
76 b'source', |
73 b'', |
77 b'', |
74 _(b'repository to clone from'), |
78 _(b'repository to clone from'), |
75 ), |
79 ), |
|
80 ( |
|
81 b'', |
|
82 b'dest', |
|
83 b'', |
|
84 _(b'repository to update to REV (possibly cloning)'), |
|
85 ), |
|
86 ( |
|
87 b'', |
|
88 b'initial-clone-minimal', |
|
89 False, |
|
90 _( |
|
91 b'Pull only the prescribed revision upon initial cloning. ' |
|
92 b'This has the side effect of ignoring clone-bundles, ' |
|
93 b'which if often slower on the client side and stressful ' |
|
94 b'to the server than applying available clone bundles.' |
|
95 ), |
|
96 ), |
76 ], |
97 ], |
77 _(b'hg admin::chainsaw-update [OPTION] --rev REV --source SOURCE...'), |
98 _( |
|
99 b'hg admin::chainsaw-update [OPTION] --rev REV --source SOURCE --dest DEST' |
|
100 ), |
78 helpbasic=True, |
101 helpbasic=True, |
|
102 norepo=True, |
79 ) |
103 ) |
80 def update(ui, repo, **opts): |
104 def update(ui, **opts): |
81 """pull and update to a given revision, no matter what, (EXPERIMENTAL) |
105 """pull and update to a given revision, no matter what, (EXPERIMENTAL) |
82 |
106 |
83 Context of application: *some* Continuous Integration (CI) systems, |
107 Context of application: *some* Continuous Integration (CI) systems, |
84 packaging or deployment tools. |
108 packaging or deployment tools. |
85 |
109 |
86 Wanted end result: clean working directory updated at the given revision. |
110 Wanted end result: local repository at the given REPO_PATH, having the |
|
111 latest changes to the given revision and with a clean working directory |
|
112 updated at the given revision. |
87 |
113 |
88 chainsaw-update pulls from one source, then updates the working directory |
114 chainsaw-update pulls from one source, then updates the working directory |
89 to the given revision, overcoming anything that would stand in the way. |
115 to the given revision, overcoming anything that would stand in the way. |
90 |
116 |
91 By default, it will: |
117 By default, it will: |
92 |
118 |
|
119 - clone if the local repo does not exist yet, **removing any directory |
|
120 at the given path** that would not be a Mercurial repository. |
|
121 The initial clone is full by default, so that clonebundles can be |
|
122 applied. Use the --initial-clone-minimal flag to avoid this. |
93 - break locks if needed, leading to possible corruption if there |
123 - break locks if needed, leading to possible corruption if there |
94 is a concurrent write access. |
124 is a concurrent write access. |
95 - perform recovery actions if needed |
125 - perform recovery actions if needed |
96 - revert any local modification. |
126 - revert any local modification. |
97 - purge unknown and ignored files. |
127 - purge unknown and ignored files. |
114 repositories), an exceptional recloning is better than missing a release |
144 repositories), an exceptional recloning is better than missing a release |
115 deadline. |
145 deadline. |
116 """ |
146 """ |
117 rev = opts['rev'] |
147 rev = opts['rev'] |
118 source = opts['source'] |
148 source = opts['source'] |
|
149 repo_path = opts['dest'] |
119 if not rev: |
150 if not rev: |
120 raise error.InputError(_(b'specify a target revision with --rev')) |
151 raise error.InputError(_(b'specify a target revision with --rev')) |
121 if not source: |
152 if not source: |
122 raise error.InputError(_(b'specify a pull path with --source')) |
153 raise error.InputError(_(b'specify a pull path with --source')) |
|
154 if not repo_path: |
|
155 raise error.InputError(_(b'specify a repo path with --dest')) |
|
156 repo_path = urlutil.urllocalpath(repo_path) |
|
157 |
|
158 try: |
|
159 repo = localrepo.instance(ui, repo_path, create=False) |
|
160 repo_created = False |
|
161 ui.status(_(b'loaded repository at "%s"\n' % repo_path)) |
|
162 except error.RepoError: |
|
163 try: |
|
164 shutil.rmtree(repo_path) |
|
165 except FileNotFoundError: |
|
166 ui.status(_(b'no such directory: "%s"\n' % repo_path)) |
|
167 else: |
|
168 ui.status( |
|
169 _( |
|
170 b'removed non-repository file or directory ' |
|
171 b'at "%s"' % repo_path |
|
172 ) |
|
173 ) |
|
174 |
|
175 ui.status(_(b'creating repository at "%s"\n' % repo_path)) |
|
176 repo = localrepo.instance(ui, repo_path, create=True) |
|
177 repo_created = True |
|
178 |
123 if repo.svfs.tryunlink(b'lock'): |
179 if repo.svfs.tryunlink(b'lock'): |
124 ui.status(_(b'had to break store lock\n')) |
180 ui.status(_(b'had to break store lock\n')) |
125 if repo.vfs.tryunlink(b'wlock'): |
181 if repo.vfs.tryunlink(b'wlock'): |
126 ui.status(_(b'had to break working copy lock\n')) |
182 ui.status(_(b'had to break working copy lock\n')) |
127 |
183 |
128 ui.status(_(b'recovering after interrupted transaction, if any\n')) |
184 ui.status(_(b'recovering after interrupted transaction, if any\n')) |
129 repo.recover() |
185 repo.recover() |
130 |
186 |
131 ui.status(_(b'pulling from %s\n') % source) |
187 ui.status(_(b'pulling from %s\n') % source) |
|
188 if repo_created and not opts.get('initial_clone_minimal'): |
|
189 pull_revs = [] |
|
190 else: |
|
191 pull_revs = [rev] |
132 overrides = {(b'ui', b'quiet'): True} |
192 overrides = {(b'ui', b'quiet'): True} |
133 with ui.configoverride(overrides, b'chainsaw-update'): |
193 with repo.ui.configoverride(overrides, b'chainsaw-update'): |
134 pull = cmdutil.findcmd(b'pull', commands.table)[1][0] |
194 pull = cmdutil.findcmd(b'pull', commands.table)[1][0] |
135 pull(ui, repo, source, rev=[rev], remote_hidden=False) |
195 pull(repo.ui, repo, source, rev=pull_revs, remote_hidden=False) |
136 |
196 |
137 purge = cmdutil.findcmd(b'purge', commands.table)[1][0] |
197 purge = cmdutil.findcmd(b'purge', commands.table)[1][0] |
138 purge( |
198 purge( |
139 ui, |
199 ui, |
140 repo, |
200 repo, |