Git squash rebase Workflow
Rebase-style instead of merge-style - changes on your branch are squashed into a single commit, then rebased on top of the latest origin/master, before being pushed back to origin/master. This workflow results in a master branch that contains no merges, just a single line of commits.
You are concerned with two branches:
- your adhoc branch
 - main (or master)
 
There are no git-flow style develop, release, hotfix, etc branches.
The main branch is expected (must be) deployable at any time.
Commits should only end up in main if they leave main in a deployable
state.
Don’t push something to main if you don’t intend to deploy it to production relatively soon!
Procedure
Configure git push to use upstream, git config --global push.default upstream Otherwise you’ll get an error on coe-release
- git clone {repo_url}
 - Start a new adhoc branch tracking main, 
code-on FEAT-xyz(use issue reference) - Stage and commit locally, 
git commit -a -m "meaningful commit message". Repeat as necessary. - Push to remote with the same branch name as the current local branch. Create remote branch if it doesn’t exist.  
code-push - Create a pull request in Github.com parlance (or merge request in Gitlab)
 - Repeat git commit, code-push as necessary to gain PR approval
 - After PR is approved, squash multiple commits and rebase your branch on top of the latest main 
git pull --rebase(equivalent to git fetch + git rebase) - Force update safely remote adhoc branch, 
code-push --force-with-lease - Release to remote main, remove local and remote adhoc branch 
code-release 
references
- Detailed description of squash rebase workflow, https://medium.com/swlh/squash-and-rebase-git-basics-5cb1be1e0dac
 - Difference between ‘git merge’ and ‘git rebase’ https://stackoverflow.com/q/16666089
 - Squash commit and rebase https://medium.com/swlh/squash-and-rebase-git-basics-5cb1be1e0dac, differs from this article
 
Shell aliases, functions
function info() {
  echo "$(tput setaf 7)$1$(tput sgr0)"
}
function code-on() {
  # Checkout a new branch for a Jira issuea
  # params:
  #   $1 jira issue key
  #   $2 tracking branch (default: main)
  (
    set -e
    local upstream=${2:=main}
    local branch="${USER}-$1"
    info "creating local branch '$branch' which tracks 'origin/${upstream}'"
    (
      set -x
      git checkout -b $branch origin/${upstream}
    )
  )
}
function code-push() {
  # Push branch to the matching branch on the remote
  # Use this to update pull requests.
  (
    set -e
    local branch=`git rev-parse --abbrev-ref HEAD`
    [[ "$branch" == "main" ]] && echo "should be on feature branch" && return 1
    info "pushing to remote branch 'origin/$branch'"
    (
      set -x
      git push origin HEAD:$branch $@
    )
  )
}
function link-pr-jira() {
  # Tell company XXXXX Jira that we've created the PR and link it
  # Params:
  #   $1 The pull request url
  (
  local pr=$1
  if [ -z $pr ]; then
    info "pull request link not given, not linking to jira"
    return
  fi
  set -e
  local repo=`git remote -v | grep fetch | grep -o 'XXXXX/[^.]*' | cut -d '/' -f2`
  local branch=`git rev-parse --abbrev-ref HEAD`
  local issue=${branch#*-}
  local jirabase="https://jira.XXXXX.com/jira/rest/api/latest"
  local pr_description=${pr#*github.com/}
  info "linking pull request $pr to jira issue $issue"
  (
    set -x
    curl --netrc -X POST -H "Content-Type: application/json" $jirabase/issue/$issue/remotelink --data-raw "{\"object\": {\"url\":\"$pr\", \"title\": \"$pr_description\"}}"
  )
  )
}
function code-pr() {
  # Create a pull request from your branch to the upstream
  # Requires hub (tool from github)
  # Automatically uses the last commit message as the pr message
  (
  set -e
  local branch=`git rev-parse --abbrev-ref HEAD`
  local upstream=`git rev-parse --abbrev-ref HEAD@{upstream}`
  local short_upstream=${upstream#origin/}
  [[ "$branch" == "main" ]] && echo "should be on feature branch" && return 1
  code-push
  info "creating pull request from '$branch' to '$short_upstream'"
  (
    set -x
    local pr=`hub pull-request -h $branch -b $short_upstream -F <(git log -1 --pretty=%B)| grep -o 'http[^ ]*'`
    set +x
    #link-pr-jira "$pr"
  )
  )
}
function code-release() {
  # This will just push the code to the remote branch
  # and also to the upstream. Pushing to both locations
  # will cause the existing pr to close.
  # This will also remove the local and remote branches
  (
  set -e
  local branch=`git rev-parse --abbrev-ref HEAD`
  local upstream=`git rev-parse --abbrev-ref HEAD@{upstream}`
  [[ "$branch" == "main" ]] && echo "should be on feature branch" && return 1
  git fetch
  code-push
  info "preparing release of 'origin/$branch' into '$upstream'"
  git status
  sleep 3
  info "pushing to remote branch '$upstream'"
  (
    set -x
    git push origin HEAD:main
  )
  info "cleaning up"
  (
    set -x
    git checkout main
    git reset --hard origin/main
    git push origin :${branch}
    git branch -d ${branch}
  )
  )
}
function code-abandon-local() {
  (
  set -e
  local branch=`git rev-parse --abbrev-ref HEAD`
  local upstream=`git rev-parse --abbrev-ref HEAD@{upstream}`
  [[ "$branch" == "main" ]] && echo "should be on feature branch" && return 1
  [[ -z "$upstream" ]] && echo "sanity failure; upstream is empty?" && return 1
    (
      set -x
      git checkout main
      git branch -d ${branch}
    )
  )
}