Bitbucket: Pull PR and commit local changes to remote forks

Bitbucket: Pull PR and commit local changes to remote forks

Working in a large repo, with multiple forks, committing to a main repo

As a maintainer of a very large repo, we ask our developers to use the Forking Workflow

This has some challenges, especially when reviewing PRs and committing changes to those PRs on the respective, remote forks.

After many months and lots of blog reading, - The Refspec review and trial and error, I think I finally figured out the best way to work with this forking strategy.

First things first, set your refspecs to pull down all the PRs to the main repo. I picked this up from Atlassian's blog - Pull Request Proficiency.

I went about it slightly differently from their post but it works just as well and it was fewer steps.

Add a git config for remote.origin.fetch This adds a local namespace so you can eventually checkout a pr with shorthand

git config add remote.origin.fetch +refs/pull-requests/*:refs/remotes/origin/pr/*

git pull origin <main branch> or whichever branch you are receiving the PRs

 git pull origin develop
remote: Enumerating objects: 501, done.
remote: Counting objects: 100% (326/326), done.
remote: Compressing objects: 100% (229/229), done.
remote: Total 248 (delta 170), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (248/248), 54.99 KiB | 1.37 MiB/s, done.
Resolving deltas: 100% (170/170), completed with 42 local objects.
From ssh://bitbucket.<your-domain>.com:<port>/<project>/<repo>
 * branch                  develop                      -> FETCH_HEAD
 * [new ref]               refs/pull-requests/1472/from -> origin/pr/1472/from
 * [new ref]               refs/pull-requests/1473/from -> origin/pr/1473/from
 * [new ref]               refs/pull-requests/1474/from -> origin/pr/1474/from
 * [new ref]               refs/pull-requests/1475/from -> origin/pr/1475/from
 * [new ref]               refs/pull-requests/1477/from -> origin/pr/1477/from

Now you can checkout the PR on your local machine with git checkout origin/pr/1474/from

git checkout origin/pr/1474/from
Note: switching to 'origin/pr/1474/from'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at <commit> fix spec validations

Now you're free to poke around, make changes, and commit changes to your local detached HEAD state

You'll notice here, git reminds you that you are in detached HEAD state on commit.

git commit -m "<commit message title>" -m "    <commit message body>[APIDESGN-1367]"
[detached HEAD <commit>] feat(api): update event-notifications schema
 7 files changed, 32 insertions(+), 384 deletions(-)
 delete mode 100644 <some-file-named-you-modified>

-- You ARE using <issue-keys> in your commit messages, RIGHT?!?!

Now comes the fun part, setting up the remote fork and pushing your local changes to the PR. Add a new remote and name it whatever you want. This is the fork where the PR originated. I usually use the PR number as the remote to remind me of the PR number

git remote add 1474 ssh://git@bitbucket.<your-domain>.com:<port>/<project>/<repo>.git

You can run git remote -vv to see all of your remote configs

git remote -vv
1474    ssh://git@bitbucket.<your-domain>:<port>/<fork-project>/<repo>.git (fetch)
1474    ssh://git@bitbucket.<your-domain>:<port>/<fork-project>/<repo>.git (push)
origin  ssh://git@bitbucket.<your-domain>:<port>/<main-project>/<repo>.git (fetch)
origin  ssh://git@bitbucket.<your-domain>:<port>/<main-project>/<repo>.git (push)

Now you can run git branch and notice you have the HEAD detached and your develop branch. (you may have additional branches)

git branch
* (HEAD detached from origin/pr/1474/from)

It's time to fetch the upstream repo to pull down the actual branch name on the remote fork

 git fetch 1474
remote: Enumerating objects: 54, done.
remote: Counting objects: 100% (54/54), done.
remote: Compressing objects: 100% (52/52), done.
remote: Total 60 (delta 28), reused 1 (delta 1), pack-reused 6
Unpacking objects: 100% (60/60), 16.54 KiB | 127.00 KiB/s, done.
From ssh://bitbucket.<your-domain>:<port>/<fork-project>/<repo>
 * [new branch]            develop    -> 1474/develop
 * [new branch]            feature/APIDESGN-1367-<branch-name> -> 1474/feature/APIDESGN-1367-<branch-name>

Now we know the name of the original branch from the fork, we can checkout and merge our new commits. Since I've committed changes to the detached HEAD, I need to merge that commit into the branch. You can work in detached HEAD state or you can work on the actual branch, I just happened to make some changes while poking around in detached state. The beauty of checking out the branch in this fashion is git automatically sets the new branch to track the upstream branch, which is linked to the original PR

 git checkout feature/APIDESGN-1367-<branch-name>
Warning: you are leaving 1 commit behind, not connected to
any of your branches:

  <commit> feat(api): update event-notifications schema

If you want to keep it by creating a new branch, this may be a good time
to do so with:

 git branch <new-branch-name> <commit>

Switched to a new branch 'feature/APIDESGN-1367-<branch-name>'
branch 'feature/APIDESGN-1367-<branch-name>' set up to track '1474/feature/APIDESGN-1367-<branch-name>'.

Checking git branch -vv once more indicates the new branch is tracking the upstream we defined as 1474

git branch -vv
  develop                                    <commit> <commit-message>
* feature/APIDESGN-1367-<branch-name> <commit> [1474/feature/APIDESGN-1367-<branch-name>] fix spec validations

I opted not to create a new branch as suggested by the git cli but rather merge my previous commit on the detached HEAD to the branch we just checked out

git merge <commit-from-detached-head>
Updating <commit>..<commit>
 ...<some-modified-files>     | 104 +---------
 7 files changed, 32 insertions(+), 384 deletions(-)

Now we are ready to push our local changes to the upstream remote fork, but first let's check the status to see where we are. Oh look! Git recognizes we are now 1 commit ahead of our upstream remote fork branch

git status
On branch feature/APIDESGN-1367-<branch-name>
Your branch is ahead of '1474/feature/APIDESGN-1367-<branch-name>' by 1 commit.
  (use "git push" to publish your local commits)

OK, let's push! We use the upstream remote name and the branch name to tell git where we want to go and where the changes were made

git push 1474 feature/APIDESGN-1367-<branch-name>
Enumerating objects: 24, done.
Counting objects: 100% (24/24), done.
Delta compression using up to 8 threads
Compressing objects: 100% (12/12), done.
Writing objects: 100% (13/13), 1.44 KiB | 491.00 KiB/s, done.
Total 13 (delta 8), reused 0 (delta 0), pack-reused 0
remote: View pull request for feature/APIDESGN-1367-<branch-name> => develop:
remote:   https://bitbucket.<your-domain>.com/projects/<project>/repos/<repo>/pull-requests/1474
To ssh://bitbucket.<your-domain>.com:<port>/<project>/<repo>.git
   <commit>..<commit>  feature/APIDESGN-1367-<branch-name> -> feature/APIDESGN-1367-<branch-name>