Gromacs

Git Tips & Tricks

    Introduction

    This page is/tries to be a cheatsheet for how to effectively use git for various tasks. At this time, it is very incomplete, but content can be added as need arises. Several empty sections exist as a suggestion for organizing the content. There is some information related to this also on other wiki pages, which in the long run would be best moved here. The intent is that this contains mainly technical information: how to perform various tasks etc. Policies on, e.g., where to push bug fixes or how to manage public branches may be better dealt with on separate pages.

    Suggestion for using this page is that anyone is free to add questions, even if they don't know the answers to, and people can then contribute the answers. Also, if you know of a more effective way of doing something, feel free to edit an answer to share that. Or if you find another web page that explains some aspect very well, feel free to add a link as an answer to a question. There is probably no substitute for trying things out to learn how to use git. So this page mainly attempts to provide pointers on where to get started. Once you know the basic commands, reading man pages and trying things out should give more details.

    Setting up git

    Q: How do I get the GROMACS development source code for read-only access?

    A: Use

    git clone git://git.gromacs.org/gromacs.git
    

    This creates a gromacs/ directory under the current directory, retrieves the development code, and checks out the development branch (called master).

    Note that this provides you with read access only. You can also do your modifications to this source code, but if you are interested in contributing patches, read the answer below. Also note that at times, the sync from Gerrit (which is the primary repository) to git.gromacs.org breaks, so the latter may not have all the latest commits (usually, it does, though).

    Q: How do I set up git to contribute patches to GROMACS (also for developers)?

    A: You need to do the following steps:

    1. You will need an ssh key. Generate one with ssh-keygen (or some other means) if you don't have one. You will also need an OpenID.
    2. Register to https://gerrit.gromacs.org using your OpenID, choose a username and upload your public ssh key.
    3. Use
      git clone ssh://[username]@gerrit.gromacs.org/gromacs.git
      to clone the repository to create a new repository (replace [username] with your chosen username). If you already have a repository cloned from git.gromacs.org, you can also use
      git remote set-url origin ssh://[username]@gerrit.gromacs.org/gromacs.git
      
      to replace git.gromacs.org with gerrit, or
      git remote add upload ssh://[username]@gerrit.gromacs.org/gromacs.git
      to add a new remote to use for pushing.
    4. Configure your user name and e-mail to match those registered to Gerrit:
      git config [--global] user.name "Your Name"
      git config [--global] user.email "your.name@domain.org"
      If necessary, register the e-mail address you want to use with Gerrit.
    5. Get the Change-Id commit-msg hook (https://gerrit.gromacs.org/Documenta...-changeid.html):
      scp -p [username]@gerrit.gromacs.org:hooks/commit-msg .git/hooks/

    If you are a core developer, e-mail Roland to get full review permissions.

    Q: Are there some other useful git configuration settings?

    A: If you need to work with branches that have large differences (in particular, if a lot of files have moved), it can be helpful to set

    git config diff.renamelimit 5000
    

    to increase the limit of inexact renames that Git considers. The default value is not sufficient, for example, if you need to do a merge or a cherry-pick from release-4-6 to master.

     

    Basic git usage

    Q: How do I use git rebase (also git pull --rebase)?

    A: Assume you have a local feature branch checked out, that it is based on master, and master has gotten new commits. You can then do

    git rebase master
    

    to move your commits on top of the newest commit in master. This will save each commit you did, and replay them on top of master. If any commit results in conflicts, you need to resolve them as usual (including marking them as resolved using git add), and then use

    git rebase --continue
    

    Note that unless you are sure about what you are doing, you should not use any commands that create or delete commits (git commit, or git checkout or git reset without paths). git rebase --continue will create the commit after conflicts have been resolved, with the original commit message (you will get a chance to edit it).

    If you realize that the conflicts are too messy to resolve (or that you did a mistake that resulted in messy conflicts), you can use

    git rebase --abort
    

    to get back into the state you started from (before the original git rebase master invocation). If the rebase is already finished, and you realize you did a mistake, you can get back where you started with (use git log <my-branch>@{1} and/or git reflog <my-branch> to check that this is where you want to go)

    git reset --hard <my-branch>@{1}
     

    Managing local branches

    This section contains more advanced git commands for managing local branches: splitting and merging commits, editing existing commits etc. This is in particular useful for preparing changes for review: it is easier to review small changes that only do one thing.

    Q: I have multiple independent changes in my working tree. How do I create multiple commits from this?

    A: Use

    git add [-p] [file]
    

    to add one independent change at a time to the index. Use

    git diff --cached

    to check that the index contains the changes you want. You can then commit this one change:

    git commit

     If you want to test that the change works, use to temporarily store away other changes, and do your testing.

    git stash

    If the testing fails, you can amend your existing commit with git commit --amend. After you are satisfied, you can push the commit into gerrit for review. If you stashed away your changes and you want the next change to be reviewed independently, do

    git reset --hard HEAD^
    git stash pop

    (only do this if you pushed the previous change to gerrit, otherwise it is difficult to get the old changes back!) and repeat until each independent change is in its own commit. If you skip the git reset --hard step, you can also prepare a local feature branch from your changes. 

    Q: I have a local feature branch with multiple commits. How do I edit an earlier commit?

    A: If you want to edit the latest commit, you can simply do the changes and use

    git commit --amend

    If you want to edit some other commit, and commits after that have not changed the same lines, you can do the changes as usual and use

    git commit --fixup <commit>

    or

    git commit --squash <commit>

    where <commit> is the commit you want to change (the difference is that --fixup keeps the original commit message, while --squash allows you to input additional notes and then edit the original commit message during git rebase -i). You can do multiple commits in this way. You can also mix --fixup/--squash commits with normal commits. When you are done, use

    git rebase -i --autosquash <base-branch>

    to merge the --fixup/--squash commits to the commits they amend. See separate question on git rebase -i on how to choose <base-branch>.

    In this kind of workflow, you should try to avoid to change the same lines in multiple commits (except in --fixup/--squash commits), but if you have already changed some lines and want to edit an earlier commit, you can use

    git rebase -i <base-branch>
    

    but you likely need to resolve some conflicts later. See git rebase -i question later.

    Q: How do I split a commit?

    A: The instructions below apply to splitting the HEAD commit; see above how to use git rebase -i to get an earlier commit as HEAD to split it.

    The simplest case is if you want to split a commit A into a chain A'-B-C, where A' is the first new commit, and contains most of the original commit, including the commit message. Then you can do

    git reset -p HEAD^ [-- <paths>]
    git commit --amend

    to selectively remove parts from commit A, but leave them in your working tree. Then you can create one or more commits of the remaining changes as described in other tips.

    If you want to split a commit A into a chain where the original commit message is reused for something else than the first commit (e.g., B-A'-C), then you can do

    git reset HEAD^

    to remove the HEAD commit, but leave everything in your working tree. Then you can create your commits as described in other tips. When you come to a point where you want to reuse the original commit message, you can use

    git reflog

    to find how to refer to your original commit as HEAD@{n}, and then do

    git commit -c HEAD@{n}

    Q: How do I use git rebase -i to only edit local commits?

    A: Assume that you have a local feature branch checked out, this branch has three commits, and that it is based on master. Further, assume that master has gotten a few more commits after you branched off. If you want to use git rebase -i to edit your feature branch (see above), you probably want to do

    git rebase -i HEAD~3
    

    followed by a separate

    git rebase master

    The first command allows you to edit your local branch without getting conflicts from changes in master. The latter allows you to resolve those conflicts in a separate rebase run. If you feel brave enough, you can also do both at the same time using

    git rebase -i master

    Interacting with Gerrit

    This section is intended for using git to interact with gerrit; interacting with the web UI may be better dealt with on a separate page.

    Q: How do I move a change from a branch to another?

    A: Moving one or a few changes is most easily done using git cherry-pick. To move a single change, first do

    git checkout <target-branch>

    Then, open the change/patch set in Gerrit that you want to move, select "cherry-pick" in the Download section for that patch set, and copy/paste the given command:

    git fetch ... refs/changes/... && git cherry-pick FETCH_HEAD

    Resolve any conflicts and do

    git commit [-a]

    You can also cherry-pick multiple changes this way to move a small topic branch. Before pushing the change to Gerrit, remove the lines about conflicts from the commit message, as they don't serve any useful purpose in the history. You can type that information into the change as a Gerrit comment if it helps the review process. Note that Gerrit creates a new change for the target branch, even if Change-Ids are same in the commits. You need to manually abandon the change in the wrong branch.

    Other topics

    Merging a development branch into master

    For the following, we assume the development branch is release-4-6. Normally, check out master branch and do git merge release-4-6. However, if there are commits that should not be included in the history of master, then some fancy footwork is required to omit "unwanted-commit" (which might be a hash, or a local tag, or whatever). One approach is 

    git merge unwanted-commit~1
    git merge -s ours unwanted-commit
    git merge release-4-6
    

    (Maybe the second command should have -m, as well.) This leaves the repo with three merge commits for a chunk of history, which is a little noisy. A merge commit is just a normal commit with two parents, so now that we know how we want the tree to look after the three stages above, we can be brutal and generate a commit that has that tree (by recording the hash of that tree) and has the two parents that the combined merge commit would have, i.e. the old HEAD of master and the current HEAD of release-4-6. So in bash

    treehash=$(git log -1 HEAD --pretty=%T)
    git reset --hard $(git cat-file commit HEAD | sed '1,/^$/d' | git commit-tree $treehash -p old-HEAD-of-master -p release-4-6)

     

    Then, use git commit --amend to note that the unwanted commit was excluded, etc.

    Managing long-lived merge commits

    You can't cherry-pick a merge commit from gerrit and have it stay as a merge commit, so that is not a recommended thing to try to do. Neither can you rebase a merge commit easily. But https://gist.github.com/jaysoffian/2622264 helps with the latter.

    Page last modified 20:07, 12 Jan 2014 by tmurtola