Skip to content

Commit 426fdfa

Browse files
janverbTom Blauwendraat
authored andcommitted
Support specifying a pin for merges
1 parent f4aaa32 commit 426fdfa

File tree

4 files changed

+91
-45
lines changed

4 files changed

+91
-45
lines changed

git_aggregator/config.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,23 @@ def get_repos(config, force=False):
5757
try:
5858
# Assume parts is a str
5959
parts = merge.split(' ')
60-
if len(parts) != 2:
60+
if len(parts) not in {2, 3}:
6161
raise ConfigException(
6262
'%s: Merge must be formatted as '
63-
'"remote_name ref".' % directory)
63+
'"remote_name ref [pin]".' % directory)
6464
merge = {
6565
"remote": parts[0],
6666
"ref": parts[1],
6767
}
68+
if len(parts) == 3:
69+
merge["pin"] = parts[2]
6870
except AttributeError:
6971
# Parts is a dict
7072
try:
7173
merge["remote"] = str(merge["remote"])
7274
merge["ref"] = str(merge["ref"])
75+
if merge.get("pin"):
76+
merge["pin"] = str(merge["pin"])
7377
except KeyError:
7478
raise ConfigException(
7579
'%s: Merge lacks mandatory '

git_aggregator/repo.py

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -275,31 +275,45 @@ def fetch(self):
275275
It returns a dict structure associating each (remote, ref) to their
276276
SHA in local repository.
277277
"""
278-
merges_requested = [(m["remote"], m["ref"])
279-
for m in self.merges]
280278
basecmd = ("git", "fetch")
281279
logger.info("Fetching required remotes")
282280
fetch_heads = {}
283281
ls_remote_refs = collections.defaultdict(list) # to ls-query
284-
while merges_requested:
285-
remote, ref = merges_requested[0]
286-
merges_requested = merges_requested[1:]
282+
for merge in self.merges:
283+
remote = merge["remote"]
284+
ref = merge["ref"]
285+
pin = merge.get("pin")
287286
cmd = (
288287
basecmd +
289288
self._fetch_options({"remote": remote, "ref": ref}) +
290289
(remote,))
291290
if remote not in self.fetch_all:
292291
cmd += (ref, )
293292
else:
293+
if pin:
294+
# Probably solvable, but a little too tricky for me to
295+
# figure out right now
296+
raise GitAggregatorException(
297+
"Cannot use fetch_all with pin"
298+
)
294299
ls_remote_refs[remote].append(ref)
295300
self.log_call(cmd, cwd=self.cwd)
296-
with open(os.path.join(self.cwd, ".git", "FETCH_HEAD"), "r") as f:
297-
for line in f:
298-
fetch_head, for_merge, _ = line.split("\t")
299-
if for_merge == "not-for-merge":
300-
continue
301-
break
302-
fetch_heads[(remote, ref)] = fetch_head
301+
if pin:
302+
try:
303+
fetch_heads[(remote, ref)] = self.rev_parse(pin)
304+
except Exception:
305+
logger.error(
306+
"Could not find pin %r after fetching %r", pin, ref
307+
)
308+
raise
309+
else:
310+
with open(os.path.join(self.cwd, ".git", "FETCH_HEAD")) as f:
311+
for line in f:
312+
fetch_head, for_merge, _ = line.split("\t")
313+
if for_merge == "not-for-merge":
314+
continue
315+
break
316+
fetch_heads[(remote, ref)] = fetch_head
303317
if self.fetch_all:
304318
if self.fetch_all is True:
305319
remotes = self.remotes
@@ -311,31 +325,16 @@ def fetch(self):
311325
remote["url"],
312326
ls_remote_refs[remote["name"]])
313327
for _, ref, sha in refs:
314-
if (remote["name"], ref) in merges_requested:
315-
merges_requested.remove((remote["name"], ref))
316328
fetch_heads[(remote["name"], ref)] = sha
317-
if len(merges_requested):
318-
# Last case: our ref is a sha and remote git repository does
319-
# not support querying commit directly by SHA. In this case
320-
# we need just to check if ref is actually SHA, and if we have
321-
# this SHA locally.
322-
for remote, ref in merges_requested:
323-
if not re.search("[0-9a-f]{4,}", ref):
324-
raise ValueError("Could not resolv ref %r on remote %r"
325-
% (ref, remote))
326-
valid_local_shas = self.log_call(
327-
['git', 'rev-parse', '-v'] + [sha
328-
for _r, sha in merges_requested],
329-
cwd=self.cwd, callwith=subprocess.check_output
330-
).strip().splitlines()
331-
for remote, sha in merges_requested:
332-
if sha not in valid_local_shas:
333-
raise ValueError(
334-
"Could not find SHA ref %r after fetch on remote %r"
335-
% (ref, remote))
336-
fetch_heads[(remote["name"], sha)] = sha
337329
return fetch_heads
338330

331+
def rev_parse(self, ref):
332+
return self.log_call(
333+
["git", "rev-parse", "--verify", ref],
334+
callwith=subprocess.check_output,
335+
cwd=self.cwd,
336+
).strip()
337+
339338
def push(self):
340339
remote = self.target['remote']
341340
branch = self.target['branch']
@@ -384,11 +383,7 @@ def _switch_to_branch(self, branch_name, ref=None):
384383
logger.info("Switch to branch %s", branch_name)
385384
cmd = ['git', 'checkout', '-B', branch_name]
386385
if ref is not None:
387-
sha1 = self.log_call(
388-
['git', 'rev-parse', ref],
389-
callwith=subprocess.check_output,
390-
cwd=self.cwd).strip()
391-
cmd.append(sha1)
386+
cmd.append(self.rev_parse(ref))
392387
self.log_call(cmd, cwd=self.cwd)
393388

394389
def _execute_shell_command_after(self):

tests/test_config.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def test_load(self):
2828
merges:
2929
- oca 8.0
3030
- oca refs/pull/105/head
31-
- oca refs/pull/106/head
31+
- oca refs/pull/106/head 1234abcd
3232
target: acsone aggregated_branch_name
3333
"""
3434
repos = config.get_repos(self._parse_config(config_yaml))
@@ -45,7 +45,8 @@ def test_load(self):
4545
'defaults': {},
4646
'merges': [{'ref': '8.0', 'remote': 'oca'},
4747
{'ref': 'refs/pull/105/head', 'remote': 'oca'},
48-
{'ref': 'refs/pull/106/head', 'remote': 'oca'}],
48+
{'ref': 'refs/pull/106/head', 'remote': 'oca',
49+
'pin': '1234abcd'}],
4950
'remotes': [],
5051
'shell_command_after': [],
5152
'target': {'branch': 'aggregated_branch_name',
@@ -76,6 +77,7 @@ def test_load_defaults(self):
7677
-
7778
remote: oca
7879
ref: refs/pull/106/head
80+
pin: 1234abcd
7981
target: acsone aggregated_branch_name
8082
""")
8183
repos = config.get_repos(self._parse_config(config_yaml))
@@ -92,7 +94,8 @@ def test_load_defaults(self):
9294
'defaults': {'depth': 1},
9395
'merges': [{'ref': '8.0', 'remote': 'oca', 'depth': 1000},
9496
{'ref': 'refs/pull/105/head', 'remote': 'oca'},
95-
{'ref': 'refs/pull/106/head', 'remote': 'oca'}],
97+
{'ref': 'refs/pull/106/head', 'remote': 'oca',
98+
'pin': '1234abcd'}],
9699
'remotes': [],
97100
'shell_command_after': [],
98101
'target': {'branch': 'aggregated_branch_name',
@@ -202,7 +205,7 @@ def test_load_merges_exception(self):
202205
self.assertEqual(
203206
ex.exception.args[0],
204207
'/product_attribute: Merge must be formatted as '
205-
'"remote_name ref".')
208+
'"remote_name ref [pin]".')
206209
config_yaml = """
207210
/product_attribute:
208211
remotes:

tests/test_repo.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,47 @@ def test_multithreading(self):
451451

452452
self.assertTrue(os.path.isfile(os.path.join(repo3_dir, 'tracked')))
453453
self.assertTrue(os.path.isfile(os.path.join(repo3_dir, 'tracked2')))
454+
455+
def test_pinned_base(self):
456+
remotes = [{
457+
'name': 'r1',
458+
'url': self.url_remote1
459+
}]
460+
merges = [{
461+
'remote': 'r1',
462+
'ref': 'master',
463+
'pin': self.commit_1_sha[:8]
464+
}]
465+
target = {
466+
'remote': 'r1',
467+
'branch': 'agg1'
468+
}
469+
repo = Repo(self.cwd, remotes, merges, target)
470+
repo.aggregate()
471+
last_rev = git_get_last_rev(self.cwd)
472+
self.assertEqual(last_rev, self.commit_1_sha)
473+
474+
def test_pinned_merge(self):
475+
remotes = [{
476+
'name': 'r1',
477+
'url': self.url_remote1
478+
}, {
479+
'name': 'r2',
480+
'url': self.url_remote2
481+
}]
482+
merges = [{
483+
'remote': 'r1',
484+
'ref': 'tag2'
485+
}, {
486+
'remote': 'r2',
487+
'ref': self.commit_3_sha,
488+
'pin': self.commit_1_sha[:8]
489+
}]
490+
target = {
491+
'remote': 'r1',
492+
'branch': 'agg'
493+
}
494+
repo = Repo(self.cwd, remotes, merges, target)
495+
repo.aggregate()
496+
last_rev = git_get_last_rev(self.cwd)
497+
self.assertEqual(last_rev, self.commit_2_sha)

0 commit comments

Comments
 (0)