Matthew West

Git Quick Reference

Good resources are listed at the home page documentation.

Especially good references are:

Git configuration (first time only)

You can either edit ~/.gitconfig by hand, or run git config ... to change the settings.

Define your identity with:

git config --global user.name "Firstname Lastname"
git config --global user.email "your_email@youremail.com"

Set up one useful alias:

git config --global alias.la "log --all --graph --date=short --format=format:\"%C(yellow)%h%Creset %ad%Cred%d %Cblue%an%Creset %s\""
You can then run git la to "log all" history.

If on OS X, tell git to remember your passwords in the keychain:

git config --global credential.helper osxkeychain

If on OS X, tell git to not trust ctime:

git config --global core.trustctime false

See this blog post or this Hacker News thread about the issue.

Working with GitHub

We use two different models of collaboration on GitHub:

  1. Centralized, for papers, proposals, etc. Here everyone has write access to a single repository and there are no forks or branches. Everyone directly pushes and pulls to the centralized repository.
  2. Distributed, for source code. Here there is a main repository that only one person has write access to. Everyone else forks this repository, works on branches, pushes to their own fork, and issues pull requests (PRs) back to the main repository.

GitHub: centralized model

Starting the project: (project owner only) Make a repo on GitHub called paper_name and a team also called paper_name. Add the repository to the team, set team permissions to "write access", and add people to the team.

Getting started as a collaborator: Everyone should clone the main repository:

git clone git@github.com:compdyn/paper_name.git

The central repository will be called origin.

Making changes: Always stay on the master branch. Edit files locally, then add/commit. To push changes to the central repository do:

git push origin master

If the push fails due to newer changes on the origin, you need to update (see below) and then push again.

Getting changes: Before getting changes from the central repository, make sure you have committed all your local changes. Check this with git status. Then do:

git pull

This will merge remote commits with your local commits (if any).

Resolving conflicts: The only time when conflicts can occur is during a git pull. Doing local git commit will always succeed, and git push will simply stop if everything isn't up to date already. To fix a conflict, do git status to see which files need fixing (they are the ones that aren't added to the index). Edit these files (look for <<<<<< ====== >>>>>> sections), then git add them. Once all files are fixed and added, do git commit.

Important note: When working with a centralized model it is important to push and pull frequently to keep everyone synchronized. Always pull before starting work each day, and commit/push frequently (at least once per day, or much more often if multiple people are working simultaneously on the same repository).

GitHub: distributed model (for contributors)

Getting started as a collaborator: Everyone should fork the main repository on GitHub. Assuming the main repository is in an organization called org, so it's full name is org/code_name, then the forked version for user user_name will be user_name/code_name. Next, clone the forked repository to your local machine and set up the main repository as the upstream (cf. Fork a repo on GitHub help):

git clone git@github.com/user_name/code_name.git
git remote add upstream git@github.com/org/code_name.git

This means that you now have three key repositories to keep track of:

  1. Upstream: the main central repository on GitHub. You can't directly push to this, but this is where you will pull other people's commits from.
  2. Origin: your forked repository on GitHub. You can push and pull directly to this.
  3. Local: your clone of origin on your local computer. This is where you commit before pushing to origin.

Important note: Don't ever commit anything to the master branch. Only the upstream owner should ever commit to master. You should do all your work in branches and issue pull requests on these branches back to upstream.

Updating your fork from upstream: Getting new changes from upstream is a two-step process. First you pull from upstream to your local repository, then you push the new changes back to your origin. There is no direct communication between upstream and your forked origin repository. Note that for this to work it is critical that you have never committed anything to your master branch. The procedure to pull and then push is:

git checkout master      # make sure you are on the master branch
git pull upstream        # pull new changes from upstream
git push origin master   # push the new changes back up to your fork (origin)
See also the GitHub help pages on Syncing a fork and Pushing to a remote.

Editing, committing, and pushing your code: You should only ever commit and push code on a branch (never on master). To start a new branch, do:

git checkout -b branch_name

Later, you can do:

git checkout branch_name   # make sure you are on the right branch
# ... edit code
git add <code>
git commit -m "<message>"
git push origin branch_name

This makes local code changes and then pushes them up to your forked repository (origin).

See also Pushing to a remote on the GitHub help pages.

Merging your code back into upstream and master: You should never merge your branches into your own master. Instead, issue Pull Requests (PRs) to have them merged back into the upstream master by the owner of upstream. To do this:

  1. Go to the GitHub page for your forked repository.
  2. Switch to the branch that you want to merge using the branch dropdown.
  3. Click the green compare-and-review button (with two tip-to-tail arrows making a square).
  4. Click the green "Create pull request" button.

