- 1. Create a Bitbucket Account
- 2. Create a Public Repository (you)
- 3. Push to a Public Repository (you)
- 4. Browse the Public Repository (you)
- 5. Clone the Repository (John
- 6. Add the Pink Page (John)
- 7. Publish the Pink Page (John)
- 8. View John's Contributions (you)
- 9. Integrate John's Contributions (you)
- 10. Publish John's Contributions (you)
- 11. Update Mary's Repository (Mary)
- 12. Update John's Repository (John)
- 13. Conclusion
Now that we know how to share information via a centralized workflow, we can appreciate some of the drawbacks of this collaboration model. While it may be convenient, allowing everyone to push to an "official" repository raises some legitimate security concerns. It means that for everyone to contribute content, they need access to the entire project.
This is fine if you re only interacting with a small team, but imagine a scenario where you are working on an open-source software project and a stranger found a bug, fixed it, and wants to incorporate the update into the main project. You probably don't want to give them push-access to your central repository, since they could start pushing all sorts of random snapshots, and you would effectively lose control of the project.
But, what you can do is tell the contributor to push the changes to their own public repository. Then you can pull their bug fix into your private repository to ensure it does not contain any undeclared code. If you approve their contributions, all you have to do is merge them into a local branch and push it to the main repository, just like we did in the previous model. You have become an integrator, in addition to an ordinary developer.
In this module, we will experience all of this first-hand by creating a free public repository on Bit-bucket.org and incorporating a contribution from an anonymous developer named John. Bitbucket is a DVCS hosting provider that makes it very easy to set up a Git repository and start collaborating with a team of developers.
You can choose any username for your account, but the email address should match the one you assigned to your git installation with git config in The basics. If you need to change your email, you can run another git config --global user.mail [email protected] command.
Thu Jun 18 ~/iOS/RysGitTutorialRepository/.git
$ git config --global user.email [email protected]
To create our first networked Git repository, log into your Bitbucket account, and navigate to Repositories - Create repository.
Use my git repository as the RepositoryName, and anything you like for the description field. Since this is just an example project, go ahead and uncheck the This is a private repository field. Select HTML/CSS for the Language field, then go ahead and click Create repository.
Essentially, we just ran git init --bare on a Bitbucket server. We can now push to and fetch from this repository as we did with central-repo.git in the previous module.
After initialization, Bitbucket offers some helpful instructions, but don't follow them just yet - we will through importing an existing repository in the next section,
Before populating this new repository with our existing my git repository project, we firs need to point our origin remote to the Bitbucket repository. Be sure to change the userName portion to your actual Bitbucket userName.
Thu Jun 18 ~/iOS/RysGitTutorial_Carlos
$ cd ../RysGitTutorialRepository/
Thu Jun 18 ~/iOS/RysGitTutorialRepository
$ git remote rm origin
Thu Jun 18 ~/iOS/RysGitTutorialRepository
$ git remote add origin https://[email protected]/C4rl0sS4nt14g0/rysgittutorialrepository.git
The utility of remotes should be more apparent than in previous modules, as typing the full path to this repository every time we needed to interact with it would be rather tedious.
To populate the remote repository with our existing code, we can use the same push mechanism as with a centralized workflow. When prompted for a password, use the one that you signed up with.
Thu Jun 18 ~/iOS/RysGitTutorialRepository/.git
$ git push origin master
To https://bitbucket.org/C4rl0sS4nt14g0/rysgittutorialrepository.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'https://[email protected]/C4rl0sS4nt14g0/rysgittutorialrepository.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
As you can see, and if you remember the creation a README file, the origin and the local is not the same, to overwrite what you have in you local repository pass the -f flag to push to force the push command.
Thu Jun 18 ~/iOS/RysGitTutorialRepository/.git
$ git push -f origin master
Enumerating objects: 84, done.
Counting objects: 100% (84/84), done.
Delta compression using up to 4 threads
Compressing objects: 100% (81/81), done.
Writing objects: 100% (84/84), 10.08 KiB | 449.00 KiB/s, done.
Total 84 (delta 37), reused 0 (delta 0)
To https://bitbucket.org/C4rl0sS4nt14g0/rysgittutorialrepository.git
+ bfa132d...450182a master -> master (forced update)
We should now be able to see our project on the bitBucket site. The Source tab displays all of the files in the project, and the commits tab contains the entire commit history. Note that the branch structure of the repository is also visualized to the left of each commit.
This repository now serves as the "official" copy our example website. We will tell everyone else to download from this repository, and we will push all the changes from our local my-git-repository to it. However, It is important to note that this "official" status is merely a convention. As the master branch is just another branch, our Bitbucket repository is just another repository according to Git.
Having both a public and a private repository for each developer makes it easy to incorporate contributions from third-parties, even if you have never met them before.
Next we are going to pretend to be John, a third-party contributor to our website. John noticed that we didn't have a pink page and, being the friendly developer that he is, wants to create one for us. We would like to let him contribute, but we don't want to give him push-access to our entire repository - this would allow him to re-write or even delete all of our hard word.
Fortunately, John knows how to exploit Bitbucket's collaboration potential. He will start by cloning a copy of our public repository:
Fri Jun 19 ~/iOS/RysGitTutorialRepository
$ cd ../RysGitTutorialRepository/
Fri Jun 19 ~/iOS/RysGitTutorialRepository
$ cd ../
I am going to follow the same pattern name used for all contributors, like RysGitTutorialMarysRepository and RysGitTutorialRepository, giving the name RysGitTutorialJohnsRepository
Fri Jun 19 ~/iOS
$ git clone https://bitbucket.org/C4rl0sS4nt14g0/rysgittutorialrepository.git RysGitTutorialJohnsRepository
Cloning into 'RysGitTutorialJohnsRepository'...
remote: Counting objects: 84, done.
remote: Compressing objects: 100% (81/81), done.
remote: Total 84 (delta 37), reused 0 (delta 0)
Unpacking objects: 100% (84/84), 10.08 KiB | 43.00 KiB/s, done.
Fri Jun 19 ~/iOS
$ cd RysGitTutorialJohnsRepository/
Fri Jun 19 ~/iOS/RysGitTutorialJohnsRepository
$
You should now have another copy of our repository called RysGitTutorialJohnsRepositor in the same folder as RysGitTutorialRepository. This is John's private repository - a completely isolated environment where he can safely develop the pink page. Let's quickly configure his name and email
Fri Jun 19 ~/iOS/RysGitTutorialJohnsRepository
$ git config user.name "John"
Fri Jun 19 ~/iOS/RysGitTutorialJohnsRepository
$ git config user.email [email protected]
Of course, John should be developing his contributions in a dedicated feature branch.
Fri Jun 19 ~/iOS/RysGitTutorialJohnsRepository
$ git checkout -b pink-page
Switched to a new branch 'pink-page'
In addition to being a best practice, this makes it easy for the integrator to see what commits to include. When John's done, he will tell us where to find his repository and what branch the new features resides in. Then, we will be able merge his content with minimal effort.
Create the file pink.html and add the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<title>The Pink Page</title>
<link rel="stylesheet" href="style.css" />
<meta charset="utf-8" />
</head>
<body>
<h1 style="color: #F0F">The Pink Page</h1>
<p>Pink is <span style="color: #F0F">girly,
flirty and fun</span>!</p>
<p><a href="index.html">Return to home page</a></p>
</body>
</html>
Add the pink page to the "Navigation" section in index.html
<li style="color: #F0F">
<a href="pink.html">The Pink Page</a>
</li>
Then, stage and commit the snapshot as normal.
Fri Jun 19 ~/iOS/RysGitTutorialJohnsRepository
$ git add pink.html index.html
Fri Jun 19 ~/iOS/RysGitTutorialJohnsRepository
$ git status
On branch pink-page
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: index.html
new file: pink.html
Fri Jun 19 ~/iOS/RysGitTutorialJohnsRepository
$ git commit -m "Add pink page"
[pink-page 51f9d9e] Add pink page
2 files changed, 18 insertions(+)
create mode 100644 pink.html
Now, John needs to publish this contributions to a public repository. Remember that we don't want him to push or our public repository, which is stored in his origin remote, which is stored in his origin remote. In fact, he can't push to origin for reasons we will discuss in a moment.
Instead, he will create his own Bitbucket repository that we can pull contributions from. In the real world, John would have his own Bitbucket account, but for convenience, we will just store his public repository under our existing account.
Once again, navigate to your Bitbucket home page and click Repositories then Create Repository to create Johns public repository. For the Name field, use RysGitTutorialJohnsRepositor
Back in John's private repository, we will need to add this as a remote. Remember to copy the link that Bitbucket provides you, and name it john-public
Fri Jun 19 ~/iOS/RysGitTutorialJohnsRepository
$ git remote add john-public https://[email protected]/C4rl0sS4nt14g0/rysgittutorialjohnsrepositor.git
This is where John will publish the pink page for us to access. Since he is pushing with HTTPS, he will need to enter the password for his bitbucket account (which is actually the password for your account).
Fri Jun 19 ~/iOS/RysGitTutorialJohnsRepository
$ git push john-public pink-page
Enumerating objects: 88, done.
Counting objects: 100% (88/88), done.
Delta compression using up to 4 threads
Compressing objects: 100% (85/85), done.
Writing objects: 100% (88/88), 10.48 KiB | 536.00 KiB/s, done.
Total 88 (delta 40), reused 0 (delta 0)
remote:
remote: Create pull request for pink-page:
remote: https://bitbucket.org/C4rl0sS4nt14g0/rysgittutorialjohnsrepositor/pull-requests/new?source=pink-page&t=1
remote:
To https://bitbucket.org/C4rl0sS4nt14g0/rysgittutorialjohnsrepositor.git
* [new branch] pink-page -> pink-page
All John needs to do now is tell us the name of the feature branch and send us a link to his repository, which will be:
https://[email protected]/C4rl0sS4nt14g0/rysgittutorialjohnsrepositor.git
Note that John used a different path for pushing to his public repository than the one he gave us for fetching from it. The most important distinction is the transport protocol: the former used https:// while the latter used http://. Accessing a repository over HTTPS (or SSH) lets you fetch or push, but as we saw, requires a password. This prevents unknown developers from overwriting commits.
On the other hand, fetching over HTTP requires no username or password, but pushing is not possible. This lets anyone fetch from a repository without compromising its security, In the integrator workflow, other developers access your repository via HTTP, while you publish changes via HTTPS. This is also the reason why John can't push to his origin remote.
Of course, if you are working on a private project, anonymous HTTP access would be disable for that repository.
Ok, we are done being John and we are ready to integrate his code into the official project. Let's start by switching back into our repository and adding John's public repository as a remote.
Sat Jun 20 ~/iOS/RysGitTutorialJohnsRepository
$ cd ../RysGitTutorialRepository/
Sat Jun 20 ~/iOS/RysGitTutorialRepository
$ git remote add john https://[email protected]/C4rl0sS4nt14g0/rysgittutorialjohnsrepositor.git
Sat Jun 20 ~/iOS/RysGitTutorialRepository
$ git fetch john
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 7 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (7/7), 1.86 KiB | 118.00 KiB/s, done.
From https://bitbucket.org/C4rl0sS4nt14g0/rysgittutorialjohnsrepositor
* [new branch] master -> john/master
* [new branch] pink-page -> john/pink-page
$ git branch -r
john/master
john/pink-page
origin/master
Sat Jun 20 ~/iOS/RysGitTutorialRepository
$ git log master..john/pink-page --stat
commit 51f9d9e84c2e10e5ac859f2c8a6c4ac66ed39b17 (john/pink-page)
Author: John <[email protected]>
Date: Fri Jun 19 16:17:28 2020 -0500
Add pink page
index.html | 3 +++
pink.html | 15 +++++++++++++++
2 files changed, 18 insertions(+)
You can visualize this history information as the following:
Let's take a look at his actual changes:
git checkout john/pink-page
Note: switching to 'john/pink-page'.
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 51f9d9e Add pink page
Open up the pink.html file to see if it is ok, remember that John is not a trusted collaborator, and we would normally have no idea what this file might contain. With that in mind, It is incredibly important to verify its contents. Never blindly merge content from a third-party contributor.
Assuming we approve John's updates, we are now ready to merge it into the project.
Sat Jun 20 ~/iOS/RysGitTutorialRepository
git checkout master
Sat Jun 20 ~/iOS/RysGitTutorialRepository
$ git merge john/pink-page
Updating 450182a..51f9d9e
Fast-forward
index.html | 3 +++
pink.html | 15 +++++++++++++++
2 files changed, 18 insertions(+)
create mode 100644 pink.html
Notice that is the exact same way we incorporated Mary's changes in the centralized workflow, except now we are pulling from and pushing to different locations.
Furthermore, John's workflow is just like ours: develop in a local, private repository, then push changes to the public one. The integrator workflow is merely a standardized way of organizing the collaboration effort - nothing has changed about how we develop locally, and we are using the same Git commands as we have been for the last few modules.
We have integrated John's contribution into our local git repository, but no one else knows what we have done, It is time to publish our master branch again.
Sat Jun 20 ~/iOS/RysGitTutorialRepository
$ git push origin master
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 611 bytes | 305.00 KiB/s, done.
Total 4 (delta 2), reused 0 (delta 0)
To https://bitbucket.org/C4rl0sS4nt14g0/rysgittutorialrepository.git
450182a..51f9d9e master -> master
Since we designated our public Bitbucket repository as the "official" source for our project, everyone (Mary and John) will now be able to synchronize with it.
Mary should now be pulling changes from our Bitbucket repository instead of the central one from the previous module. This should be fairly easy to her to configure.
Sat Jun 20 ~/iOS/RysGitTutorialRepository
$ cd ../RysGitTutorialMarysRepository/
Sat Jun 20 ~/iOS/RysGitTutorialMarysRepository
$
Sat Jun 20 ~/iOS/RysGitTutorialMarysRepository
$ git remote rm origin
Sat Jun 20 ~/iOS/RysGitTutorialMarysRepository
$ git remote add origin https://[email protected]/C4rl0sS4nt14g0/rysgittutorialrepository.git
Again, remember to change userName to your bitbucket account's name. For the sake brevity. We will do a blind merge to add John's updates to Mary's repository (normally, Mary should check what she is integrating before doing so).
Sat Jun 20 ~/iOS/RysGitTutorialMarysRepository
$ git checkout master
Already on 'master'
Sat Jun 20 ~/iOS/RysGitTutorialMarysRepository
$ git fetch origin
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (4/4), 591 bytes | 7.00 KiB/s, done.
From https://bitbucket.org/C4rl0sS4nt14g0/rysgittutorialrepository
* [new branch] master -> origin/master
$ git rebase origin/master
First, rewinding head to replay your work on top of it...
Applying: Add CSS styles for headings and links
For Mary, it does not really matter that the updates came from John. All she has to know is that the "Official" master branch moved forward, prompting her to synchronize her private repository.
John still needs to incorporate the pink page into his master branch. He should not merge directly from his pink-page topic branch because we could have edited his contribution before publishing it or included other contributions along with it. Instead, he will pull from the "official" master:
Sat Jun 20 ~/iOS/RysGitTutorialMarysRepository
$ cd ../RysGitTutorialJohnsRepository/
Sat Jun 20 ~/iOS/RysGitTutorialJohnsRepository
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
Sat Jun 20 ~/iOS/RysGitTutorialJohnsRepository
$ git fetch origin
From https://bitbucket.org/C4rl0sS4nt14g0/rysgittutorialrepository
450182a..51f9d9e master -> origin/master
$ git branch -r
john-public/pink-page
origin/HEAD -> origin/master
origin/master
Sat Jun 20 ~/iOS/RysGitTutorialJohnsRepository
$ git rebase origin/master
First, rewinding head to replay your work on top of it...
Fast-forwarded master to origin/master.
If John had updated master directly from his local pink-page, it could have wound up out-of-sync from the main project. For this reason, the integrator workflow requires that everyone pull from a single, official repository, while they all push to their own public repositories.
Using the integrator workflow, our private development process largely remains the same (Develop a feature branch, merge it into master, and publish it). But, we have added an additional task: incorporating changes from third-party contributors. Luckily, this does not required any new skills - just access to a few more remotes repositories.
While this setup forces us to keep track of more remotes, it also makes it much, much easier to work with a large number of developers. You will never have to worry about security using an integrator workflow because you will still be the only one with access to the "official" repository.
There is also an interesting side-effect to this kind of security. By giving each developer their own public repository, the navigator workflow creates a more stable development environment for open source software projects. Should the lead developer stop maintaining the "official" repository, any of the other participants could take over by simply designating their public repository as the new "official" project. This is part of what makes Git distributed version control system: There is no single central repository that Git forces everyone to rely upon.