Skip to content

Latest commit

 

History

History
606 lines (484 loc) · 26.8 KB

File metadata and controls

606 lines (484 loc) · 26.8 KB

Working with Git and GitHub

Git and GitHub are separate tools: Git is a software package for tracking versions of files, often used for source code management, and GitHub is a commercial website that houses "repositories" of versioned files relating to numerous different projects, including Numberscope. Since there is some overlap when it comes to working with them, though, we describe the Git and GitHub operations relevant to Numberscope in this same document.

Contents

Basic operations

When using Git, it can be helpful to think of it as a photographer who stages your work ("adds" changes), takes a snapshot of it ("commits" changes), and stores the photo somewhere safe ("pushes" changes to e.g. GitHub).

Clone a repo

As described in the onboarding page, to run the frontscope user interface from its source code and/or to set up for making and then submitting changes to the code, you need a copy of all the code (and the previous snapshots of it as it developed) on your computer. That's called "cloning" the repository.

To do this, make sure you are in a directory on your computer where you are comfortable with new subdirectories being created for the project(s) you clone. (Of course, you must also have the git software installed.) Then issue a command that looks like this:

git clone [URI_of_repository]

Note that [URI_of_repository] is a placeholder for information that you need to fill in, not something you type as is. In general, we will put placeholders like this in brackets [] so you know what to fill in.

How do you find the URI of the repository you want? In the case it is hosted on GitHub, as frontscope is, you navigate to the repository page (the one just linked). In the center of the page left-to-right and near the top of the page, you will see a bright green "Code" button. Click on that button, and it will give you a choice of two different URIs to clone the repository with:

  1. via HTTPS: This is an unauthenticated connection to the GitHub server, so it is easiest to use for download. However, you cannot submit code changes via an HTTPS URI. That's not a problem for the standard repository for frontscope, because you never submit changes directly to the standard repository. Instead, you will create what's called a "fork" and submit your changes through the fork. So bottom line, we recommend you clone frontscope via its HTTPS URI, which is https://github.com/numberscope/frontscope.git.
  2. via SSH: This method of connecting is authenticated and will allow you to upload changes to GitHub. It requires some setup. If you have created a clone of frontscope for the purpose of submitting code, you will have to connect to it via an SSH URI, which has the format git@github.com:[USER]/[REPO].git.

The "Code" button will also show you a command you could use to clone the repository via the "GitHub Command-Line Interface" (or "GitHub CLI"). That method requires installing additional software. If you're comfortable with the GitHub CLI, you can use it for several of the tasks on this page, but we won't be going into the details of it here.

To sum this all up for the case of frontscope, we recommend you clone it via git clone https://github.com/numberscope/frontscope.git.

Display status

One of the main uses of Git is to facilitate different people working together by creating (as it were) different albums of photos of your work (called "branches"). The "official" version of frontscope (or backscope) is in the branch called "main" in its standard repository (as just linked to). But as we will see, you can create new branches for your personal project that involves modifying the source code, to make sure that your work doesn't interfere with anyone else's.

But first, you will often need to see what the situation is with your copy of frontscope. Let's assume from now on that you have cloned a repository and your current directory is within the "working directory" of that clone. (In other words, if you have just cloned frontscope, say, then you would need to

cd frontscope

before executing any of the commands in the rest of this page.)

OK, so if you are in the working directory of your clone and you want to see what the situation is, execute

git status

This command will tell you what "branch" of the repository you are currently working in, and it will let you know about any differences between the files in that working directory and the last snapshot, i.e. "commit", of the project.

For example, if you execute git status immediately after cloning the frontscope repository, you will see:

On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

On the other hand, if you had switched to a different branch (more about how to do that in a moment), modified one file and created one new one, you might see something like this:

On branch git_docs
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   doc/working-with-git-and-github.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	doc/example-documentation.md

no changes added to commit (use "git add" and/or "git commit -a")

Note that we have used a different branch in this example because you should never make changes directly to main. Always create and work in a different branch.

Create a branch

To create a branch and switch to it in one command, first make sure (for example, using git status) that you are "on" the branch that you want your new branch based on. That's sort of the "parent" of the new branch -- in other words, the baseline that your branch will record a series of changes to. Typically for working on Numberscope, that base will be the main branch, so make sure that's checked out. Then issue the following command:

git checkout -b [your_new_branch_name]

(At Numberscope we use snake_case for branch names.) Once you've done this, you have your own "little world" where you can make changes without affecting anyone else. Go ahead and modify files or add new ones to get the visualization you want!

Add changes

At some point you will have something new and/or improved working, and you will want to get ready to take a snapshot (i.e. make a "commit" to your branch) to possibly share or submit to Numberscope, or just as a checkpoint of what you have done so far.

