Skip to content

Commit

Permalink
Merge pull request #531 from rhubert/rh-fix-git-switch
Browse files Browse the repository at this point in the history
git: fix scm switch when going back to older revision
  • Loading branch information
jkloetzke authored Sep 5, 2023
2 parents d8326d7 + a08c442 commit a160bcc
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 5 deletions.
26 changes: 21 additions & 5 deletions pym/bob/scm/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,14 +295,30 @@ async def __checkoutTagOnBranch(self, invoker, fetchCmd, switch):
else:
# We're switching the ref and the branch exists already. Be extra
# careful: the user might have committed to this branch, some other
# branch might be checked out currently or both of that. To keep
# things simple, assume a fast-forward of the commit on the branch.
# It will catch user changes and is usually safe wrt. submodules.
# branch might be checked out currently or both of that.
await invoker.checkCommand(["git", "checkout", "--no-recurse-submodules",
self.__branch], cwd=self.__dir)
preUpdate = await self.__updateSubmodulesPre(invoker)
await invoker.checkCommand(["git", "-c", "submodule.recurse=0", "merge",
"--ff-only", commit], cwd=self.__dir)

# check if any remote or any other than the local branch holds the current
# commit. Otherwise we'd lose it when going back in history.
contains = await invoker.runCommand(["git", "branch", "-a",
"--format=%(refname:lstrip=2)",
"--contains", "HEAD"],
cwd=self.__dir, stdout=True)
currentBranch = await invoker.runCommand(["git", "rev-parse",
"--abbrev-ref", "HEAD"],
cwd=self.__dir, stdout=True)
for b in contains.stdout.splitlines():
if b.rstrip() != currentBranch.stdout.rstrip():
break
else:
# neither a remote nor another local branch contains the current commit
# move to attic
invoker.fail("Cannot switch: Current state woulde be lost.")

await invoker.checkCommand(["git", "-c", "submodule.recurse=0", "reset",
"--keep", commit], cwd=self.__dir)
await self.__updateSubmodulesPost(invoker, preUpdate)

async def __checkoutTag(self, invoker, fetchCmd, switch):
Expand Down
43 changes: 43 additions & 0 deletions test/black-box/git-scm-switch/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,52 @@ run_bob dev -c submodules -DSCM_DIR="$git_dir1" -DSCM_REV="$d1_c0" -DSCM_BRANCH=
expect_output "foobar" git -C dev/src/root2/1/workspace rev-parse --abbrev-ref HEAD
expect_output "$d1_c0" git -C dev/src/root2/1/workspace rev-parse HEAD
expect_not_exist dev/src/root2/1/workspace/submod/sub.txt
expect_output "hello world" cat dev/src/root2/1/workspace/test.txt

# Moving to a later commit will update the branch and bring in the submodules.
run_bob dev -c submodules -DSCM_DIR="$git_dir1" -DSCM_REV="$d1_c1" -DSCM_BRANCH=foobar root2 -vv
expect_output "foobar" git -C dev/src/root2/1/workspace rev-parse --abbrev-ref HEAD
expect_output "$d1_c1" git -C dev/src/root2/1/workspace rev-parse HEAD
expect_exist dev/src/root2/1/workspace/submod/sub.txt

# Move to c2 but without submodules. Otherwise every revision change triggers a move to attic and
# hides potential issues when going back to older revisions
run_bob dev -DSCM_DIR="$git_dir1" -DSCM_REV="$d1_c2" -DSCM_BRANCH=foobar root2 -vv
expect_output "changed" cat dev/src/root2/1/workspace/test.txt

# Move back to a older commit
run_bob dev -DSCM_DIR="$git_dir1" -DSCM_REV="$d1_c0" -DSCM_BRANCH=foobar root2 -vv
expect_output "foobar" git -C dev/src/root2/1/workspace rev-parse --abbrev-ref HEAD
expect_output "$d1_c0" git -C dev/src/root2/1/workspace rev-parse HEAD
expect_not_exist dev/src/root2/1/workspace/submod/sub.txt
expect_output "hello world" cat dev/src/root2/1/workspace/test.txt

# make a new commit but do not push it and run the update. This should move to attic as we'd lose
# the commit when switching.
pushd dev/src/root2/1/workspace
git config user.email "bob@bob.bob"
git config user.name test
echo "local_commit" > test.txt
git commit -a -m "just a local change"
popd
rm dev/src/root/1/attic* -rf
run_bob dev -DSCM_DIR="$git_dir1" -DSCM_REV="$d1_c1" -DSCM_BRANCH=foobar root2 -vv
ls -la dev/src/root2/1/workspace
expect_output "hello world" cat dev/src/root2/1/workspace/test.txt
expect_exist dev/src/root2/1/attic

# make a new commit, switch to a different local branch and run the update. This should not move to attic.
# But first go back...
run_bob dev -DSCM_DIR="$git_dir1" -DSCM_REV="$d1_c0" -DSCM_BRANCH=foobar root2 -vv
pushd dev/src/root2/1/workspace
git config user.email "bob@bob.bob"
git config user.name test
echo "local_commit" > test.txt
git commit -a -m "just a local change"
git checkout -b foobar2
popd
rm dev/src/root2/1/attic* -rf
run_bob dev -DSCM_DIR="$git_dir1" -DSCM_REV="$d1_c1" -DSCM_BRANCH=foobar root2 -vv
ls -la dev/src/root2/1/workspace
expect_output "hello world" cat dev/src/root2/1/workspace/test.txt
expect_not_exist dev/src/root2/1/attic

0 comments on commit a160bcc

Please sign in to comment.