Git tasks

From Dcmf

Jump to: navigation, search

{ Git | Git Setup | Git tasks | Git maintainer tasks }

This document attempts to describe how to use git for common tasks. Git push is the only command on this page that will attempt to write to a remote repository. All the others can be used and tested to get a better understanding of their use. One may find the full Git manual to be quite helpful, and there is a more detailed Git tutorial available.


Contents

Git commands for CVS equivalency

The goal is to give a one-to-one comparison to the familiar CVS commands. Additionally, you can take a look at Git for CVS users.

Creating your working repository/sandbox (cvs checkout)

This is how you get the source files to edit. For this example, we'll load from the primary git repository.

git clone git://git.kernel.org/pub/scm/git/git.git

The URL passed to git clone could also be a file path, http, or git+ssh.

This will create a remote repository called "origin", and remote heads for all the remote branches. It will create a local branch called "master" that matches the remote "master" branch. The files will be found in a subdirectory (in this case, "git"). Inside the first subdirectory, you will find all the revision-controlled files. From a code-hackery & build perspective, it is now just like normal.

Git stores all it's special information in a subdirectory called ".git". There will be only one, in the top-most directory of the repository. The branch heads will be stored in .git/refs/, and remote branch heads are in .git/refs/remotes/${remote}/${branch}.

Get the latest files (cvs update)

To retrieve all file changes for all remote branches. This does not alter any of the local source files

git fetch origin

To retrieve all file changes for a specific remote branches and merge it into your current local branch. This pulls "master" from the "origin" repo and merges the changes into the local "master" to update the source code.

git pull origin master

Which is the same as this

git fetch origin master
git merge origin/master

Switching to a specific branch/tag/revision/date

Branches

You can enumerate all the existing branches.

git branch -a
Creating branches (cvs tag -b)

Before one can use a branch, it has to exist. If one wishes to do some testing work on the side, it is simple to create a new branch. This creates the branch off the current branch head, but does not change the local files.

git branch new

This will create a new branch based on an existing branch/tag/revision:

git branch new master
git branch new v1.5.3.6
git branch new 005e2dfdf299a7d869fa9ec819171fba574f42eb

This will create a branch called "new" based on the branch "master". It will also switch to that branch, changing the local source code.

git checkout -b new master
Switching branches (cvs update -r)

This will switch and update the local files:

git checkout new

Switching to a tag or revision (cvs update -r)

This will switch and update the local files:

git checkout v1.5.3.6
git checkout 005e2dfdf299a7d869fa9ec819171fba574f42eb

As the warning will tell you, this will "detach" the repository head, and you will not be able to commit changes. However, it is possible to create a new branch to work on in a simple way.

git branch new

Switching to a specific date (cvs update -D)

At this time, I haven't found a way to switch to a date in one step. I now suspect that this is because with multiple branches, it wouldn't be clear which one was intended. The most effective way I've found is to use gitk to find the revision corresponding to the data and branch you want, and then use the above section to switch.

Creating tags (cvs tag)

When you think you have an interesting version (whatever that means to you), you may wish to tag a commit. These are not visible to other people unless you explicitly push tags.

git tag -d "tag description (long version)" name_tag_id

Editing files (cvs edit)

Editing is trivial: just open the file and go. There is nothing that has to be done first.

Adding new files (cvs add)

Much like CVS, one needs only to specify the file names. Directories are not added in git repos.

git add file1

Deleting dead files (cvs remove)

Again, this is very much like CVS.

git rm file1

Renaming/moving files

Git does not actually support moves, but the diff tool does notice when an added file looks like a deleted file. The file is then considered to have been moved, even if the two are not identical. The operation is usually a paired git rm and git add.

git mv old_name new_name

See what files are changed (cvs status)

git status

This effectively lists the state of the files so that it is understood what would happen during a commit.

Diffing files/tags/branches (cvs diff)

To see what has changed from the last commit, use the diff command

git diff

One can also check the difference between one or two branches

git diff -r master # How do the current files differ from the files checked into the "master" branch
git diff -r my_tag..new # Differences between the "my_tag" tag and the "new" branch
git diff -r f0c4881facabc0aa76f4b51871bd70c45effb508 # How do the current files differ from that commit
git diff -p # See what changed in the last commit

Git's diff uses it's own set of options that are similar but not identical to the normal diff.

