Git is a great tool, but its documentation leaves much to be desired at times. Bitbucket's documentation doesn't fare much better. Commands mentioned in its wiki often don't work as advertised. The fact that git's commands are often incredibly counter-intuitive, incomplete and at times simply wrong doesn't help either. So while you can get far with git by just copying random (and again, wrong more often than not) commands from Stack Overflow, there comes a time where you actually have to learn how it works.
In my case, I had a very simple request. Somebody opened a pull request from a fork of one of my projects and I simply wanted to test that change before I merged it. Bitbucket's wiki was unhelpful as the commands it listed simply didn't work. Here's how I got it to work in a way that actually makes sense.
What we'll be doing
We'll be working with two repositories here:
- The main repository called "test", owned by user "fboender"
- The forked repository called "test-fork", also owned by user "fboender". Normally the forked repository wouldn't be owned by the same person, but in Bitbucket you can fork your own repositories, and this makes for a good test.
- A pull request containing two commits on a branch called 'bugfix' on the forked 'test-fork' repository.
- We want to review these commits, test them and either merge them into our main repository 'test' or reject them.
We'll perform the following steps:
- Prepare the working directory
- Retrieve the remote changes (commits) for the pull request to our local clone
- Review the changes
- Either reject or accept (merge) the changes
- Push the accepted changes (merge / pull request) back to Bitbucket.
Prepare the working directory
Make sure you have no uncommited changes in your working dir:
$ git status # On branch master nothing to commit, working directory clean
If there are uncommited changes, either commit them or get rid of them.
Retrieve remote changes
Next we'll retrieve the remote changes introduced by the pull request. We don't want those changes to affect our local repository in any way. We just want to retrieve them so we can review the changes. To do so, we'll need to know the source from where we want to fetch the changes. This is what the pull requests looks like in the Bitbucket interface:
If you hover your mouse over the branch, you can see the URL for the source of the merge request. In this case:
We'll use git's fetch command to fetch the objects (commits) in the pull request. The format for the fetch command in this case is:
git fetch <repository> <refspec>
For our pull request the repository is
https://bitbucket.org/fboender/test-fork. The refspec can be almost anything really. A commit, a branch name, whatever. In this case we'll use the branch name, which is
bugfix, as you can infer from the URL above.
$ git fetch https://bitbucket.org/fboender/test-fork bugfix remote: Counting objects: 13, done. remote: Compressing objects: 100% (7/7), done. remote: Total 8 (delta 2), reused 0 (delta 0) Unpacking objects: 100% (8/8), done. From https://bitbucket.org/fboender/test-fork * branch bugfix -> FETCH_HEAD
Git has now retrieved the commits and put them in our local index. If you wouldn't know better, you wouldn't be able to find them though! The commits are there, but they're not part of any branch or anytihng. To get to the changes, we'll need to use the last commit id (ad38a9b) or the (temporary) FETCH_HEAD ref, which git has created for us.
Review the changes
Okay, so we want to do various reviews of the changes. First let's look at the changes between our current branch (master) and the new changes:
$ git diff master FETCH_HEAD diff --git a/README.md b/README.md index 08cdfb4..cc11a2f 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,3 @@ About This is a test repository, for testing. -TEST in sub diff --git a/TEST b/TEST index 3fb25cd..bc440ae 100644 --- a/TEST +++ b/TEST @@ -1 +1,2 @@ BLA +FOO
Looks good. Two commits that ammend the TEST file and update the README.md file. Now perhaps we want to run some tests on the new changes before accepting and merging the pull request. To get to the actual changes, we can checkout a refspec. In this case either the FETCH_HEAD refspec that git helpfully created for us, or we can use the last commit in the pull request: ad38a9b. We'll be using FETCH_HEAD. Remember that this refspec is temporary! If you do a new fetch (that includes git pull and various other commands), the FETCH_HEAD could have changed.
$ git checkout FETCH_HEAD Note: checking out 'FETCH_HEAD'. 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 performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b new_branch_name HEAD is now at ad38a9b... Updated README
Okay, seems to have worked. The git log shows the commits:
$ git log commit ad38a9b402eac93995902560292697245418a192 Author: Ferry Boender <firstname.lastname@example.org> Date: Mon Mar 31 19:37:26 2014 +0200 Updated README commit c538608863dd9dda276edf5adcad9e0f2ef9f9ed Author: Ferry Boender <email@example.com> Date: Mon Mar 31 19:37:11 2014 +0200 Ammended TEST file commit f8d3d31ea1195e2cb1c0631d95c2b33c313b60b8 Author: Ferry Boender <firstname.lastname@example.org> Date: Mon Mar 31 17:36:23 2014 +0000 Created new branch bugfix
We can now run some tests on the working directory, further inspect the changes, etc.
Reject the changes
If you're not satisfied with the changes, you can just check out the original branch:
$ git checkout master Warning: you are leaving 3 commits behind, not connected to any of your branches: ad38a9b Updated README c538608 Ammended TEST file f8d3d31 Created new branch bugfix If you want to keep them by creating a new branch, this may be a good time to do so with: git branch new_branch_name ad38a9b Switched to branch 'master'
Note that the commits are still in your index. You've just chosen not to do anything with them. You can now reject the pull request in Bitbucket's interface.
Accept the changes
If you're satisfied that the changes made work properly and want to keep the changes from the pull request, you can merge them into a branch. In this case, we'll merge them directly into the master branch.
First, we reattach our HEAD to the correct branch:
$ git checkout master
Next, we merge in the changes:
$ git merge FETCH_HEAD Updating 2f6ecbf..ad38a9b Fast-forward README.md | 1 - TEST | 1 + 2 files changed, 1 insertion(+), 1 deletion(-)
Finally, we push the changes to Bitbucket. This will automatically accept the pull request on Bitbucket.
$ git push Counting objects: 13, done. Delta compression using up to 4 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (8/8), 873 bytes | 0 bytes/s, done. Total 8 (delta 2), reused 1 (delta 1) To email@example.com:fboender/test.git 2f6ecbf..ad38a9b master -> master
The changes have now been pushed to the origin repository and the pull request has been accepted automatically by Bitbucket.
That's it, we're done!
Make "changes" to the pull request
Suppose you find the pull request is largely okay, however it's missing some small things. Maybe the developer who forked didn't update the documentation to reflect the changes he made? Such a hypothetical situation would of course never happen in real life, but let's suppose it does! How do you go about fixing the pull request which still properly accepting it? Here's how:
We've fetched the remote changes and are now inspecting them in our working dir:
$ git fetch https://bitbucket.org/fboender/test-fork bugfix ... $ git checkout FETCH_HEAD ...
Everything is almost correct, but we need to make some minor changes before accepting the merge request. We can now simply create a new commit in the detached head state:
$ echo "QUUX" >> TEST $ git add TEST $ git commit -m "Fixed missing ammend" [detached HEAD ab11d9a] Fixed missing ammending 1 file changed, 1 insertion(+)
This gives us a new commit id "ab11d9a" which we can use to merge both the commits from the pull request as well as the new commit:
$ git checkout master Warning: you are leaving 2 commits behind, not connected to any of your branches: ab11d9a Fixed missing ammending e761f43 Ammended TEST some more $ git merge ab11d9a Updating ad38a9b..ab11d9a Fast-forward TEST | 2 ++ 1 file changed, 2 insertions(+) $ git push
The merge request will now automatically be accepted and our additional commit will also be pushed to the main repository. Note that this doesn't actually makes any changes to the pull request. It's just an additional commit that you're merging in at the same time as the commits from the pull request. This is useful to prevent broken code on branches.