Once the upstream owner merges your code, you can then get it back into your master by updating your fork from upstream, and then you can delete your branch that was merged. See also Using pull requests on GitHub Help.

GitHub: distributed model (for owners)

Starting the project: Make a repo on GitHub called code_name and a team also called code_name. Add the repository to the team, set team permissions to "read access", and add people to the team. If there is going to be more than one maintainer with direct write access, then make a code_name_maintainers team with "write access". Alternatively, if this is a public repository with just one owner/maintainer then no teams are needed.

PartMC repositories

To clone the main repository on your laptop:

git clone ssh://lagrange.mechse.illinois.edu/~mwest/public_git/partmc.git
To publish your own private repository, on lagrange first do:
mkdir ~/public_git
git init --bare ~/public_git/partmc.git
Then on your laptop in your partmc repository do:
git remote add lagrange ssh://lagrange.mechse.illinois.edu/~<username>/public_git/partmc.git
git config remote.lagrange.push '+refs/heads/*:refs/heads/*'
git push lagrange

To get branches from another repository owned by username:

git remote add <username> ssh://lagrange.mechse.illinois.edu/~<username>/public_git/partmc.git
git fetch <username> refs/heads/<branchname>:refs/remotes/joined/<branchname> # just one branch
git fetch <username> # all branches

To look at a remote branch from <username> for the first time:

git checkout <branchname> <username>/<branchname>
Thereafter it's just:
git checkout <branchname>

To update your local copy of a remote branch to the current value of a remote repository, do:

git checkout <branchname>
git pull <username>
In the case of the master branch this is just:
git checkout master
git pull origin

To set up a push repository on taub, do:

taub$ git init ~/partmc
taub$ module initadd git # make sure "module load git" is in .bashrc, not .bash_profile
Then on the local laptop do:
laptop$ git remote add taub ssh://taub.campuscluster.illinois.edu/~<username>/partmc
laptop$ git config remote.taub.push '+refs/heads/*:refs/heads/*'
laptop$ git push taub
This will complain about not updating a currently-checkout-out branch. To fix this, just checkout a different branch on taub then push again.

PartMC branches and tags

PartMC version numbers have the format major.minor.patch. Patch-level changes are only for bugfixes.

Branches:

Tags:

Main repository topology:

*  master branch
| *  2.1 branch, 2.1.4 tag
| *  2.1.3 tag
| *  2.1.2 tag
| *  2.1.1 tag
| *  2.1.0 tag
|/  
*  2.0.0 tag
| *  1.2 branch, 1.2.1 tag
|/  
*  1.2.0 tag
| *  1.1 branch, 1.1.1 tag
| *  1.1.0 tag
|/  
*  1.0.0 tag

Useful commands

Difference between different states of code:

git diff oldbranch..newbranch filename # diff of one file from oldbranch to newbranch
git diff oldbranch..newbranch # diff of all files from oldbranch to newbranch
git diff --stat oldbranch..newbranch # show which files changed from oldbranch to newbranch

Delete remote tracking branches that have been deleted on the remote side:

git remote prune REMOTENAME

Find a git commit that introduced a change:

git log -Ssearchtext # search everything
git log -SFoo -- path_containing_change # for just one file
See also --pickaxe-regex argument to git log.

Undo git add FILENAME:

git reset FILENAME

Undo a git rm FILENAME:

git checkout HEAD -- 

Undo the last commit:

git reset --soft HEAD^  # leaves things staged and in working tree
git reset --mixed HEAD^ # leaves working tree alone but unstages files
git reset --hard HEAD^  # resets both index and working tree

Diff by words (rather than lines):

git diff --word-diff PREV_REV..NEW_REV
git diff --word-diff=color PREV_REV..NEW_REV
git diff --word-diff=plain PREV_REV..NEW_REV
git log -p PREV_REV..NEW_REV
git log -p --word-diff PREV_REV..NEW_REV
git log -p --word-diff=color PREV_REV..NEW_REV
git log -p --word-diff=plain PREV_REV..NEW_REV

Delete a remote branch:

git push REMOTENAME :BRANCHNAME

Diff or log a branch from a common ancestor of the master branch:

git log BRANCHNAME ^master # commits reachable from BRANCHNAME but not from master
git diff master...BRANCHNAME # special syntax that only works for diff
Note that git log treats "..." like git rev-list does, which is very different to how git diff treats it (confusingly, git rev-list uses "..." to mean symmetric difference, which is more like how git diff uses "..").

Copy a specific file on a different branch:

git checkout revision -- filename  # checkout just one file
git checkout revision -- directory # can also do whole directories
git show revision:path/to/file > local_file # stores in a different location

Push specific revisions:

git push remotename : # push matching branches
git push remotename tag tagname # push just one tag