Skip to content
jgonthier edited this page Sep 10, 2015 · 2 revisions

Recommended references

Pragmatic Version Control Using Git, Travis Swicegood (The Pragmatic Bookshelf, Raleigh, 2010) Another excellent online source can be found ​here.

Setting Things Up

Creating a local repository

Not so relevant for Psi4 development, but FYI git can also be very useful for your own private projects. To make a repository for your own (non-shared) use

cd [project-directory-name]
git init

This will make a local repository and store it in a .git subdirectory. That's all there is to it! Migrating your repository to another computer is as simple as copying the working directory (and its included .git subdirectory) where you want it to go.

Cloning a repository

To clone a local repository

git clone other-repository-location proj-dir

This will make a clone of the remote repository and put it in a local directory called proj-dir.

To clone a remote repository

git clone username@machine://remote-repository proj-dir

where username@machine specifies an SSH login to a remote machine.

Alternatively, if you have access to a repository that's accessible via an https interface, you can use

git clone https://remote-repository proj-dir

you may be prompted for a username and password for the site.

For Psi4, if you are registered with github, you can use your github account information to clone via https. Alternatively, you can also use SSH if your SSH key is registered with the project on github. Most commonly, to clone the public repository,

git clone https://github.com/psi4/psi4public.git [local-dir-name]

and to clone the private repository (if you have access),

git clone https://github.com/psi4/psi4.git [local-dir-name]

Telling git to ignore some files in the working directory

Sometimes you'll have files, like vi swap files with names like .file.swp, that you don't want git to worry about. This is easy to fix, just add the relevant filenames or wildcard filenames (like *.swp) to the file .gitignore. Alternatively, if this is a personal preference appropriate to a particular developer, and maybe not appropriate to adding to a shared repository, exclusions like this can be added to the local .git/info/exclude file. Psi4 already contains a .gitignore file with some common suffixes of files that should not be checked into the Psi4 repositories.

Adding, Deleting, and Moving Files

Adding a new file to the repository

To add a new file or files to the repository,

git add [filename-list]

and then check it in with

git commit -m "log message"

To remove a file from the repository

git rm filename

Follow this up with git commit

To move a file

git mv oldname newname

Note: we had trouble once trying to move a file in a different directory. Make sure you're moving a file in the same directory you're in.

Follow this up with a git commit

Checking in Changes

Checking in changes

Checking in changes to a local repository requires two steps: (1) staging the change, and (2) committing the change.

git add [filenames]
git commit -m "log message"

Note: if you have a really long log message, you can split it up and specify multiple -m "" arguments.

a shortcut to the above procedure is

git commit -m "log message" filenames

To automatically stage all changed files and commit the changes,

git commit -a -m "log message"

Note: git add filename will stage a commit of the changes to the given file at the time of the git add command. If you change the file again, you need to do git add again to stage the latest change.

If you have a large number of changed files, and you want to review them one by one, you can enter the "interactive" mode of git add by typing

git add -i

If you made many changes to a file, you might need more information than just the list of changed files. You can look at diffs with the diff command within git add -i, or via git diff. Alternatively, you can review the individual changes at staging time, and even selectively apply some and leave others unstaged, by using ::

git add -p

which enters "patch" mode (one change at a time). (You can also look at patches from within git add -i using the "patch" option in the interactive menu.)

To unstage a staged commit

 git reset HEAD file-to-unstage

Merging remote changes

To merge in changes from a remote repository:

git pull [remote-repository] [remote-branch-to-pull]

where remote-branch-to-pull does not include the origin/ prefix. This fetches and merges changes from the remote repository into the local repository. The merge may lead to conflicts. If that happens, see below for resolving conflicts.

To bring over changes from a remote repository but not merge them yet:

git fetch

This updates remote branches but does not merge changes into your local branch yet until you do a subsequent git commit. Not as commonly used as git pull.

To update the remote repository with (committed) changes from your local repository:

git push [--dry-run] [remote-repository] [refspec]

pushes committed changes from the local repository to the remote repository. The --dry-run option shows you what changes would be pushed. refspec is a tag, branch, or keyword like HEAD; e.g., mybranch:master pushes changes from mybranch to the remote master branch. The default remote repository is named origin.

Resolving merge conflicts

Sometimes you might get a conflict when trying to do a merge. In such a case, the file(s) with the conflict will have the standard "conflict markers." The beginning of the conflict region will be marked with "<<<<" symbols and the name of the current branch and file. That version of the file will continue until another marker of "======" symbols. Then, the conflicting version of the text will appear until closed off by ">>>>" symbols and the name of the conflicting branch and filename. Multiple areas of conflicting text might appear in a single file.

Edit the file by hand to resolve the conflict. Make sure you delete the conflict marker lines beginning with ">>>", "<<<", or "===". We don't want those checked in. Once the file is the way it's supposed to be, git add the conflicted file and do a git commit. In this particular case, if you leave off a log message, git will automatically create one specifying that it's related to a merge.

If the conflict isn't trivial to resolve, it can be useful to use git mergetool, which brings up a native text editor to show the conflict details clearly.

Occasionally, you will just want to accept the remote version of the file, or just keep your version of the file (although make sure you aren't losing any important changes by deciding to do this!). In such a case, to keep the remote version (and if you have git version 1.6.1 or newer), use git checkout --theirs [filename], and to keep your version, use git checkout --ours [filename]. This would be followed by git add [filename] and git commit.

Reverting a commit

To undo the latest commit, do

git revert

To undo multiple previous commits, stage them before committing with the -n flag

git revert -n [commit-id, or HEAD for most recent]
git revert -n [other-commit-id]
...

Revert the most recent commit first, then the earlier commits, in order. Finally,

git commit -m "revert [commit-id1] and [commit-id2]"

If you don't give -m, git will make an automatic revert log message for you

Seeing What's Changed

Checking status of working copy

To check on the status of the working copy

git status

This will list files that are modified, staged for checkin, etc.

Looking at differences

To see the differences between changes that are staged and what's in the repository,

git diff --cached

To see differences between the working directory AND what's staged vs the repository,

git diff HEAD

To see differences between the working tree and a previous revision,

git diff [revision-id]

To get a summary of how often files have changed since a revision or tag,

git diff --stat [tagname or revision-id]

To view the diffs that a revision created, use

git log -p

Summaries of changes (git log)

To print the git log

git log [-n] [--pretty=oneline]

will print the last n log entries.

To view the log starting from a given revision, give the revision number (just enough characters, like 7, to be unique) as an argument

git log [revision-id]

To view recent changes happening in the last certain timeframe,

git log --since="7 hours"

where instead of "7 hours" you can put "1 day" or "2009-12-01", etc.

To get a list of changes from a particular commit or tag until now, and in a short summary format,

git log --pretty-format:"%h %s" 1.0..HEAD

You can also use the character to mean the revision before a given revision. 6f1bf6f is the revision two revisions before 6f1bf6f.

Who to blame (git blame)

To see who changed a particular file, so you know who to blame for an error

git blame filename

To see who changed line 208 or the following 5 lines in filename,

git blame -L 208,+5 filename

You can also search text instead of linenumbers using regular expressions

git blame -L "regexp",+5 filename

Working with Branches

Branches can be created to allow you to continue development in a way that your changes are kept separate from the main trunk. Then, when you're done, you can merge changes from your branch back into the main trunk so that other developers will have them. For work that is meant to be kept private before release, use a branch on the private Psi4 repository. Otherwise, you can create a branch within the public repository. (Note: synchronization between private and public Psi4 repositories is only regularly performed for the main trunk, not branches)

To create a branch

git branch new-branch-name branch-to-create-from

where branch-to-create-from is a branch name or tag (often branch "master"). If the final parameter is not present, git will assume you want to create a branch off of the current working branch. Note that creating a new branch does NOT automatically switch the working copy to that branch. Use git checkout to do that. A shortcut to avoid having to do that is ::

git checkout -b new-branch-name branch-to-create-from

This will create a new branch and check it out in one step.

To switch the working copy to a different branch ::

git checkout branch-name

Warning: you can lose unsaved changes when doing this

To see a list of all available local branches ::

git branch

The name of the current working branch will be marked by a *

To see a list of all available remote branches ::

git branch -r

This lists branches in the remote repository. These branches can be checked out, but they should not be changed. If you want to change them, create a local branch from them first, and then make the change. The remote branches will be named with an origin/ prefix to keep them distinct from local branches.

To rename a branch

git branch -m oldname newname

Applying changes from one branch to some other branch (rebasing)

First, figure out what branch you want to absorb changes from another branch (usually, master). Assuming the other branch is already up-to-date and checked in, switch to the branch you want to absorb the changes::

git checkout master

Now, apply changes from the other branch on top of the current one::

git rebase other-branch-name

That should do it.

If you're done with the other branch once it's absorbed back into the master branch, you can delete it with ::

git branch -d other-branch-name

Squashed commits

If you had to experiment quite a bit in an alternate branch before getting something right, you might not want to commit all the individual changes, but just the final changes in the alternate branch relative to the main branch. In a case like this, finish checking in any changes on the alternate branch, check out the master branch ::

git checkout master

merge all the changes from the alternate branch in squashed form, ::

git merge --squash alternate-branch

note that squashed merges DO NOT update the master branch repository yet. They must be committed with ::

git commit -m "log message"

Cherry-picking merges

If you want to merge in just one change from another branch, you can use the fact that commit identifiers are unique across the entire repository to select the commit you want to merge. Get the commit ID by noting it after it's printed in git commit, or from the log file (you don't need the entire ID number from the logfile, the first 7 characters or so should do it). Then, switch to the branch you want to merge changes into, like

git checkout master

Finally,

git cherry-pick [commit-id]

To cherry-pick multiple commits, do ::

git cherry-pick -n [commit-id-1]
git cherry-pick -n [commit-id-2]
...

This will do the merges but will not automatically do a commit.

Working with Tags

Tags

You can tag a specific point in the repository by

git tag tag-name branch-name

You can view a list of all the tags in the repository by running

git tag

with no arguments

Making gzip archives

To create a gzip archive of the project as of some specific tag

git archive --format=tar
--prefix=proj-dir tagname
| gzip > proj-dir.tar.gz

where proj-dir is the name of the directory for the working copy, and tagname is the name of a tag for the version you want to archive.