Skip to content

Commit 2ef8f1b

Browse files
committed
[Update Checkout] Fall back to revision if branch checkout fails.
In workflows featuring git worktrees, it is common for the same branch to be in use by multiple multiple checkouts. For example, at the moment, the "master" branches of swift-format, swift-tensorflow-apis, and pythonkit (and the "release" branch of ninja) are indicated, in update-checkout-config.json, by both "master" and "release/5.3". If one has a workflow featuring git worktrees, that means that when one runs <<update_checkout --scheme master>> in one's mainline directory and <<update_checkout --scheme release/5.3>> in one's release/5.3 directory, the latter will encounter failures for each of those four projects because the branch "master" will already be checked out in the mainline directory's worktrees and so it cannot be checked out in the release/5.3's directory's worktrees. The error looks something like: /path/to/swift-container/release53/swift-format failed (ret=128): ['git', 'checkout', u'master'] fatal: 'master' is already checked out at '/path/to/swift-container/mainline/swift-format' /path/to/swift-container/release53/tensorflow-swift-apis failed (ret=128): ['git', 'checkout', u'master'] fatal: 'master' is already checked out at '/path/to/swift-container/mainline/tensorflow-swift-apis' /path/to/swift-container/release53/pythonkit failed (ret=128): ['git', 'checkout', u'master'] fatal: 'master' is already checked out at '/path/to/swift-container/mainline/pythonkit' /path/to/swift-container/release53/ninja failed (ret=128): ['git', 'checkout', u'release'] fatal: 'release' is already checked out at '/path/to/swift-container/mainline/ninja' Here, that workflow is enabled. If <<git checkout branch_name>> fails, for one of the projects, update_checkout falls back to getting the SHA for the indicated branch via <<git rev-parse branch_name>> and then checking out the SHA directly.
1 parent d9a88b9 commit 2ef8f1b

File tree

3 files changed

+77
-2
lines changed

3 files changed

+77
-2
lines changed

utils/update_checkout/tests/scheme_mock.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ def setup_mock_remote(base_dir):
119119
with open(get_config_path(base_dir), 'w') as f:
120120
json.dump(base_config, f)
121121

122+
return (LOCAL_PATH, REMOTE_PATH)
123+
122124

123125
BASEDIR_ENV_VAR = 'UPDATECHECKOUT_TEST_WORKSPACE_DIR'
124126
CURRENT_FILE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -147,7 +149,7 @@ def __init__(self, *args, **kwargs):
147149

148150
def setUp(self):
149151
create_dir(self.source_root)
150-
setup_mock_remote(self.workspace)
152+
(self.local_path, self.remote_path) = setup_mock_remote(self.workspace)
151153

152154
def tearDown(self):
153155
teardown_mock_remote(self.workspace)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# ===--- test_clone.py ----------------------------------------------------===#
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
# Licensed under Apache License v2.0 with Runtime Library Exception
7+
#
8+
# See https:#swift.org/LICENSE.txt for license information
9+
# See https:#swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
#
11+
# ===----------------------------------------------------------------------===#
12+
13+
import os
14+
15+
from scheme_mock import call_quietly
16+
17+
from . import scheme_mock
18+
19+
WORKTREE_NAME = "feature"
20+
21+
22+
def path_for_worktree(workspace_path, worktree_name):
23+
return os.path.join(workspace_path, worktree_name)
24+
25+
26+
def setup_worktree(workspace_path, local_path, worktree_name):
27+
worktree_path = path_for_worktree(workspace_path, worktree_name)
28+
os.makedirs(worktree_path)
29+
for project in os.listdir(local_path):
30+
local_project_path = os.path.join(local_path, project)
31+
worktree_project_path = os.path.join(worktree_path, project)
32+
call_quietly(['git',
33+
'-C', local_project_path,
34+
'worktree', 'add', worktree_project_path])
35+
36+
37+
def teardown_worktree(workspace_path, local_path, worktree_name):
38+
worktree_path = path_for_worktree(workspace_path, worktree_name)
39+
for project in os.listdir(local_path):
40+
local_project_path = os.path.join(local_path, project)
41+
worktree_project_path = os.path.join(worktree_path, project)
42+
call_quietly(['git',
43+
'-C', local_project_path,
44+
'worktree', 'remove', worktree_project_path])
45+
46+
47+
class WorktreeTestCase(scheme_mock.SchemeMockTestCase):
48+
49+
def __init__(self, *args, **kwargs):
50+
super(WorktreeTestCase, self).__init__(*args, **kwargs)
51+
52+
def test_worktree(self):
53+
self.call([self.update_checkout_path,
54+
'--config', self.config_path,
55+
'--source-root', self.worktree_path,
56+
'--scheme', 'master'])
57+
58+
def setUp(self):
59+
super(WorktreeTestCase, self).setUp()
60+
self.worktree_path = os.path.join(self.workspace, WORKTREE_NAME)
61+
setup_worktree(self.workspace, self.local_path, WORKTREE_NAME)
62+
63+
def tearDown(self):
64+
teardown_worktree(self.workspace, self.local_path, WORKTREE_NAME)
65+
super(WorktreeTestCase, self).tearDown()

utils/update_checkout/update_checkout/update_checkout.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,15 @@ def update_single_repository(pool_args):
158158
if checkout_target:
159159
shell.run(['git', 'status', '--porcelain', '-uno'],
160160
echo=False)
161-
shell.run(['git', 'checkout', checkout_target], echo=True)
161+
try:
162+
shell.run(['git', 'checkout', checkout_target], echo=True)
163+
except Exception as originalException:
164+
try:
165+
result = shell.run(['git', 'rev-parse', checkout_target])
166+
revision = result[0].strip()
167+
shell.run(['git', 'checkout', revision], echo=True)
168+
except Exception as e:
169+
raise originalException
162170

163171
# It's important that we checkout, fetch, and rebase, in order.
164172
# .git/FETCH_HEAD updates the not-for-merge attributes based on

0 commit comments

Comments
 (0)