Skip to content

Instantly share code, notes, and snippets.

@wooters
Last active September 18, 2021 18:51
Show Gist options
  • Save wooters/82b600cf0cdc61a6dc78 to your computer and use it in GitHub Desktop.
Save wooters/82b600cf0cdc61a6dc78 to your computer and use it in GitHub Desktop.
Notes on developing code collaboratively using git

Git+GitHub Collaboration

This gist provides some info on how to collaborate on software projects using git+GitHub. It is focused on the Fork & Pull collaboration model. This model is described as:

In the fork and pull model, anyone can fork an existing repository and push changes to their personal fork without needing access to the source repository.

Note: I mostly use GitHub. I haven't verified that these instructions will work for other hosting services such as GitLab. I suspect they will but I'd love some feedback on this.

Definitions

For me, the terminology was one of the most confusing things about learning to collaborate on git+GitHub. I'm not sure if the definitions I give below are "standard" (again feedback welcome!). But for the sake of clarity, these are the definitions I'll use here.

  • repository (or repo)

    n.: A location for software or documents, typically hosted in GitHub or GitLab. "My code is stored in a repo on GitHub."

  • fork

    n.: A copy of a repo that you created by forking that repo in GitHub. "My fork of that repo got munged."

    v.: To make a copy of a repo. "Feel free to fork my repo on GitHub."

  • clone

    n.: A local copy of a repo. "I added some files to my clone of the repo." I've heard people refer to a clone as a repo. But for this gist, I'll try to keep them separate to make it clear what I'm referring to.

    v.: To make a copy of a repo to your local machine. "I forked your repo, but I haven't yet cloned it to my dev box."

  • remote

    n.: The location of a repo or a fork. The url of a remote is added to a clone and a clone can have more than one remote. "Here's the url of my repo that you can add as a remote for your clone."

    A remote has a name. Two commonly used names for remotes are "origin" and "upstream". "origin" usually refers to the fork from which you generated your clone, and "upstream" typically refers to the original GitHub repo from which you generated your fork:

    +----------+
    | original |              +----+
    |   repo   |------------->|fork|
    +----------+              +----+
          ^                      ^
          |                      |  "origin"
          |                      |
          |     "upstream"    +-----+
          +-------------------|clone|
                              +-----+
  • branch

    n.: Development in git happens on a branch. The metaphor is that a repo is a tree and modifications to the repo happen on its branches. We create a new branch from an existing branch. Once a new branch has been created, changes made to that branch won't affect any other branch, including the branch from which it originated. (The tree metaphor is not perfect because in git we can merge two branches.)

    Each branch must be given a name. Here is GitHub's branch naming guideline. There is one branch, the "main" or "master" branch, that is special. Well, actually, there is nothing special about this branch. Its only special by convention. The convention is that the "main" branch should always contain a stable, buildable version of the code. (So don't develop on the "main" branch!) Before code from a branch is merged into "main", it should be reviewed by one or more developers familiar with the repo. In addition to human review, automated tests are often run to make sure the code is as bug-free as possible.

Collaboration Details

Let's say someone has a repo on GitHub and you'd like to contribute. The first step in the "Fork & Pull" collaboration model is to fork their repo. This is easy -- just click on the "Fork" button on their repo's GitHub page.

Once you have forked the repo you have your own copy and are can begin making changes to the code. To get started, run the following on your development machine:

git clone https://github.com/YOUR-GITHUB-USER-NAME/REPO-NAME.git

Replacing "YOUR-GITHUB-USER-NAME" with your GitHub user name, and "REPO-NAME" with the name of the repo you forked. This will create a clone of your fork. The clone will be put into a subdir whose name is the same as the REPO-NAME.

Before you begin working with your clone, do the following:

cd REPO-NAME
git remote add upstream https://github.com/ORIGINAL-AUTHOR-GITHUB-USER-NAME/REPO-NAME.git