Committing files (cvs commit)

By default, the git commit command will only commit files explicitly listed or added (even if there are already part of the repo). Use "-a" to get around this issue

git commit -m "commit message" -a
git commit -m "commit message" file 1 file2...

To finish a cvs-style commit, you need to push the changes back to the origin repo (not possible on this example)

git push origin master

Commits are not done on a file level like CVS, but against the entire repo. Therefore, it is easy to see the entire repo as it existed at any given moment without using "date/-D" args for commands.

Before pushing changes into a repository, we plan to email the change sets to a public list. Please be sure to do that to avoid surprising people.

From the Git tutorial:

A note on commit messages: Though not required, it's a good idea to begin the commit message with a single short (less than 50 character) line summarizing the change, followed by a blank line and then a more thorough description. Tools that turn commits into email, for example, use the first line on the Subject: line and the rest of the commit in the body.

Merging changes (cvs update -j branch)

Having changed the "new" branch, it is logical to want to merge the changes into the original.

git checkout master
git merge new

This are two steps because merges always are into the current branch. In this case, the merge is a "fast-forward". This is because one branch is a direct descendant of the other, which implies that only one branch contains any changes since the fork. If both branches were to have changed, it would be more complicated. Specifically, it would be much like running "cvs update" when a local file has changed and so has the server version. Usually, cvs/git handles the merging fine, but there can be conflicts.

Generally, a merge automatically creates a "merge commit" that lets the system know what happened. In the case of conflicts, the system does not automatically commit the changes. You will have to fix the conflicts, call git add file1..., and then call git commit (no args). This will complete the merge.

Mailing list interaction

Creating patches and emailing the list

This is an example "commit and email" I tried with my working MPI repository.

$ git status
    # On branch master
    # Changed but not updated:
    #   (use "git add <file>..." to update what will be committed)
    #
    #       modified:   mpich2/src/pmi/bgml/configure.in
    #
    no changes added to commit (use "git add" and/or "git commit -a")
$ git commit mpich2/src/pmi/bgml/configure.in
    Created commit 1c347d3: Make full dependency checking work in MPICH2
$ git format-patch -n -s -M -C 1c347d3~1  # One file is created for each commit in the range
    0001-Make-full-dependency-checking-work-in-MPICH2.patch
$ git send-email --smtp-server=localhost --from="Joe Ratterman <jratt@us.ibm.com>" --to="DCMF <dcmf@lists.anl-external.org>" 000*
    0001-Make-full-dependency-checking-work-in-MPICH2.patch
    (mbox) Adding cc: Joe Ratterman <jratt@us.ibm.com> from line 'From: Joe Ratterman <jratt@us.ibm.com>'
    (sob) Adding cc: Joe Ratterman <jratt@us.ibm.com> from line 'Signed-off-by: Joe Ratterman <jratt@us.ibm.com>
    '
    OK. Log says:
    Sendmail: /usr/sbin/sendmail -i jratt@us.ibm.com
    From: Joe Ratterman <jratt@us.ibm.com>
    To: Joe Ratterman <jratt@us.ibm.com>
    Cc: Joe Ratterman <jratt@us.ibm.com>
    Subject: [PATCH] Make full dependency checking work in MPICH2
    Date: Thu, 20 Dec 2007 09:52:38 -0600
    Message-Id: <1198165958-13439-1-git-send-email-jratt@us.ibm.com>
    X-Mailer: git-send-email 1.5.4.rc0.73.gce85b

    Result: OK

When the editor popped up on the git commit command, I added this as the message. The first line becomes the email subject, and the rest becomes the message body.

Make full dependency checking work in MPICH2.


I added the depend var to the configure program and told the TL makefile to build the dep list each time you do a build. This should help to simplify changes to header files. I forgot to change the configure program in pmi, so this fixes that.


In this operation,

  1. I first check git status to see exactly what I have changed.
  2. Next, I commit the file. Because I specified the file name, I don't have to stage it with git add. It tells me that it created commit id "1c347d3".
  3. I make a patch for that commit. 1c347d3~1 is the first parent of 1c347d3, and is the basis for the diff. The "-s" option adds a sign-off line to the patch to say who approves of the changes. The "-M -C" options say to detect file moves and copies (respectively). "-n" requests that the patch emails be numbered (e.g. [PATCH 2/5]).
  4. The last step is to email the files. The patches are included as inline text, not attachments.