For this, you must set the stage for this commit. To "add" one file you have been working on to the commit that's about to be made, issue the following command:

git add [path/to/file]

Note that the paths are relative to the top-level directory of the working tree, i.e. to the directory named frontscope in our case. For example, the path of this documentation file is doc/working-with-git-and-github.md.

You need to do this for each file you want to stage for this commit; or, see the next section for a faster option you can sometimes use.

Commit changes

To take a snapshot of the changed files you have added (i.e, "commit" them), issue the following command:

git commit -m "[put your commit message here]"

As a shortcut, you can add and commit all of the changed files in your working tree with

git commit -a -m "[put your commit message here]"

Note that this latter command does not include any new ("untracked") files. Those you must explicitly add with the git add command. That's to make sure that you don't inadvertently put some temporary junk file you may have made into the repository.

You should write your commit messages according to the "semantic commit message" guidelines.

If your changes are more extensive than can be comfortably described in a single sentence, leave off the -m "[message]" part of the command. Then an editor will pop up where you can insert the description of your changes, Start with one summary line in the semantic commit message format, then skip a line, and go into more detail in paragraph(s) below. When you save the message file, the commit will complete. In these descriptions, always describe the commit in present tense: "This commit implements a frobozzinator", not "This commit implemented a frobozzinator". Having them in a uniform format makes the eventual git log of changes much easier to read.

Note that if you are working on frontscope and you have installed the standard pre-commit actions (as will happen automatically if you execute the usual npm install when you start working with your clone), then before the commit actually occurs, git will run several code integrity checks. If any of the checks fail, creating the commit will be blocked until you have further modified the code so that they pass. Making these fixes may be very simple: for example, there is a code formatting check, and executing npm run lint to run Numberscope's standard "prettyprinting" utility may well resolve any problems it is reporting. Or the issue may be more involved: one of the unit tests or end-to-end tests may have uncovered a bug you've inadvertently introduced. See the page on code tests for more information.

Create a fork

Now that you have a new snapshot of your work, you might like a safe place to put it, and/or you may want to submit what you've done for consideration to be incorporated into the official Numberscope (that's called "making a pull request"). Either way, you'll need to create a "fork" of the frontscope repository, since you are not allowed to submit directly to the official repository.

Creating a fork of a repository is like creating your own personal copy of the entire repository, including all present and past snapshots of the code. It's actually essentially the same thing as cloning the repository, except that the clone is housed on GitHub's servers rather than on your own local machine. It also adds functionality that makes it easier to keep your fork synced with the original and submit pull requests.

Note: This is a GitHub operation, not a Git operation. So the first thing you will need is a GitHub account. Once you have that and are logged in:

  1. Go to the page of the repository you want to fork. For instance, here's a link to the numberscope/frontscope repository.
  2. In the upper right corner of the page (as of this writing) there should be a button that says "Fork". Click that button.
  3. Follow the instructions that GitHub provides. Make your GitHub account the owner of the fork.

Add a remote

Now back on your local machine, you'll want your clone to be able to communicate not just with the standard Numberscope repository from which it was cloned, but also with your new fork. That's called "adding a remote". First, you'll need to grab the SSH URI for your fork from its page on GitHub, see the description of URIs in the section on cloning a repository. Then execute the following command

git remote add [name_of_remote] [SSH URI for remote]

You can pick whatever name you want to use for this remote; we typically use the name "fork" for the fork that we will primarily be submitting ("pushing") commits to. So if your user name on GitHub is "somebody", your command might look like

git remote add fork git@github.com:somebody/frontscope.git

Push a branch

Now you're finally ready to upload your snapshot, or in git lingo, "push your branch". Before you are able to do this, you either need to install the GitHub command line interface (CLI) and log in using the GitHub CLI or install an SSH key.

Presuming that you have not previously pushed anything from your current branch to your fork, you will want to create a corresponding branch in that remote git repository and set your local branch to "track" or correspond to that remote branch. You do this with the following command, that also pushes all of the changes on the current branch to the remote:

git push -u [name_of_remote] [name_of_branch]

So for example, when first pushing the branch with the changes to this guide, the command used was git push -u fork git_docs.

Push changes

If you have already pushed a given branch and set it track the remote branch as in the last section, then when you have made further commit(s) that you want to upload, just execute:

git push

Git remembers the remote and branch you want to push to (assuming you used the -u option earlier) and also what commit(s) were uploaded before, and only pushes the new ones.

Submit a pull request

Once you have a branch in working order that implements one new feature or fixes one bug or otherwise changes Numberscope in a coherent way, and that branch is pushed to your fork in the state you want to propose for inclusion in Numberscope, the first thing to do is to carefully go through the pull request checklist. This is a GitHub functionality, not one intrinsic to git.

Presuming that you have satisfied all of the guidelines in the checklist file, it's time to submit a "pull request" (PR). To do so, go to your fork on the GitHub website, and select your branch in the dropdown list on near the top left. Then in the bar just below that dropdown list, you should see information on how your branch compares with the current "main" branch of Numberscope, in terms of the number of commits it is "ahead" of main (i.e., has added since the last common commit to main) and the number of commits "behind" main (i.e., the number of commits added in main since the last common commit). Ideally, your branch is not behind main at all; otherwise, maybe some of those commits in main might affect the changes you were working on. So if you see any commits behind main, you might want to go back to your working copy and sync your clone with the original and then rebase your branch on the current version of main. If you submit a PR that is not based on the current version of the main branch, you run the risk that it may have "merge conflicts" in which both you and a newer version of main have modified the same or nearby sections of code. Such merge conflicts will prevent your PR from being accepted until the are resolved by rebasing on the latest version of main.

In any case, when you are satisfied your branch is ready to submit as a PR, you will notice that report of the number of commits ahead or behind main is actually a link. Click on that link, and you will reach a page that shows the relevant commits and all of the changes that have been made in your branch. Look these over one last time to make sure they are the changes you intend. If not, go back to your branch and fix up whatever was awry and push it again.

