Note: There is a separate workflow that you may encounter which uses branches within the same repository to achieve similar effects. There is a tutorial for that as well: https://github.com/pqz317/cnc_github_tutorial/blob/main/feature_branch_workflow.md
Welcome to the Forking Workflow tutorial! Here we will demonstrate a workflow that allows for collaborative development of a shared code repository. In brief, the "Forking Workflow" can be described as:
- Individual developers "fork" some shared repository
- Development occurs on individual, forked repositories
- Individual development gets integrated into shared repository through Pull Requests (PRs)
The Forking Workflow involves 3 different repositories:
- Upstream, or "Parent" Repo: This remote repository belongs to some organization that is not the individual developer. Maintainers of this repository control who gets to make changes, and can review/accept/decline and incoming changes through pull requests.
- Origin, or "Forked Remote" Repo: This remote repository is a fork of the "Parent", and belongs to the individual developer. Any changes made here are separate from the parent. The individual developer can create pull requests from this repo to the parent.
- Local Repo: This is the local repo on the device of the individual developer, and it is where development occurs. It is a clone of the "Forked Remote" repo.
The steps through this demo are:
- Fork this "Parent" Repo to create a "Forked" Repo
- Clone the "Forked" Repo to create a Local Repo
- Make local changes
- Push your changes to your "Forked" repo on Github
- Create a Pull Request to integrate your changes into the "Parent" repo
- Fetch changes from the parent repo to your local repo
- (Potentially) Resolve merge conflicts
Say there is an existing remote repository that belongs to some organization, and you'd like to start development on it, usually the first thing to do would be to create a "fork" of this repository for yourself. This provides you with a repository on Github that is a clone of its "parent", where you can do any type of development and testing on without affecting the parent.
For this tutorial, you can fork this repo with the "fork" button on Github:
You have now successfully "forked" the cnc_github_tutorial
repo, and created a new repo that is <YOUR_USERNAME>/cnc_github_tutorial
Your "forked" repo still only lives on Github, so to obtain a local copy of it and begin developing, run
git clone https://github.com/<YOUR_USERNAME>/cnc_github_tutorial.git
The URL can always be found under the "Clone" Section of the Github page of your "forked" repo:
Once cloned, you should have a directory cnc_github_tutorial
in your local filesystem. Run git status
to verify that the directory is a git repository, and that you are on the main
branch. An example output:
Patricks-MacBook-Pro:cnc_github_tutorial patrick$ git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
Note: origin/main
here refers to the main branch of the origin repo. main
is the main branch. We won't go into branching too much here, so just ignore that. Origin
is a name for the remote repository this local repository is cloned from, the "Forked" repo. Git by default keeps track of this remote repo after a git clone
command. It will also be helpul for git to keep track of another remote repo, the "Parent" repo. To configure this, run:
git remote add upstream https://github.com/uwcnc/cnc_github_tutorial.git
This will tell your local git repo about another remote repo, and name it upstream. This will come in handy when there are changes to the "Parent" repo you want to keep track of locally.
Open a new window and "Clone Git Repository":
Paste the clone URL and create a local directory cnc_github_tutorial
for the repository location:
Check/change what branch you're on in the bottom left corner:
Add the remote "Parent" repo by opening the Command Palette (Ctrl+Shift+P) and typing Git: Add Remote
:
Paste the parent URL (found under the "Clone" section on Github):
Name the "Parent" repo upstream
:
Now you have access to local, remote (origin), and remote parent (upstream) branches:
In your local repository, feel free to make any changes you'd like. For simplicity, you can create a new file <YOUR_NAME>_test.txt
and add some text to it. Once that is saved, remember to git add
and git commit
your changes:
git add .
This command adds all changes to staging. You can also choose to be more granular here and only add certain files, with git add <FILE_NAME>
.
Then:
git commit -m "create new file"
This command creates a commit from the changes in staging, and adds associated commit message.
Note about commit messages:
- Messages should be brief but informative. "fix bug" won't be too useful a year later.
- Current standard is to use imperative mood, "write an awesome function" instead of "wrote an awesome function"
- More info in this post: https://cbea.ms/git-commit/
Find the "Source Control" tab on the left panel and select the file you have created/modified to see a side-by-side comparison of the changes:
Use "+" to stage changes to specific files or stage all changes:
Add a commit message and use "✓" to commit the staged changes:
You've made and commited changes to your local repository, now its time to push your changes to your "Forked" remote repo on Github!
Run:
git push origin master
Doing so both backs up your changes, and gets you ready to create a pull request. Note again, origin here is referring to the forked remote repo.
"Sync Changes" on the "Source Control" tab or simply use the sync button in the bottom left corner:
Now, to integrate your changes into the "Parent" repo, you can create a Pull Request via Github.
Navigate to https://github.com/<YOUR_USERNAME>/cnc_github_tutorial. Under the "Pull requests" tab, make a new pull request and select the branches you want the pull from/to:
While you were busy working on your awesome feature, other changes might've been merged into "Parent" as well, so before working on anything else, its always good to have your local repo up-to-date with what's in "Parent". In Step 2, we already configured our local git repo to track the "Parent" repo as "Upstream", so now we can just do:
git fetch upstream # fetches upstream branches
git merge upstream/main # merges upstream/main into your local main branch
Alternatively, a git pull
command also works:
git pull upstream main
Now, any changes that have been merged into "Parent" should also be reflected in your local repo.
On the "Source Control" tab, pull from the main branch of upstream
:
Your local repo is now up-to-date with the "Parent" repo. Sync these changes to make your remote repo up-to-date with "Parent" as well.
Merge conflicts arise when Git doesn't know how to reconcile conflicting changes occuring on the same file. In these situations, Git leaves it to the user to figure out how to best resolve the conflicting changes. For this example, to create a merge conflict, navigate to your local repo and run:
git pull upstream patrick_branch
Don't worry too much about the specifics of this command for now. You should see some text:
Auto-merging some_file.txt
CONFLICT (content): Merge conflict in some_file.txt
Automatic merge failed; fix conflicts and then commit the result.
Opening up some_file.txt in any text editor, you should see something like:
This file contains some text
<<<<<<< HEAD
Some change katherine has made
Another change katherine has made
=======
Some change patrick has made
>>>>>>> b49f523264642383fb9c61bda4dcd0348f11d695
The text within the <<<<<<<
and the >>>>>>>
are the conflicting areas.
Anything between <<<<<<< HEAD
and =======
represent what you had in the file, before trying to merge in any new changes.
Anything between =======
and >>>>>>>
represent what the incoming changes are.
You can choose to accept what you had, the incoming changes, both, or a mixture of the two.
For our example, let's choose to accept both changes, so remove the any lines with <<<<<<< HEAD
, =======
and >>>>>>>
Your file should now look like:
This file contains some text
Some change katherine has made
Another change katherine has made
Some change patrick has made
Now, to finish up the merge, we need to add/commit our changes.
git add . && git commit -m "resolve merge conflict, keep both changes"
Your merge conflict has been successfully resolved!
On the "Source Control" tab, pull from the patrick_branch of upstream
:
Use "Compare Changes" to get a side-by-side comparison of the changes (current changes in red, incoming changes in green):
For our example, let's choose to accept both changes:
You have successfully merged the changes in your local repo! Be sure to stage, commit, and sync the changes so they are reflected in your remote repo.