Getting patches from the list

You can use git am to get code from the mailing list. The tool is designed to work well with the common Unix email storage systems (mailbox and Maildir), but you can makeit work with Notes. You will just have to export the Notes as text and save them to the system before you can import them.

Git commands to help keep you sane

These are commands that have no direct CVS correspondence. However, they do things that are already commonly done in CVS.

Visualizing the repository

This has no CVS equivelent but is very useful. It will show all branches and tags that are stored in your copy of the repository. It does a great job of displaying branch interaction. There are a number of window panes that can be resized to focus on your primary interest.

gitk --all

Starting the GUI for Git

This is not a tool that I've used a lot, but it can be quite helpful. It makes a number of features easier to access without needing to remember so many commands.

git gui &

Finding a bad change by binary search (cvs update -D)

Git has a built-in "bisect" tool to help find a cset that introduced a new bug. It will do an automated binary search between two commit points to find the first one that was broken. This is best for the sort of error that went unnoticed for a while, and has been part of the software for a while. You will need to know two things: a revision where the code worked, and a revision where it doesn't. For this example, let's say that "master(1ab58e8d6f728cdde0057f7ee88daab3a1c2d06f)" is broken, but "92b7ba16b7605f70cac845d4a8d0162392ac0223" runs fine.

  1. First, one must set up a few thing so the tool knows what it will be trying
    git bisect start # This sets up the bisect working branches
    git bisect bad  1ab58e8d6f728cdde0057f7ee88daab3a1c2d06f # Same as master at time of writing
    git bisect good 92b7ba16b7605f70cac845d4a8d0162392ac0223 # Good example
    • It is sometimes helpful to visualize the binary search. This command brings up a gitk interface showing only the area being searched. It lists that start- and end-points, as well as the current location.
      git bisect visualize &
  2. At this point, the bisect tool will create and checkout a bisect branch. This will change the local files to their state at the time of that commit, and you need only to re-compile and run a test. If the test passes, then you say
    git bisect good

    If the test failed, you say

    git bisect bad
  3. The tool will automatically switch to a new point in the tree based on your input—repeat the above step. Part of the output is the number of tests cset left to test. Eventually, it will say Bisecting: 0 revisions left to test after this. After you give a good/bad response to that one, it will print the info for the first failing commit. It will look something like this:
    1cdade2c4cb27f648a98d326ef3db523b6afafa7 is first bad commit
    commit 1cdade2c4cb27f648a98d326ef3db523b6afafa7
    Author: J. Bruce Fields <bfields@citi.umich.edu>
    Date:   Sat Mar 3 22:53:37 2007 -0500
    
        user-manual: recovering from corruption
        
        Some instructions on dealing with corruption of the object database.
    
    :040000 040000 3706a56bcc5a8aa444250231a3cd766e3a44d3f3 c0caeb229a77570786e8796034e904a57c94fcb7 M      Documentation

    Given the first line listing the bad commit, you can view the difference between it and its parent.

    git diff -p -r 1cdade2c4cb27f648a98d326ef3db523b6afafa7

    Hopefully, that will tell you were the bug came from.

  4. Lastly, you will need to reset the repo to its original state
    git bisect reset

This method, like the comparable CVS version, works best when all commits tend to compile and run successfully. That said, it is certainly not mandatory that be the case.

Fixing bad things

Restoring a file to a tracked state

If you find that you completely messed up a file you've been editing, simply check it out to start fresh.

git checkout path/file.c

Reverting a commit

There are a few ways to revert a commit you have made. The best choices depend on a couple options:

  1. Has anyone pulled from your tree (or you pushed your code up)?
  2. How honest do you want to be about the mistake?
Completely blowing away a mistake

If nobody has a copy of your tree, it is ok to mess with the history in bad ways. Completely deleting a commit causes lots of trouble when someone is building on it, but it's fine if you are the only one. This will not restore the files, but only change the history, by setting the current HEAD to be exactly the commit id specified.

git reset 757f58ed380a17225e8de9f461083a21d0a2c97c
Using another commit to revert an earlier mistake

This is the correct way, especially if you have pushed your tree. Simply spcify the commit id of the bad source, and it will apply a reverse patch to remove the code.

git revert 84ef033832af9e0be886214c70b2006b08630072
Personal tools