This will add a new remote called "upstream" to your clone. So your clone will now have two remotes: "origin" and "upstream". The "origin" remote refers to your fork of the original repo and "upstream" refers to the original repo you forked from. I'll talk about why we need the "upstream" remote below.

Making changes and saving them to "origin"

  1. Before changing any code, create a new branch:

    $ git checkout -b BRANCH-NAME

    This says to create and then switch to a new branch named "BRANCH-NAME".

  2. Make some changes to the code. For example, modify the .gitignore file:

    $ vim .gitignore # make changes and quit vim

  3. Add and commit the changes:

    $ git add .gitignore

    $ git commit -m "your commit message here"

  4. Push the changes to "origin" (your fork):

    $ git push origin BRANCH-NAME

Getting your changes to "upstream"

This is also known as creating a "pull request". Why is it called a "pull" request? Because you are asking the owner of the original "upstream" repo to consider "pulling" your changes into their repo.

After you completed step 4 above, your changes will live in your fork as well as in your local clone. If you view your fork on GitHub, it will show you your recently-pushed branch (BRANCH-NAME). Now you can click on the "Pull Request" button to create the pull request.

Keeping your fork up to date

You're probably not the only person generating pull requests to the original "upstream" repo. So to update your fork you can run these commands in your local working dir:

git checkout master
git fetch upstream
git merge upstream/master
git push origin master 

This will checkout the "master" branch in your clone (just in case you happened to be on another branch), fetch and then merge changes from the "master" branch in the "upstream" repo, and then push all of this new code to the "master" branch in your fork ("origin").

Other Git Notes

Good Git Guides

Git Submodules

Tagging

Creating an annotated tag in Git is simple. The easiest way is to specify -a when you run the git tag command:

$ git tag -a v1.4 -m 'my version 1.4'
$ git tag
v0.1
v1.3
v1.4

The -m specifies a tagging message, which gets stored with the tag. If you don't specify a message on the command line, git launches your editor so you can add a message.

You can see the tag data along with the commit that was tagged by using the git show command.

Note that by default the git push command does not transfer tags to remotes. So you must push the tag like so:

git push origin tag TAG-NAME

Rebasing

Assume the following history exists and the current branch is called "topic":

                 A---B---C topic
                /
           D---E---F---G master

From this point, the result of the following commands:

git checkout topic 
git rebase master

would be this:

                         A'--B'--C' topic
                        /
           D---E---F---G master

Note: you can also rebase from the "upstream" master branch with:

git rebase upstream/master

In case of a conflict, git rebase will stop at the first conflict and leave markers in the code. You can use git diff to locate the markers <<<<<< and make edits to resolve the conflicts.

For each file you edit/fix, you need to tell Git that the conflict has been resolved. This can be done with:

git add FILENAME

After resolving the conflicts, you can continue the rebasing process with:

git rebase --continue

Alternatively, you can undo the git rebase with:

git rebase --abort

Discarding unstaged changes

git stash
git pull
git stash drop

Pull vs Fetch

git pull does a git fetch followed by a git merge.

Working on someone else's pull request

If someone has created a pull request against the "upstream" repo and you'd like to make changes to the branch from which they issued the pull request, you can do:

git fetch upstream
git checkout BRANCH-NAME

make changes and then:

git push upstream BRANCH-NAME

Working with remotes

Show all remotes

git remote -v

Add a new remote

git remote add REMOTE-NAME https://github.com/ORIGINAL-AUTHOR-GITHUB-USER-NAME/REPO-NAME.git

Get more info about a remote

git remote show REMOTE-NAME

Rename a remote

git remote rename OLD-REMOTE-NAME NEW-REMOTE-NAME

Remove a remote

git remote rm REMOTE-NAME

Change the URL of a remote

git remote set-url REMOTE-NAME NEW-GIT-URL

Fetching from a remote (doesn't merge)

git fetch REMOTE-NAME
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment