Version control with Git

Week 5 – Lecture B

Presented on

September 21, 2026

Page last modified

September 23, 2026



1 Introduction

This is the hands-on continuation of today’s lecture on version control with Git.

1.1 Learning goals for this lecture

  • Learn the basic Git workflow and the most common Git commands.
  • Make Git ignore specific files and dirs.
  • Undo accidental changes to your files.
  • Show the changes you’ve made to files, also using VS Code’s Git interface.

1.2 Getting ready

Start a VS Code session just like you’ve done before.

Click here to see the instructions
  1. Log in to OSC’s OnDemand portal at https://ondemand.osc.edu.

  2. In the blue top bar, select Interactive Apps and near the bottom, click Code Server.

  3. Fill out the form as follows (as necessary — should be prefilled now!):

    • Cluster: pitzer
    • Account: PAS3493
    • Number of hours: 2
    • Working Directory: /fs/ess/PAS3493/people/<user> (replace <user> with your user name)
    • App Code Server version: 4.129.0
  4. Click Launch, and in the next page, click the Connect to VS Code button once it appears.

  5. In VS Code, open a terminal by either:

    • Clicking → Terminal → New Terminal
    • Using the keyboard shortcut Ctrl+`
  6. Check that you are in /fs/ess/PAS3493/people/$USER by typing pwd in the terminal.

    Recall that $USER is a variable that represents your username.

    If you’re not in /fs/ess/PAS3493/people/$USER, it may be listed under Recents in the Get Started document — if so, click on that entry. Otherwise, click File > Open Folder and type/select that path.

2 One-time Git configuration

We will start with one-time personal Git configuration. This will apply anywhere on OSC, and won’t ever have to be redone unless you want to make changes.

These steps are all done with git config --global:

  1. Make your name known to Git, since it will be recorded in every commit you make — your actual first and last name (not your OSC or GitHub username!):

    git config --global user.name "John Doe"
  2. Make your email address known (should match the address associated with your GitHub account, if you already created one):

    git config --global user.email "doe.391@osu.edu"
  3. Set a default text editor for Git. (Git may occasionally open a text editor for you. Even though we’re using VS Code, here it is better to select a text editor that runs in the shell, like nano1.)

    git config --global core.editor "nano -w"
  4. Change the default “branch” name to main (Git’s default is still the older master, whereas GitHub uses main)2:

    git config --global init.defaultBranch main
  5. Finally, print your current settings to check whether you successfully changed them:

    git config --global --list
    user.name=John Doe
    user.email=doe.391@osu.edu
    core.editor=nano -w
    init.defaultbranch=main

3 The basic Git workflow

You’ll create a Git repository for a mock book project: writing Charles Darwin’s “On the Origin of Species”. This example follows Chapter 2 of Allesina and Wilmes (2019). The topic may seem silly, but keeping the content trivial means you can focus entirely on the Git workflow.

3.1 Create and move to a new project dir

Create a new dir for a mock project that you will version-control with Git:

# (Your starting point should be /fs/ess/PAS3493/people/$USER)
mkdir -p week05/evo
cd week05/evo

Next, we’ll open this dir in VS Code with “Open Folder” to move all of VS Code, not just the terminal, to this dir. For example, after moving, the file explorer will only show this project’s files, and VS Code’s Git functionality will work with the repo you’ll create here. (This is also how you’ll want to work with your own projects: open each project’s dir as the folder in VS Code.)

  1. Click → File → Open Folder.

  2. Type/select /fs/ess/PAS3493/people/<user>/week05/evo, replacing <user> with your user name. This will cause VS Code to reload.

  3. Open a new terminal and check that you are in the evo dir:

    pwd
    /fs/ess/PAS3493/people/jelmer/week05/evo

3.2 Start a new Git repository

Now we’re ready to start your first Git repository! The command to initialize a new Git repository is git init — use that to start a repo for the evo dir:

git init
Initialized empty Git repository in /fs/ess/PAS3493/people/jelmer/week05/evo/.git/

Can you confirm that this hidden dir is there? The ls command will show hidden files and dirs with the -a option, and indeed, the .git dir is present:

# (ls -a will also show the current dir "." and the parent dir "..")
ls -a
.  ..  .git

Above, we created a Git repo in an empty dir, but you can use git init just the same in a dir that already contains files. However, only run it in the top-level dir of a single project, and never:

  • In a dir like your home dir or /fs/ess/PAS3493/people/$USER, which contains many projects,
  • Inside a dir that is already part of a Git repo.

If you did run git init in the wrong place, you can undo it by removing the .git dir that it created using rm -rf .git3.

Next, check the status of your new repository with git status:

git status
On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)

Git reports that you:

  • Are on a “branch” called main. We won’t cover Git branches in class, but this is discussed in the optional self-study material and in Allesina and Wilmes (2019) Chapter 2.6. Basically, branches are “parallel versions” of your repository that are useful for collaborative and experimental work.
  • Have not created any commits yet.
  • Have “nothing to commit” because there are no files in this dir.

If you run a Git command, like git status, outside of an existing Git repository, you’ll get this error:

fatal: not a git repository (or any of the parent directories): .git

That usually just means that you’re in the wrong dir: check with pwd. The bigger point is that Git commands only work inside a Git repository, not in any other directory.

3.3 Your first Git commit

Start writing the book (😉) by echo-ing text into a new file called origin.txt:

echo "An essay on ..." > origin.txt

Now, check the status of the repository again:

git status
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        origin.txt

nothing added to commit but untracked files present (use "git add" to track)

Git has clearly detected the new file. But as mentioned in the intro slides, Git does not automatically start “tracking” files — that is, it won’t automatically include all files that are in your dir in the repository. Instead, it tells you the file is “Untracked” and gives a hint on how to add it to the repository.

Start tracking the file and stage it in one go with git add:

git add origin.txt

As always, Tab-complete file names, like origin.txt above!

Next, check the status of the repo again:

git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   origin.txt

Now, your file has been added to the staging area (also called the Index) and is listed as a “change to be committed”4. That phrase means that if you now run git commit, the file will be included in that commit.

So, with your file tracked & staged, let’s make your first commit. Add the option -m followed by a “commit message”: a short description of the changes you are including in the current commit5:

git commit -m "Started the book"
[main (root-commit) 3df4361] Started the book
 1 file changed, 1 insertion(+)
 create mode 100644 origin.txt

You’ve made your first commit! Here is another way of visualizing how changes are staged and committed in Git — and of the three areas that files can be in:

  1. The working tree (also called working dir) is where you make changes to files.
  2. The staging area (also called Index) is where you stage changes to be included in the next commit.
  3. The commit history is where committed changes are stored. It has many “layers” of commits, and you can go back to any of them at any time.

A diagram of the process of adding and committing changes with Git commands

Note that git add has a dual function: it starts tracking files and stages them.

Why might the term “working tree” be used instead of “working directory”? (Click to see the answer)

The term “working tree” is used in Git to emphasize that we are referring to the entire hierarchy of dirs and files that makes up your project, and not the single dir that your terminal is currently in.

That said, both terms are often used interchangeably. As such, if you hear “working dir” in a Git context, it usually refers to the entire hierarchy.

3.4 Checking the status and history

First, let’s check the status of the repo again:

git status
On branch main
nothing to commit, working tree clean

It reported “nothing to commit, working tree clean”, which means no changes have been made anywhere in the project dir since the last commit.

Try to get in the habit of using git status a lot — as a sanity check before and after other git actions.

Next, look at the commit history of the repo with git log:

git log
commit 3df4361c1de9b71e08bf6e050105d53097acec21 (HEAD -> main)
Author: Jelmer Poelstra <jelmerpoelstra@gmail.com>
Date:   Mon Mar 11 10:55:35 2024 -0400

    Started the book

Notes on the first line of the above output:

  • The long “hexadecimal code” (which uses numbers and the letters a–f) is a unique identifier for that commit, usually called the commit hash (or SHA-1 checksum). You can reference and access each past commit by its hash, and the first 7 characters are usually enough (as you’ll see with git log --oneline below).

  • (HEAD -> main) means that you are (your “HEAD” is) currently located at the commit in question, and that the commit was made on the main branch.

Below that, git log reports the author, date and time of the commit, and the commit message you provided.

Exercise 1: Put the commands in order

Say you want to start a brand-new project with a Git repository, and make a first commit containing a single file, notes.md. In which order should you run the commands below? (Only figure out the order — don’t run these commands.)

git commit -m "Start notes"
git init
cd myproject
git add notes.md
echo "# Project notes" > notes.md
mkdir myproject
Click for the solution.
mkdir myproject
cd myproject
git init
echo "# Project notes" > notes.md
git add notes.md
git commit -m "Start notes"

Bonus: two of these commands could be swapped without any problems. Which ones?

Click for the solution.

git init and echo can be swapped: Git doesn’t care whether files already existed when you initialized the repo! Swapping git add and git commit, on the other hand, would make Git report that there is nothing to commit.

3.5 Your second commit

Find the file origin.txt in the VS Code file explorer, open it, and replace its line of text with the book’s final title:

origin.txt
On the Origin of Species

Check the status of the repo:

git status
On branch main
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:   origin.txt

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

Git has noticed the changes, because the file is being tracked: origin.txt is listed as “modified”. But changes to tracked files are not automatically staged.

Use git add to stage the file as a first step to committing these changes:

git add origin.txt
TipGit never acts on files by itself

Git behaves similarly when it comes to new files and changes to tracked files: it does not automatically do anything with them. That is deliberate: managing which files are tracked and staged is up to you.

Now, make your second commit, again adding a commit message. Like always, it’s up to you to decide what to write in the commit message, but the idea is that you give a short description of the changes you made:

git commit -m "Changed the title as suggested by Murray"
[main f106353] Changed the title as suggested by Murray
 1 file changed, 1 insertion(+), 1 deletion(-)

Git gives a brief summary of the changes that were made: you changed 1 file (origin.txt), and since you replaced the line of text in that file, it interprets that as 1 insertion (the new line) and 1 deletion (the replaced line).

Check the history of the repo again — you’ll see that there are now 2 commits:

git log
commit f1063537b6a1e0d87d2d52c9e96c38694959997a (HEAD -> main)
Author: Jelmer Poelstra <jelmerpoelstra@gmail.com>
Date:   Mon Mar 11 11:01:49 2024 -0400

    Changed the title as suggested by Murray

commit 3df4361c1de9b71e08bf6e050105d53097acec21
Author: Jelmer Poelstra <jelmerpoelstra@gmail.com>
Date:   Mon Mar 11 10:55:35 2024 -0400

    Started the book

As you start accumulating commits, you might prefer git log --oneline for a one-line-per-commit summary:

git log --oneline
f106353 (HEAD -> main) Changed the title as suggested by Murray
3df4361 Started the book

Once your history is longer than your screen, git log opens in the less pager that you used last week: scroll with the arrow keys and press q to get back to your prompt. The same goes for long output of git diff, a command you’ll see later today.

When you would like to stage multiple files, you don’t need to add them one by one. Here are a few other options:

  • Stage all files in the project/repository:

    # [Don't run any of this - these are hypothetical examples]
    git add --all
  • Stage all files in the current dir and its subdirs (when run from the repo’s root dir, this is the same as git add --all):

    git add .
  • Stage all files in a specific dir (here: scripts) in the project:

    git add scripts/

Finally, you can stage and commit all changes with a single command using the -a option for git commit (but be aware that this will not add untracked files):

git commit -am "My commit message"

3.6 What to include in individual commits

As shown in the box above, there’s a way to stage and commit all changes to tracked files at once. That may seem more convenient than separately git adding files before committing. However, we focus here on the more deliberate workflow of staging and committing files separately to encourage you to think about what changes you are committing.

In turn, that’s useful because it’s good practice to make each commit a unit of progress worth saving, rather than committing lots of things at once (say, at the end of each day). That is, you ideally create separate commits for distinct changes, even if those are small.

For example, say you use git status and find that since your last commit you have:

  • Updated a README file to include more information about your samples.
  • Worked on a script to run quality control of sequence files.

These are unrelated changes, and it’s not recommended to include both in a single commit.

Exercise 2: A third change and commit

  1. Create a new file todo.txt containing the line: “June 18, 1858: Read essay from Wallace”.

    Click for the solution.

    echo "June 18, 1858: Read essay from Wallace" > todo.txt

    Or: create the file with touch todo.txt or in the VS Code file explorer, and then open it and type the line in the editor.

  2. Stage the file to be committed.

    Click for the solution.

    git add todo.txt
  3. Create a Git commit with the commit message “Added to-do list”.

    Click for the solution.

    git commit -m "Added to-do list"

Exercise 3: Separate commits?

Say that since your last commit, you have changed three files:

  1. README.md: you added information about your samples.
  2. scripts/qc.sh: you changed the output dir of this QC script from qc to results/qc.
  3. scripts/qc_summary.sh: you changed the dir that this script reads QC results from, also from qc to results/qc.

How many commits would you make, and which file(s) would go in each?

Click for the solution.

Two commits: one with README.md, and one with both scripts.

The changes to the two scripts are in different files, but they form a single unit of progress: if you committed only the change to qc.sh, that commit would contain a qc_summary.sh script that looks for QC results in the wrong place. So, the rule is “one commit per change”, not “one commit per file”.

Separate commits for distinct changes are the ideal, but don’t let that ideal stop you from committing at all. If you’ve made lots of changes since your last commit and don’t have time to split them up (say, at the end of a long day, or before a deadline), committing everything at once is much better than not committing: until you commit, your changes are not saved in your Git history, so you can’t go back to this version later.

In that case:

  • Stage everything at once (see the box above), but check git status first to make sure you’re not including files you don’t want in the repo, such as large data files:

    # [Don't run this - this is a hypothetical example]
    git status
    git add --all
    git commit -m "Updated README; QC scripts now use results/qc"
  • Still, try to write a commit message that briefly summarizes the main changes, rather than something like “Various updates”. This will help you find your way back to this version later.

You have now learned the basic Git workflow! 🥳 Below, we’ll go through a few related key aspects of using Git.

4 Ignoring files and directories

As discussed in the intro slides, it’s best not to track some files, such as (bulky) data files, temporary files, and results. At the same time, we’ve seen that Git reports any “untracked” files in your project whenever you run git status.

Having these files reported every time you run git status can get annoying and make it harder to spot changes and untracked files that you do want to add. More importantly, it makes it easy to commit such files by accident — and once a large data file has been committed, it stays in the repo’s history even if you later delete it (!).

To deal with this, you can tell Git not to pay attention to certain files by adding file names and wildcard selections to a special file called .gitignore.

As an example, let’s start by adding some types of content you would not want to commit to your repository: a dir data, and a file ending in a ~ (a type of backup file that some text editors create):

mkdir data
touch data/drawings.png todo.txt~

Before checking what Git reports, let’s take a look at the current contents of our project dir with tree:

tree
.
├── data
│   └── drawings.png
├── origin.txt
├── todo.txt
└── todo.txt~

1 directory, 4 files

When you check the status of the repo, you should see that Git has noticed the new files:

git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        data/
        todo.txt~
NoteHow untracked dirs are reported

When a dir is untracked, Git reports the dir name and does not list the files inside it (above, the drawings.png file inside data is not shown). That is helpful, because it avoids cluttering the output of git status, but it’s good to be aware of.

You can create a .gitignore file to prevent Git from reporting these files whenever you run git status:

  • This is a plain text file that contains dir and file names/patterns, all of which will be ignored by Git.
  • The file should be called .gitignore and be placed in the project’s root dir (use one .gitignore file per repo).

Some more things to know about .gitignore:

  • As soon as a .gitignore file exists, Git automatically checks and processes its contents.
  • Perhaps somewhat counterintuitively, you should add and commit the .gitignore file itself (that way, it’s shared along with the rest of the repo, such as with collaborators or on GitHub).

Create a .gitignore file and open it in the editor:

# After this command, click on the file name in the file explorer to open it:
touch .gitignore

Then, type the following two lines in the file (press Enter at the end of the last line too):

.gitignore
data/
*~

The first line makes Git ignore everything in the data dir, and the second makes it ignore everything that ends in a ~. That is:

  • .gitignore files accept the same wildcard patterns as the shell, so * matches any string of characters.

  • But unlike wildcards in the shell, which only match files in the current dir, these patterns apply across the entire repo: *~ will also match files ending in ~ in any subdir (and data/ will match a data dir at any level).

You have now changed files both with echo and in the editor. Git doesn’t care how a file changed: it only looks at file contents. In your own projects, you’ll mostly edit files in the editor, and then use the terminal to stage and commit your changes.

When you check the status again, you should see that Git has automatically processed the .gitignore file, since the targeted files are no longer listed as untracked:

git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .gitignore

However, you do now have an untracked .gitignore file, so track and commit this file as follows:

git add .gitignore
git commit -m "Added a gitignore file"
[main 9715ab5] Added a gitignore file
 1 file changed, 2 insertions(+)
 create mode 100644 .gitignore

Exercise 4: .gitignore

  1. Say that a repo’s .gitignore file contains these two lines:

    .gitignore
    results/
    *.log

    Which of these new files would Git now ignore?

    1. results/counts.tsv
    2. run.log
    3. scripts/run.log
    4. scripts/qc.sh

    Click for the solution.

    It would ignore all except d.

    results/counts.tsv is inside an ignored dir, and both .log files match *.log. c is ignored too: patterns like *.log (and results/) match in any dir of the repo, not just in the top-level dir.

  2. Bonus: Say that you add “todo.txt” to the .gitignore file of your evo repo, and then append another line to todo.txt. What do you think, would git status still report that todo.txt was modified?

    Click for the solution.

    Yes, it would report that todo.txt was modified, because .gitignore only affects currently untracked files.

    Since todo.txt is already tracked (you committed it in Exercise 2), Git keeps tracking it and reports changes to it.

    To stop tracking a file, you’d first have to remove it from the repo, as shown in the optional self-study material for Git.


TipGood project file organization makes ignoring files easier

If you keep your data and results in their own top-level dirs, as recommended in week 2, the two lines data/ and results/ in your .gitignore file are all you need to ignore them. If you instead mix, for example, scripts and results within dirs, you’ll need many more specific and error-prone .gitignore entries.

5 Undoing changes that have not been committed

Now, we’ll practice undoing changes to your working tree by recovering a version from the repo: in other words, using Git as an “undo button” after accidental file changes or removal. (In the optional self-study Git material, you can learn how to unstage files and how to undo changes that have been committed.)

Say you accidentally overwrote a file, and want to recover the previous version from the repo:

  1. Let’s first check the contents of todo.txt:

    cat todo.txt
    June 18, 1858: Read essay from Wallace
  2. You accidentally overwrite the file contents instead of appending to it 😳:

    echo "Finish taxidermy of the finches" > todo.txt
  3. Confirm the disaster by checking the file contents again:

    cat todo.txt
    Finish taxidermy of the finches
  4. Always start by checking the status:

    git status
    On branch main
    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:   todo.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
  5. You’ll want to “discard changes in working directory”, and Git told you how to do this — with git restore:

    git restore todo.txt
  6. Check if it worked by looking at the file contents:

    cat todo.txt
    June 18, 1858: Read essay from Wallace

Great! You have successfully recovered the previous version of the file from the repo. If you accidentally deleted a file, you can similarly retrieve it with git restore:

  1. You accidentally remove todo.txt:

    rm todo.txt
  2. Use git restore to get the file back!

    git restore todo.txt
    
    cat todo.txt
    June 18, 1858: Read essay from Wallace

Warninggit restore can only bring back what Git has stored

git restore replaces your file with the version stored by Git (the last commit, or the staged version if you staged the file). Had you not committed todo.txt in Exercise 2, its contents would now be gone for good.

git restore overwrites your current version of the file without asking for confirmation. So be careful when using it: if the file contained changes that you had not committed, those changes are gone for good — Git cannot undo a git restore.


Self-study exercise: What can Git restore?

In which of these situations can git restore get back what you lost?

  1. You accidentally removed a new file that you had never added with git add.
  2. You accidentally removed a whole dir of committed files with rm -r.
  3. You had edited a committed file without staging or committing those edits, and then accidentally overwrote the file. You want those edits back.
Click for the solution.

The only correct answer is b, because Git can only restore versions of files that it has stored (and it can bring back a whole dir of committed files).

It never stored anything for the file in a, nor for the uncommitted edits in c. (In a, git restore would just give an error, and in c, it would only give you the last committed version.)

6 VS Code’s Git functionality

VS Code has built-in Git functionality, which can be more user-friendly than the Git command-line interface, especially for showing changes in files. To have a change to look at, let’s first modify the to-do list you made in Exercise 2.

Open todo.txt in the editor by clicking on it in the file explorer, and add a line to it (again, press Enter at the end of the line):

todo.txt
June 18, 1858: Read essay from Wallace
June 20, 1858: Send draft to Huxley

6.1 Git decorations in the file explorer

First, in the file explorer, several “decorations” related to Git are shown, as can be seen in the screenshot on the right:

  • Files that have been modified since the last commit are shown in orange and marked with an M.

  • The .gitignore file has a Git icon next to it.

  • Files that are ignored via .gitignore are shown in a paler color (data and todo.txt~ on the right).

  • Untracked files that are not ignored would be shown in green and marked with a U — there are none here.

A screenshot of the VS Code file explorer, showing Git decorations next to file names.

File decorations in the VS Code file explorer.

6.2 The Source Control option in the side bar

Click on the version control icon in the narrow side bar (bottom icon in the screenshot on the right) to open the source control option of the wide side bar. (The icon should have an encircled 1 inside it, because we made one change since the last commit!)

The side bar should now show two sections, CHANGES at the top and GRAPH at the bottom, which together replicate the Git functionality we have so far used on the command line. Starting with the bottom one:


  • “GRAPH” (bottom) — shows a visual representation of the commit history of the repo, similar to git log --oneline.

VS Code Git icon.

  • “CHANGES” (top) — shows the files that have been modified since the last commit. You can stage changes, make commits, and more. You can also click on a file name to see the specific changes made to that file, which we’ll do now.

6.3 Showing changes made to a file

  1. Under Changes, click on todo.txt, which has an M (for “modified”) next to it, as shown in the screenshot on the right.

  2. Then, your editor should show a nice overview of the changes made to the file, with the old version on the left, the new version on the right, and changes highlighted in color (green for additions, red for deletions) — as shown in the screenshot below:

A screenshot of the VS Code source control sidebar.

The source control side bar option.

A screenshot of the VS Code diff view.

The “diff” (difference) view of a file in VS Code, showing the old version on the left and the new version on the right.


The git diff command also shows changes made to tracked files. By default, for each file, it shows changes between the working tree and either the last commit or the staging area, depending on whether you have staged the file.

So, after the change to the to-do list that you just made, git diff shows the following:

git diff
diff --git a/todo.txt b/todo.txt
index e3b5e55..9aca508 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1 +1,2 @@
 June 18, 1858: Read essay from Wallace
+June 20, 1858: Send draft to Huxley

We won’t go into the details of the above “diff format”, but at the bottom of the output above, you can see the specific change: the line “Send draft to Huxley” was added (hence the + sign) in our latest version of the file.

Two more notes on git diff:

  • To show differences between the staging area and the last commit, use the --staged option to git diff. So, don’t be surprised if git diff shows nothing right after you ran git add!

  • If you have changed multiple files, git diff will show all of them. If you only want to see differences for one of them, you can specify the filename to git diff. In our example, doing so prints the same output as the plain git diff command above, since we only changed one file:

    git diff todo.txt
    # Output not shown, same as above

6.4 Staging and committing

Finally, let’s commit the change that you made to the to-do list, now using the Source Control side bar instead of Git commands:

  1. Hover over todo.txt under Changes, and click the + icon that appears to stage the file. It should move to a new Staged Changes section.

  2. Type a commit message in the box at the top of the side bar: “Added Huxley draft to to-do list”.

  3. Click the Commit button below the message box.

After that, the side bar should no longer list any changes, and the number in the version control icon should be gone: your working tree is “clean” again.

The command-line equivalent of what you just did would have been:

git add todo.txt
git commit -m "Added Huxley draft to to-do list"

Or, with the -a option to git commit, which stages and commits changes in tracked files in one step:

git commit -am "Added Huxley draft to to-do list"

At the top of the Source Control side bar, you may have noticed a blue Publish Branch button. That button creates an online copy of your repo on GitHub in a few clicks.

We will create such an online copy (a “remote” repository) in the next lecture, but in a different way: on the GitHub website and with Git commands, so that you can see each of the steps involved. The VS Code alternative is explained in a callout there.

TipWhat am I allowed to use in this course?

In class, we will mostly use the command line for our Git operations. But it’s up to you whether you want to use the command line or the VS Code interface in your at-home course work, including graded assignments and the final project.

7 Self-study: Recap and best practices

7.1 The commands you learned today

Command What it does
git init Start a new repo in the current dir
git status Show which files changed and what is staged
git add <file> Start tracking a file and stage its changes
git commit -m "..." Save a snapshot of all staged changes
git log [--oneline] Show the commit history
git diff Show what changed in tracked files
git restore <file> Get a file back as it was in the last commit

And remember the .gitignore file, which keeps files you don’t want to track out of git status output.

7.2 A few Git best practices

Several of these have already been mentioned above, but here is a summary of some key points:

  • Write informative commit messages. Imagine looking back at your project in a few months, after finding an error that you introduced a while ago.
    • Not-so-good commit message: “Updated file”
    • Good commit message: “In file x, updated function y to include z”

A humorous cartoon showing ever-worsening Git commit messages over time.

By XKCD - image source
  • Commit often, using small commits.
    This will also help to keep commit messages informative!

  • Before committing, check what you’ve changed.
    Use git diff [--staged] or VS Code functionality.

  • Avoid including unrelated changes in commits.
    Separate commits if your working tree contains work from disparate edits: use git add + git commit separately for two sets of files.

  • Don’t commit unnecessary files.
    These clutter your repo and its history (especially automatically generated, temporary files), and when collaborating, they can lead to so-called merge conflicts.


Self-study exercise: File states

Say that in a different repo, git status prints the following:

git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   scripts/qc.sh

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:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        scripts/trim.sh
  1. Which file(s) would be included in a commit if you now ran git commit -m "My message"?

    1. Only scripts/qc.sh
    2. scripts/qc.sh and README.md
    3. All three files
    4. None of them: you first need to run git add

    Click for the solution.

    The correct answer is a. Only staged changes are included in a commit. README.md was modified but not staged, and scripts/trim.sh is not even tracked.

  2. And which file(s) would be included if you instead ran git commit -am "My message" (see this box above for the -a option to git commit)?

    Click for the solution.

    scripts/qc.sh and README.md: the -a option stages all changes to tracked files, but it does not add untracked files.

  3. Say that, before committing, you edit scripts/qc.sh once more, without running git add on it again. How do you think git status would list this file, and which version of it would git commit -m "My message" include?

    Click for the solution.

    scripts/qc.sh would be listed twice: under “Changes to be committed” and under “Changes not staged for commit”. git add stages the file as it was at that moment, so the commit would only include the earlier, staged changes. To also include your newest edits, run git add on the file again.


Back to top

References

Allesina, Stefano, and Madlen Wilmes. 2019. Computing Skills for Biologists: A Toolbox. Princeton University Press. https://doi.org/10.1515/9780691183961.

Footnotes

  1. Git will open an editor when you need to provide a “commit message” but didn’t enter one on the command line. Its default editor is vi/vim, which is harder to use and notoriously hard for beginners to exit.↩︎

  2. We’ll talk a bit about what branches are later on.↩︎

  3. -f is needed because some files inside .git are write-protected — and recall from last week that rm -rf is unforgiving, so double-check that you are removing the correct .git dir!↩︎

  4. You also get a hint on how to “unstage” the file: i.e., reverting what you just did with git add and leaving the file untracked once again↩︎

  5. If you leave out -m, Git will instead open the nano editor that you configured above, so you can type your commit message there. Type your message, then press Ctrl+X, Y (to save), and Enter (to confirm the file name) — Git will then make the commit.↩︎