atomic_push.sh 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. #!/usr/bin/env bash
  2. set -e
  3. if [ "$#" -eq 0 ]; then
  4. printf 'Usage: %s <origin> <branch> <branch> ...\n' "$(basename "$0")"
  5. exit 0;
  6. fi
  7. REMOTE="$1"
  8. WORKING_DIR=$(pwd)
  9. if ! git ls-remote --exit-code "${REMOTE}" > /dev/null 2>&1; then
  10. echo >&2 "Remote '${REMOTE}' is not valid."
  11. exit 1;
  12. fi
  13. if [ "$#" -lt 3 ]; then
  14. echo >&2 "You must specify at least two branches to push."
  15. exit 1;
  16. fi
  17. if ! git diff-index --quiet HEAD -- ; then
  18. echo >&2 "Found uncommitted changes in working copy."
  19. exit 1;
  20. fi
  21. ATOMIC_COMMIT_DATE="$(date)"
  22. for BRANCH in "${@:2}"
  23. do
  24. echo "Validating branch '${BRANCH}'..."
  25. # Validate that script arguments are valid local branch names
  26. if ! git show-ref --verify --quiet "refs/heads/${BRANCH}"; then
  27. echo >&2 "No such branch named '${BRANCH}'."
  28. exit 1;
  29. fi
  30. # Determine if the branch is checked out to a worktree directory
  31. WORKTREE_DIR=$(git worktree list | grep -F [${BRANCH}] | awk '{print $1}')
  32. # If the branch is checked out to a worktree directory change directory to there
  33. if [ -n "${WORKTREE_DIR}" -a "${WORKTREE_DIR}" != "${WORKING_DIR}" ]; then
  34. echo "Switching to ${BRANCH} worktree in ${WORKTREE_DIR}";
  35. cd ${WORKTREE_DIR}
  36. fi
  37. # Pull and rebase all branches to ensure we've incorporated any new upstream commits
  38. git checkout --quiet ${BRANCH}
  39. git pull "${REMOTE}" "${BRANCH}" --rebase --quiet
  40. PENDING_COMMITS=$(git log ${REMOTE}/${BRANCH}..HEAD --oneline | grep "^.*$" -c || true)
  41. # Ensure that there is exactly 1 unpushed commit in the branch
  42. if [ "${PENDING_COMMITS}" -ne 1 ]; then
  43. echo >&2 "Expected exactly 1 pending commit for branch '${BRANCH}' but ${PENDING_COMMITS} exist."
  44. exit 1;
  45. fi
  46. # Amend HEAD commit to ensure all branch commit dates are the same
  47. GIT_COMMITTER_DATE="${ATOMIC_COMMIT_DATE}" git commit --amend --no-edit --quiet
  48. # Change back to original working directory if necessary
  49. cd ${WORKING_DIR}
  50. done
  51. echo "Pushing to remote '${REMOTE}'..."
  52. git push --atomic "${REMOTE}" "${@:2}"