When all of those changes look right, click the big green button at the top right that says "Create Pull Request". This click will take you to the "Open a Pull Request" form. (Note that the first time you push a branch to git, the response message actually includes a quick direct link to this form; you can save that link and use it later, instead of navigating through GitHub's web interface.)

There are just two boxes you need to fill out on the form to open a pull request. The first is the title. GitHub will have filled in something based on the content of your branch. It should be in the "semantic commit message" format linked in the commit section, and it should summarize the overall effect of your PR on the Numberscope system. So if you don't like what GitHub filled in, improve it before you submit.

The other box is the commit description. It is prefilled with a statement you need to agree to -- read it over carefully to make sure you agree before you submit. It may also have some description extracted from your branch. If so, and you like the looks of that, you can just delete the \<Please provide a high-level description of your PR.\> placeholder and you are good to go. If not, replace that placeholder with a more detailed but still brief explanation of the reason, purpose, and content of your PR. If it takes care of any of the outstanding issues concerning Numberscope listed on the GitHub site for Numberscope, include at the bottom on a separate line the statement Resolves #[issue_number].

When you are happy with the title, description, and changes made in your PR, and you agree with the statement that was pre-filled in the description (which you should leave in the description to record your agreement), you can click the green "Create pull request" at the bottom right corner of the description box. You should leave the box labeled "Allow edits and access to secrets by maintainers" checked. Don't worry, there aren't any secrets associated with the Numberscope project, so you are not giving us your bank account numbers or anything like that.

With this click, your pull request should actually truly be created, and show up on the pull request list in the main GitHub page for Numberscope. The maintainers of Numberscope will automatically be notified of your proposed code and will get back to you about your ideas. Thank you for submitting your work to Numberscope!

Advanced operations

Stash your changes

You may be in the middle of working on a project, and decide that you need to check out main again to see how something worked originally, or to start on a new more urgent project, or something like that. But you may have changed some of the files in the working tree, in a way that you want to save, but that is not quite ready to turn into a bona fide commit.

In that situation, git has a facility to squirrel away the changes you have made so far for later use -- it's called "stashing" the changes. Use the command:

git stash

Unstash your changes

To unstash your changes, issue the following command:

git stash apply

This command recreates the changes that were previously stashed in your working tree, and it also keeps a record of those changes in the stash.

Alternatively, if you want to recreate the stashed changes but also simultaneously discard them from your stack of stashed changes, issue the following command:

git stash pop

Finally, if you just want to discard a set of stashed changes without affecting your working tree, the command is:

git stash drop

Be careful -- the difference between "pop" and "drop" is a bit subtle. Both get rid of the most recent set of changes in your stash, but "pop" affects working tree whereas "drop" throws them away without affecting enything.

Check your remotes

To check to see what your "remotes" (other Git repositories you are set up communicate with via push/pull) are, issue the command below.

$ git remote -v

This will show a list that might look something like

fork	git@github.com:somebody/frontscope.git (fetch)
fork	git@github.com:somebody/frontscope.git (push)
origin	https://github.com/numberscope/frontscope.git (fetch)
origin	https://github.com/numberscope/frontscope.git (push)

The repository you first cloned from will always be named origin; any other remotes shown will have the names you assigned them when they were addes. The name fork is just a typical choice for the remote you are generally going to push to.

Sync a local clone

Once you or other people have submitted changes and they have been accepted and "merged" into the official main branch, the local clone you made on your machine will become out of date in terms of its view of the code in the main branch. (It won't change its concept of what code is in main until you explicitly tell it to download or "pull" the latest commits in main.)

This can be a problem -- you may not want to base a new branch on out-of-date code, or a PR you may have submitted may have ended up in a conflicted state because it was based on an older version of main. Here's how you correct the situation.

First, you need to get back onto the main branch in your local working tree. If you have any uncommited changes (they would show via git status) that you want to save, you should first stash them. Then execute

git checkout main
git pull

This will download all of the changes to the standard main branch since you created your clone or last pulled main, and leave your working tree on the main branch with all of the new code. Now you if you want to return to the branch you were on, you can execute

git checkout [branch-name]

And if you previously stashed any changes, you may want to unstash them.

Rebase your branch

A branch works from a specific commit in its parent branch (usually main) -- its like a logbook of all of the changes you've made from that particular snapshot of all of the code. Often, for a PR to succeed, the branch must be based on the latest commit in main. So here's what you do if your branch is based on an earlier commit in main (likely the one that was current when you started your work), causing a problem with your PR. You may also want to update your branch in this way to make sure it will work with new features that have been introduced in main, or just to minimize the obstacles that a PR based on it will encounter in the review process.

First, make sure you have synced your clone, as outlined in the previous section. Then switch to your branch (with git checkout [branch_name]) if necessary. Make sure that there are no uncommitted changes (by stashing them or waiting to unstash them if you had previously stashed them).

Now you can instruct git to "replay" each of the changes you made in getting your branch to where it was, creating new commits along the way. It's as if it automatically re-edits each file in just the way you did when you were creating your branch, except starting from the current versions of the file. The command is:

git rebase main

This may go smoothly and simply report "Successfully rebased and updated [branch_name]." Or it may hit a "conflict." This conflict can occur when you changed a region of a file, and the same or very nearby region of the same file has changed in the main branch between when you started and now. Under these circumstances, git may not be able to reconstruct how to "re-do" the edits you made, starting from the current version of the file. (For example, maybe you edited the code for a function that has since been removed, renamed, or relocated from the file where it was. Git can't necessarily figure out what to do with your changes in that case.)

If git hits such a conflict, rebasing will stop with a report on which files are in a conflicted state. If you now look at these files with a text editor, you will see that they contain sections that look like:

<<<<<<< HEAD
## This is how origin/main changed this file
=======
## And this is how your branch changed this file
>>>>>>> xxxxxxx (Updated doc/working-with-git-and-github.md)

(where xxxxxxx will be a seven-character hexadecimal "hash" identifying the particular commit from your branch where these changes occurred).

You need to manually edit this block, removing the <<<<<<< line, the ======= line, and the >>>>>>> line, and putting the contents in between to be the way they "should" be considering both the changes that were made in the main branch and in your branch. Sometimes this is easy: maybe main fixed a typo in a comment and your branch inserted code just before the comment, and so you just do both. Other times it is moot: main removed a function altogether and you changed it in some way that is now irrelevant because the function is gone. And other times it is quite tricky: you fixed a bug in some function, but main changed its algorithm slightly and now it's not clear if the bug is still there or if it is, whether your fix will still work. So you may need to consider carefully and do some investigation and testing of your new combined code; but most of the time it is pretty straightforward. It's just that there aren't any hard-and-fast rules for how to resolve these sorts of "overlapping changes" conflicts. If there were, git would just take care of them automatically for you.

Anyhow, when you've settled on the best way to resolve the conflict and have edited all the conflicted files to have consistent code and have removed the conflict markers, you can execute:

git rebase --continue

and the process will proceed further, either finishing up and reporting that your branch is successfully rebased, or stopping again if further conflicts are found.

When the process finishes, you now have locally a version of your changes to main that is based on the latest commit from main. It is a sort of rewriting of history. If you had already pushed this branch to a remote, your local version of the branch is now very different from the remote copy, and you will want to force them to be the same again. You can do this with

git push -f

(where the -f stands for "force"). Finally, your branch is all back in sync with main, and, for example, the process of your pull request being reviewed can continue.

Note that you will find many tutorials or references concerning Git that recommend to use git merge to bring your branch up to date with additional changes that have occurred in main since you branched. In our experience working with Numberscope, that has led to more complicated PRs that are more difficult to review. Hence, it is our recommendation that you use git rebase instead of git merge for keeping your feature branch up-to-date.