diff --git a/lib/git-subrepo b/lib/git-subrepo index 954380b8..4aed1897 100755 --- a/lib/git-subrepo +++ b/lib/git-subrepo @@ -118,6 +118,7 @@ main() { local subrepo_remote= # Remote url for subrepo's upstream repo local subrepo_branch= # Upstream branch to clone/push/pull + local subrepo_branch_commit= # Upstream branch commit to clone local subrepo_commit= # Upstream HEAD from previous clone/pull local subrepo_parent= # Local commit from before previous clone/pull local subrepo_former= # A retired gitrepo key that might still exist @@ -205,7 +206,11 @@ command:clone() { local re= $force_wanted && re=re local remote=$subrepo_remote - say "Subrepo '$remote' ($subrepo_branch) ${re}cloned into '$subdir'." + local bc=$subrepo_branch + if [ -n "$subrepo_branch_commit" ]; then + bc=$subrepo_branch@$subrepo_branch_commit + fi + say "Subrepo '$remote' ($bc) ${re}cloned into '$subdir'." } # `git subrepo init ` command: @@ -236,7 +241,11 @@ command:pull() { subrepo:pull if OK; then - say "Subrepo '$subdir' pulled from '$subrepo_remote' ($subrepo_branch)." + local bc=$subrepo_branch + if [ -n "$subrepo_branch_commit" ]; then + bc=$subrepo_branch@$subrepo_branch_commit + fi + say "Subrepo '$subdir' pulled from '$subrepo_remote' ($bc)." elif [[ $CODE -eq -1 ]]; then say "Subrepo '$subdir' is up to date." elif [[ $CODE -eq 1 ]]; then @@ -274,7 +283,11 @@ command:fetch() { say "Ignored '$subdir', no remote." else subrepo:fetch - say "Fetched '$subdir' from '$subrepo_remote' ($subrepo_branch)." + local bc=$subrepo_branch + if [ -n "$subrepo_branch_commit" ]; then + bc=$subrepo_branch@$subrepo_branch_commit + fi + say "Fetched '$subdir' from '$subrepo_remote' ($bc)." fi } @@ -727,6 +740,13 @@ subrepo:fetch() { RUN git fetch --no-tags --quiet "$subrepo_remote" "$subrepo_branch" OK || return + if [[ -n "$subrepo_branch_commit" ]]; then + reset-upstream-subrepo-branch-commit + o "Re-fetch the upstream: $subrepo_remote ($subrepo_branch@$subrepo_branch_commit)." + RUN git fetch --no-tags --quiet "$subrepo_remote" "$subrepo_branch_commit" + OK || return + fi + o "Get the upstream subrepo HEAD commit." OUT=true RUN git rev-parse FETCH_HEAD^0 upstream_head_commit=$output @@ -1093,8 +1113,11 @@ get-command-options() { -a) all_wanted=true ;; -A) ALL_wanted=true all_wanted=true ;; - -b) subrepo_branch=$1 - override_branch=$1 + -b) subrepo_branch=${1%%@*} + override_branch=${1%%@*} + if [ "${subrepo_branch}" != "${1}" ]; then + subrepo_branch_commit=${1#*@} + fi commit_msg_args+=("--branch=$1") shift ;; -e) edit_wanted=true ;; @@ -1650,6 +1673,25 @@ add-subrepo() { subrepos+=("$1") } +# Determine and set real commit behind branch@commit request +reset-upstream-subrepo-branch-commit() { + local branch_commit + OUT=true RUN git rev-parse FETCH_HEAD^0 + branch_commit=$output + [[ -n "$branch_commit" ]] || + error "Problem finding head commit in upstream branch '${subrepo_branch}'." + + if [[ ! "${branch_commit}" =~ ^$subrepo_branch_commit ]]; then + OUT=true FAIL=false RUN git rev-list --parents "$subrepo_branch_commit".."$branch_commit" + branch_commit=$( + echo "$output" | sed -e '$!d' -e 's/.* //' + ) + [[ -n "$branch_commit" ]] || + error "Problem finding '${subrepo_branch_commit}' commit in upstream branch '${subrepo_branch}'." + fi + subrepo_branch_commit=$branch_commit +} + # Determine the upstream's default head branch: get-upstream-head-branch() { local remotes branch diff --git a/test/use-branch-at-commit.t b/test/use-branch-at-commit.t new file mode 100644 index 00000000..b918bdae --- /dev/null +++ b/test/use-branch-at-commit.t @@ -0,0 +1,368 @@ +#!/usr/bin/env bash + +set -e + +source test/setup + +use Test::More + +clone-foo-and-bar + +# Test that the repos look ok: +{ + test-exists \ + "$OWNER/foo/.git/" \ + "$OWNER/foo/Foo" \ + "!$OWNER/foo/bar/" \ + "$OWNER/bar/.git/" \ + "$OWNER/bar/Bar" +} + +# Make a new branch bar1 in bar +( + cd $OWNER/bar + git checkout -b bar1 +) &> /dev/null || die + +# check the new bar1 branch: +{ + is "$( + cd "$OWNER/bar" + git branch | grep "^*" + )" \ + "* bar1" \ + 'bar: The repo branch is correct' +} + +# Make a few new commits in a new branch bar1 +( + cd $OWNER/bar + echo "Added first change to Bar" >> Bar + git add Bar + git commit -m"Added first commit to Bar" +) &> /dev/null || die + +{ + cd $OWNER/bar + bar_commit1=$(git rev-parse HEAD) + is "$( + cd "$OWNER/bar" + git log -1 --oneline | sed -e 's/ .*//g' + )" \ + "${bar_commit1:0:7}" \ + 'bar: The first added commit is correct' +} + +( + cd $OWNER/bar + echo "Added second change to Bar" >> Bar + git add Bar + git commit -m"Added second commit to Bar" +) &> /dev/null || die + +{ + cd $OWNER/bar + bar_commit2=$(git rev-parse HEAD) + is "$( + cd "$OWNER/bar" + git log -1 --oneline | sed -e 's/ .*//g' + )" \ + "${bar_commit2:0:7}" \ + 'bar: The second added commit is correct' +} + +( + cd $OWNER/bar + echo "Added third change to Bar" >> Bar + git add Bar + git commit -m"Added third commit to Bar" +) &> /dev/null || die + +{ + cd $OWNER/bar + bar_commit3=$(git rev-parse HEAD) + is "$( + cd "$OWNER/bar" + git log -1 --oneline | sed -e 's/ .*//g' + )" \ + "${bar_commit3:0:7}" \ + 'bar: The third added commit is correct' +} + +# Checkout default branch in bar +( + cd $OWNER/bar + git checkout ${DEFAULTBRANCH} +) &> /dev/null || die + +# check the default branch: +{ + is "$( + cd "$OWNER/bar" + git branch | grep "^*" + )" \ + "* ${DEFAULTBRANCH}" \ + 'bar: The repo branch is correct' +} + +# Do the subrepo clone branch@commit1 and test the output: +{ + clone_output=$( + cd "$OWNER/foo" + git subrepo clone -b bar1@${bar_commit1:0:8} "$OWNER/bar" + ) + + # Check output is correct: + is "$clone_output" \ + "Subrepo '$OWNER/bar' (bar1@${bar_commit1}) cloned into 'bar'." \ + 'subrepo clone command output is correct' + + is "$( + cd "$OWNER/foo" + git remote -v | grep subrepo/bar + )" \ + "" \ + 'No remotes created' +} + +# Check that subrepo files look ok: +gitrepo=$OWNER/foo/bar/.gitrepo +{ + test-exists \ + "$OWNER/foo/bar/" \ + "$OWNER/foo/bar/Bar" \ + "$gitrepo" +} + +# Test foo/bar/.gitrepo file contents: +{ + foo_clone_commit=$(cd "$OWNER/foo"; git rev-parse HEAD^) + test-gitrepo-comment-block + test-gitrepo-field "remote" "$OWNER/bar" + test-gitrepo-field "branch" "bar1" + test-gitrepo-field "commit" "$bar_commit1" + test-gitrepo-field "parent" "$foo_clone_commit" + test-gitrepo-field "cmdver" "$(git subrepo --version)" +} + +# Make sure status is clean: +{ + git_status=$( + cd "$OWNER/foo" + git status -s + ) + + is "$git_status" \ + "" \ + 'status is clean' +} + +# Do the subrepo pull branch@commit2 and test the output: +{ + pull_output=$( + cd "$OWNER/foo" + git subrepo pull -b bar1@${bar_commit2:0:8} bar + ) + + # Check output is correct: + is "$pull_output" \ + "Subrepo 'bar' pulled from '$OWNER/bar' (bar1@${bar_commit2})." \ + 'subrepo pull command output is correct' + + is "$( + cd "$OWNER/foo" + git remote -v | grep subrepo/bar + )" \ + "" \ + 'No remotes created' +} + +# Check that subrepo files look ok: +gitrepo=$OWNER/foo/bar/.gitrepo +{ + test-exists \ + "$OWNER/foo/bar/" \ + "$OWNER/foo/bar/Bar" \ + "$gitrepo" +} + +# Test foo/bar/.gitrepo file contents: +{ + foo_clone_commit=$(cd "$OWNER/foo"; git rev-parse HEAD^) + test-gitrepo-comment-block + test-gitrepo-field "remote" "$OWNER/bar" + test-gitrepo-field "branch" "bar1" + test-gitrepo-field "commit" "$bar_commit2" + test-gitrepo-field "parent" "$foo_clone_commit" + test-gitrepo-field "cmdver" "$(git subrepo --version)" +} + +# Make sure status is clean: +{ + git_status=$( + cd "$OWNER/foo" + git status -s + ) + + is "$git_status" \ + "" \ + 'status is clean' +} + +# Make additional change in foo/bar +( + cd $OWNER/foo + echo "Added first change to foo/bar/Bar" >> bar/Bar + git add bar/Bar + git commit -m"Added first commit to foo/bar/Bar" + #foo_commit1=$(git rev-parse HEAD) +) &> /dev/null || die + +{ + cd $OWNER/foo + foo_commit1=$(git rev-parse HEAD) + is "$( + cd "$OWNER/foo" + git log -1 --oneline | sed -e 's/ .*//g' + )" \ + "${foo_commit1:0:7}" \ + 'foo: The first added commit is correct' +} + +# Do the subrepo push and test output +{ + message=$( + cd "$OWNER/foo" + git subrepo push bar 2>&1 || true + ) + + is "$message" "git-subrepo: There are new changes upstream, you need to pull first." \ + 'Subrepo push command output is correct' +} + +# Do the subrepo forced clone branch@commit3and test the output: +{ + clone_output=$( + cd "$OWNER/foo" + git subrepo clone -f -b bar1@${bar_commit3:0:8} "$OWNER/bar" + ) + + # Check output is correct: + is "$clone_output" \ + "Subrepo '$OWNER/bar' (bar1@${bar_commit3}) recloned into 'bar'." \ + 'subrepo clone command output is correct' + + is "$( + cd "$OWNER/foo" + git remote -v | grep subrepo/bar + )" \ + "" \ + 'No remotes created' +} + +# Check that subrepo files look ok: +gitrepo=$OWNER/foo/bar/.gitrepo +{ + test-exists \ + "$OWNER/foo/bar/" \ + "$OWNER/foo/bar/Bar" \ + "$gitrepo" +} + +# Test foo/bar/.gitrepo file contents: +{ + foo_clone_commit=$(cd "$OWNER/foo"; git rev-parse HEAD^) + test-gitrepo-comment-block + test-gitrepo-field "remote" "$OWNER/bar" + test-gitrepo-field "branch" "bar1" + test-gitrepo-field "commit" "$bar_commit3" + test-gitrepo-field "parent" "$foo_clone_commit" + test-gitrepo-field "cmdver" "$(git subrepo --version)" +} + +# Make sure status is clean: +{ + git_status=$( + cd "$OWNER/foo" + git status -s + ) + + is "$git_status" \ + "" \ + 'status is clean' +} + +# Make additional change in foo/bar +( + cd $OWNER/foo + echo "Added first change to foo/bar/Bar" >> bar/Bar + git add bar/Bar + git commit -m"Added first commit to foo/bar/Bar" + #foo_commit1=$(git rev-parse HEAD) +) &> /dev/null || die + +{ + cd $OWNER/foo + foo_commit1=$(git rev-parse HEAD) + is "$( + cd "$OWNER/foo" + git log -1 --oneline | sed -e 's/ .*//g' + )" \ + "${foo_commit1:0:7}" \ + 'foo: The first added commit is correct' +} + +# Do the subrepo push and test output +{ + message=$( + cd "$OWNER/foo" + git subrepo push bar 2>&1 || true + ) + + is "$message" \ + "Subrepo 'bar' pushed to '$OWNER/bar' (bar1)." \ + 'Subrepo push command output is correct' +} + +# Do the subrepo pull branch and test the output: +{ + pull_output=$( + cd "$OWNER/foo" + git subrepo pull -b bar1 bar + ) + + # Check output is correct: + is "$pull_output" \ + "Subrepo 'bar' is up to date." \ + 'subrepo pull command output is correct' +} + +# Do the subrepo pull branch@commit2 and test the output: +{ + pull_output=$( + cd "$OWNER/foo" + git subrepo pull -b bar1@${bar_commit2:0:8} bar 2>&1 | sed -e 's/not contain.*/not contain/' || true + ) + + # Check output is correct: + is "$pull_output" \ + "git-subrepo: Local repository does not contain" \ + 'subrepo pull command output is correct' +} + +# Do the subrepo clone branch@commit2 and test the output: +{ + clone_output=$( + cd "$OWNER/foo" + git subrepo clone -b bar1@${bar_commit2:0:8} "$OWNER/bar" 2>&1 || true + ) + + # Check output is correct: + is "$clone_output" \ + "git-subrepo: The subdir 'bar' exists and is not empty." \ + 'subrepo clone command output is correct' +} + +done_testing + +teardown