We also have a shorter, less-complete git writeup you may consult.

1 Add, commit, pull, push

On a single machine, a file can be in one of four states:

State Meaning
untracked on your disk but not being tracked by git
modified tracked by git, but the on-disk copy doesn’t match git’s copy
staged git plans to track the on-disk copy next time it commits something
committed tracked by git, and the on-disk copy matches git’s copy

The command git status will show you the untracked, staged, and modified files. Files not shown by git status have been committed.

git add myfile
changes myfile to be staged (works for both untracked and modified files)
git commit -m "description of changes"
commits all staged files
git commit -a -m "description of changes"
commits all modified and staged files

Git keeps a history of all committed files, which can be viewed using git log.

When working with a client (usually your laptop) and a server, each has its own copy of the files, which can be untracked, modiied, staged, or committed individually on each computer. The only time the computers talk to each other is when you ask the client to git pull or git push

git pull

Copies all committed changes on the server to the client.

What happens depends on the history of commits on the two computers:

If the client’s current state is part of the server’s history
Client gets all of the server’s updated committed files
If the server’s current state is part of the client’s history
Nothing happens
If the two histories have diverged

The client tries to merge the server’s history into its history.

Fast-forward merges can occur if there’s no conflict between the changes: for example, if the server modified file X which the client didn’t modify, the client grabs the server’s copy of file X.

If there are conflicts, the merge may require you intervention to resolve.

git push

Copies all committed changes on the client to the server.

The server will refuse the pull unless the server’s current state is part of the client’s history. Because of this limitation, git pull (which will sync the two chronologies) should always be run before git push.

Hence the common case on the client is:

  1. git commit -a -m "description of changes"
  2. git pull
  3. git push

2 Single user, several machines

Requirements
Access to a server you can ssh into
Recommendations
Set up passwordless SSH access
Design
The server will have a copy of your project. Your laptop will have another copy. When you push from the laptop to the server it automatically accepts those changes. If you change something on the server you have to tell the laptop to pull them in.
Setup
On the server

Let’s assume you want to put the project in directory projects/proj1

  1. mkdir -p projects/proj1
  2. cd projects/proj1
  3. git init -b main ./
  4. git config receive.denyCurrentBranch updateInstead
  5. echo "Initial commit" > README.md
  6. git add README.md
  7. git commit -m 'Initial commit'

The -b in step 3 tells the server to store both the files and the repository structure in the same directory. Step 4 tells the server to automatically accept anything you push to it.

Feel free to change steps 5 and 6 to make a different initial filename and contents. It may be possible to skip steps 5–7 entirely, but sometimes that causes strange errors in the laptop steps noted below.

On the laptop

Let’s assume you want to put the project in directory school/work/myproj1

  1. cd school/work
  2. git clone yourusername@the.server.edu:projects/proj1 myproj1
  3. cd myproj1

If you have more than one computer, you can repeat steps 8–10 on each of them.

Usage
Edit on laptop
  1. git pull
  2. make any edits you wish
  3. git addany new files you’ve created
  4. git commit -a -m "description of changes"
  5. git push

Note: to avoid merges, don’t make edits on any other computers between steps 1 and 5 above. If you did edit on another computers in the middle, add another git pull before the git push.

Edit on server
  1. make any edits you wish
  2. git add any new files you’ve created
  3. git commit -a -m "description of changes"

Warning: Your laptop won’t be able to git push or git pull in between steps 1 and 3 above.

3 Histories and undoing

Using git on a single machine makes it simpler (there’s no push and pull to worry about) and enables the chronological views (as well as branching) of git, including the ability to recover an earlier version of the project.

The two most important commands for inspecting things are git status and git log.

git status
Shows a snapshot of the current state of files: which are untracked, modified, or staged. It also shows you the code you need to change that in parentheticals
git log

Shows your entire history of commits, with the most recent on the top.

If you have more than a screen full of entries in your log, git log will run in a pager, a program for showing more than a screen of text on the command line such as less. Scroll up and down using arrow keys or page up/page down keys. Exit the pager using q.

If you realize you’ve committed changes you don’t like and want to get back to an earlier commit, there’s a bit of work to do. This work is based on git’s belief that nothing should ever be lost: you might change your mind about the undo later. Git thus gives two options for undoing commits: reversing and branching.

3.1 Reversing

One way to think about an undo operation is as a new operation that just happens to do the opposite of an earlier operation. For example, consider the following sequence:

  1. I start with a file containing abcd
  2. I add ef to the end, creating abcdef
  3. I add - in the middle, creating abc-def
  4. I remove - from the middle, creating abcdef

Step 4 in this sequence is an reversing-type undo: a new action added to the history that happens to undo a previous action.

The command to do this in git is called git revert and it requires the specific commit action(s) to undo.

Undo the most recent commit

git revert HEAD

HEAD is a special git variable that means the most recent commit.

Undo the most recent four commits

git revert HEAD..HEAD~3

The ~ here means minus, so this reverses the four commits HEAD, HEAD − 1, HEAD − 2, and HEAD − 3.

Undo a specific commit from the log

Each commit in the log has a long hex identifier, like commit 985baf822fa1a3221a892833b0622858a6c75694. You can pick any one of them and git will try to figure out what the repository would have looked like if everything before and after than had happened but that one thing had not.

git revert 985baf822fa1a3221a892833b0622858a6c75694

In most cases, you can use a prefix of the hex identifier instead of the whole thing

git revert 985baf82

Undo a specific commit from the log and everything after it
git revert HEAD..985baf822fa1a3221a892833b0622858a6c75694

It is worth noting that reverting the HEAD twice does not reverse two commits: the first revert adds a new undo commit, which the second undoes, so git revert HEAD; git revert HEAD leaves all files in the repository unchanged but adds two new commits to the log.

3.2 Branching

Another way to think about an undo operation is to think of a tree of versions. I always have a point in the tree I think of as now and when I undo I’m simply changing my point of view. For example, consider the following sequence

  1. I start with a file containing abcd
  2. I add ef to the end, creating abcdef
  3. I add - in the middle, creating abc-def
  4. I move back to where it was after step 2: abcdef
  5. I delete the c and add a q in front, creating qabdef

In the end I have this history, where the part I am looking at currently is circled in red:

abcd abcdef abc-def qabdef

To deal with branches, git uses names. By default when you make a new git repository you have one named branch, typically either master (for older versions of git) or main (for newer ones). If you want to undo in a branching way, you need to name your new branch.

List all branches

git branch

In the list, your current branch is shown with an asterisk in front.

Rename a branch

git branch -m oldName newName

Because renaming could confuse people working collaboratively, renaming only works on your local copy. You can’t push a rename to the server nor pull it from the server.

Pick an old commit and start a new branch there

Each commit in the log has a long hex identifier, like commit 985baf822fa1a3221a892833b0622858a6c75694. You can switch to a new branch pointing at one of these by running

git checkout -b newBranchName 985baf822fa1a3221a892833b0622858a6c75694

When you git push after making a new branch, you’ll get a special message explaining how to create the branch on server too.

Switch to an existing named branch

git switch branchName

Each copy of the repository has it’s own working branch. You can’t push a switch to the server nor pull it from the server.

Note that almost all of git’s commands (commit, push, pull, etc) only apply to the current branch.

Branches can also be merged, but that is out of scope for this writeup. See https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging for more.

4 Collaboration

To do: write up basics of merges and using github/bitbucket