Using Git

From Dogtag
Jump to: navigation, search

Overview

This page shows how to accomplish some common development procedures using Git. There are usually multiple ways to accomplish the same thing in Git, so keep in mind that the steps below are only one way of doing things. There may be other shortcuts or different ways of doing things that you prefer.

Creating a Local Repository

Our git repository is located on fedorahosted.org here. You can use one of the following commands to clone our Git repository (use the "ssh://" version of the clone command if you are authenticating with a Fedora account, otherwise use the anonymous "git://" version):

git clone git://git.fedorahosted.org/git/pki.git
git clone ssh://git.fedorahosted.org/git/pki.git

Developing on a Local Branch

The preferred way of doing development is to do your work on a local branch. This makes it easy to isolate fixes you are working on from each other. It also helps to prevent merge issues when you are ready to push your changes up to the remote repository. The following example shows the commands a developer would use to fix a bug on a local branch.

The first step is to create a local branch. You should name your branch something descriptive, such as the bug number you are working on, or the name of the feature you are developing. Here is an example of creating a new local branch:

$ git checkout -b my_branch
Switched to a new branch 'my_branch'

This command creates a local branch named my_branch and switches to it. If you ever want to check and see what branch you are on, you can do that with the following command:

$ git branch
  master
* my_branch

This shows that we have two local branches. The master branch is the branch created when we initially cloned the remote repository. This branch is set to track the development tip. The * next to my_branch means that we are currently on my_branch. If you ever want to switch the branch that you are on, that is done with the checkout command. Here is an example that shows switching to the master branch, then back to my_branch:

$ git checkout master
Switched to branch 'master'
$ git branch
* master
  my_branch
$ git checkout my_branch
Switched to branch 'my_branch'

Now that we are on our branch, we would change the source code to fix our bug. This example just makes a simple change to the copyright block in pki/README. After updating the source, we can see the changes with the diff command:

$ git diff
diff --git a/pki/README b/pki/README
index 2c2c36b..f6b2e83 100644
--- a/pki/README
+++ b/pki/README
@@ -1,5 +1,5 @@
 # BEGIN COPYRIGHT BLOCK
-# (C) 2008 Red Hat, Inc.
+# (C) 2011 Red Hat, Inc.
 # All rights reserved.
 # END COPYRIGHT BLOCK

We can see the status of the files in the source tree according to Git with the status command:

$ git status
# On branch my_branch
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   pki/README
#
no changes added to commit (use "git add" and/or "git commit -a")

This shows us that the only modified file in the source tree is pki/README. Note also that is says that the change to this file is not staged for commit. If you want to stage a file for commit, you can use the add command. Here is an example of staging the pki/README file for commit and then showing the status of it:

$ git add pki/README
[nkinder@localhost pki]$ git status
# On branch my_branch
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   pki/README
#

Now we can see that our modifications to pki/README are staged for commit. We will use the commit command at this point to check our changes in on our local branch.

$ git commit
[my_branch 535cf2d] Updated copyright in README file
 1 files changed, 1 insertions(+), 1 deletions(-)

When you run the commit command, you will have to fill in the commit message for your commit. The format should be a one-line summary of the fix (including the applicable bug or ticket number if appropriate), followed by a blank line and a more complete description of the change. Now that our change is commited to our local branch, we can look at the commit history to confirm that our commit is there. We'll also check the status of the tree to show that git considers the tree to be up to date.

$ git log -1
commit 535cf2d38b876ac784bd3449dea885b9da4b1c3c
Author: Nathan Kinder <nkinder@redhat.com>
Date:   Mon Jan 16 11:50:52 2012 -0800 

    Updated copyright in README file
   
    The date in the copyright of README needed to be updated.
$ git status
# On branch my_branch
nothing to commit (working directory clean)

The log command used above shows the last commit. If you want to see more history, you can simply change the -1 argument to the last number of commits that you want to see.

At this point, you probably want to generate a patch file that you can send out for review. This is accomplished with the following command:

$ git format-patch -1
0001-Updated-copyright-in-README-file.patch

This patch file can be e-mailed out to the development list for review. Here is what the contents of the patch for this example look like:

$ cat 0001-Updated-copyright-in-README-file.patch 
From 535cf2d38b876ac784bd3449dea885b9da4b1c3c Mon Sep 17 00:00:00 2001
From: Nathan Kinder <nkinder@redhat.com>
Date: Mon, 16 Jan 2012 11:50:52 -0800
Subject: [PATCH] Updated copyright in README file

The date in the copyright of README needed to be updated.
---
 pki/README |    2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/pki/README b/pki/README
index 2c2c36b..f6b2e83 100644
--- a/pki/README
+++ b/pki/README
@@ -1,5 +1,5 @@
 # BEGIN COPYRIGHT BLOCK
-# (C) 2008 Red Hat, Inc.
+# (C) 2011 Red Hat, Inc.
 # All rights reserved.
 # END COPYRIGHT BLOCK
 
-- 
1.7.7.5

Pushing a Patch to the Remote Repository

Following on our previous example of developing on a branch, we're going to go through how you would push the change up to the remote master branch. Assuming that your review is complete, the first step is to see if anything has changed in the remote repository since you created your branch. If there have been any remote changes, we will need to rebase our changes on top of them.

Here we will switch to our master branch and pull any changes that may have happened:

$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
[nkinder@localhost pki]$ git pull
Updating 0491736..8ff3cae
Fast-forward
 .../com/netscape/cms/servlet/base/model/Link.java  |   88 +++++++++++++++
 .../com/netscape/cms/servlet/key/KeysResource.java |   55 ++++++++--
 .../com/netscape/cms/servlet/key/model/KeyDAO.java |   32 ++++--
 .../cms/servlet/key/model/KeyDataInfos.java        |   87 ++++++++++++++
 .../cms/servlet/request/KeyRequestsResource.java   |   84 ++++++++++++---
 .../cms/servlet/request/model/KeyRequestDAO.java   |  118 ++++++++++++++++++--
 .../cms/servlet/request/model/KeyRequestInfos.java |   89 +++++++++++++++
 7 files changed, 506 insertions(+), 47 deletions(-)
 create mode 100644 pki/base/common/src/com/netscape/cms/servlet/base/model/Link.java
 create mode 100644 pki/base/common/src/com/netscape/cms/servlet/key/model/KeyDataInfos.java
 create mode 100644 pki/base/common/src/com/netscape/cms/servlet/request/model/KeyRequestInfos.java

We can see that git detected that our local master branch was behind the remote master branch. We use the pull command to actually pull down the changes. You should always explicitly do a pull just to double check if anything has changed in the remote repository.

The next step is to rebase our changes on top of this change that we pulled down from the remote master branch. We do that by switching to our development branch and doing a rebase. I've added some steps to show the git log in the following example to give an idea of how rebase works.

$ git checkout my_branch
Switched to branch 'my_branch'
$ git log -2
commit f6234a5ad913cc98f5bda74b5c84963753352c10
Author: Nathan Kinder <nkinder@redhat.com>
Date:   Mon Jan 16 11:50:52 2012 -0800

    Updated copyright in README file
    
    The date in the copyright of README needed to be updated.

commit 0491736b2570447391835bc2e5282d809f0de4f1
Author: Jack Magne <jmagne@redhat.com>
Date:   Fri Jan 13 16:57:06 2012 -0800

    Big numbers fix for CA and DRM.
    
    This patch resolves multiple issues related to use of big numbers on CA and DRM
    It also provides a fix for incomplete recovery requests causing null pointer exception.
    
    Bugs: 756133, 758505.
    
    Complete formatting changes for QueryRec.java.
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: Updated copyright in README file
$ git log -3
commit 5516ece394889facb511265e43a70340b19cea35
Author: Nathan Kinder <nkinder@redhat.com>
Date:   Mon Jan 16 11:50:52 2012 -0800

    Updated copyright in README file
    
    The date in the copyright of README needed to be updated.

commit 8ff3cae8509bf5527d49166f818776cfc618555c
Author: Ade Lee <alee@redhat.com>
Date:   Thu Jan 12 12:24:24 2012 -0500

    Enhanced new REST search interface for keys and key requests
    
    Defined parameters that can be searched for in key and keyrequest searches.
    
    Searches for KeyRequests and Keys will perform VLV searches if those searches are defined.
    The results will include links to next and previous pages in the results.
    
    Also added maxTime and maxResults parameters for regular searches.  These will be operational
    unless they exceed server defined limits - which are enforced at the repo level.
    
    Modified link URL from "link" to "Link"

commit 0491736b2570447391835bc2e5282d809f0de4f1
Author: Jack Magne <jmagne@redhat.com>
Date:   Fri Jan 13 16:57:06 2012 -0800

    Big numbers fix for CA and DRM.
    
    This patch resolves multiple issues related to use of big numbers on CA and DRM
    It also provides a fix for incomplete recovery requests causing null pointer exception.
    
    Bugs: 756133, 758505.
    
    Complete formatting changes for QueryRec.java.

The commit history shown in the logs shows how the rebase rewinds our local commit, applies any changes from the branch we are rebasing from (master in this case), then re-applies our local commit.

Now we have our patch all rebased on top of the remote master branch, so we can go ahead and push it up to the remote repository. We want to push this up to the remote master branch. Our local master branch is set-up to track the remote master, so the first thing we need to do is to bring our change over to our local master branch. At that point, we can push it up to the remote master branch. Here is how we do this:

$ git checkout master
Switched to branch 'master'
$ git pull
Already up-to-date.
$ git merge my_branch
Updating 8ff3cae..5516ece
Fast-forward
 pki/README |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
$ git log -2
commit 5516ece394889facb511265e43a70340b19cea35
Author: Nathan Kinder <nkinder@redhat.com>
Date:   Mon Jan 16 11:50:52 2012 -0800

    Updated copyright in README file
    
    The date in the copyright of README needed to be updated.

commit 8ff3cae8509bf5527d49166f818776cfc618555c
Author: Ade Lee <alee@redhat.com>
Date:   Thu Jan 12 12:24:24 2012 -0500

    Enhanced new REST search interface for keys and key requests
    
    Defined parameters that can be searched for in key and keyrequest searches.
    
    Searches for KeyRequests and Keys will perform VLV searches if those searches are defined.
    The results will include links to next and previous pages in the results.
    
    Also added maxTime and maxResults parameters for regular searches.  These will be operational
    unless they exceed server defined limits - which are enforced at the repo level.
    
    Modified link URL from "link" to "Link"
$ git push

It is a good idea to do a quick pull to check if anything has changed one last time before pushing. It is also a good idea to check the commit hsitory right before pushing to see if there is a git merge commit. A merge commit is a commit that git automatically makes if you didn't rebase your fix ahead of time. In general, we want to avoid merge commits. If you see a merge commit, you will need to rewind your tree, rebase your fix, and then go through the push procedure again.

I did not acutally perform the push here in my example, as I didn't want to commit this example change. When a change is actually pushed, git will output some information about the result of the push.

Rewinding Commits

You will have times where you want to rewind your source tree, or maybe just your commit history. This can be easily done with the reset command. The first thing to understand is that there are two types of resets, soft and hard.

A soft reset is what you would use when you want to leave the source files on disk alone, but you want to back out the commit history. A common case where this is needed is when a patch needs to be changed due to review feedback. Instead of making a second patch to address the review feedback, you should rewind the commit history, fix the source files, then recommit everything in a single patch. Here is an example that backs out my example change (which was never pushed):

$ git log -2
commit 5516ece394889facb511265e43a70340b19cea35
Author: Nathan Kinder <nkinder@redhat.com>
Date:   Mon Jan 16 11:50:52 2012 -0800

    Updated copyright in README file
    
    The date in the copyright of README needed to be updated.

commit 8ff3cae8509bf5527d49166f818776cfc618555c
Author: Ade Lee <alee@redhat.com>
Date:   Thu Jan 12 12:24:24 2012 -0500

    Enhanced new REST search interface for keys and key requests
    
    Defined parameters that can be searched for in key and keyrequest searches.
    
    Searches for KeyRequests and Keys will perform VLV searches if those searches are defined.
    The results will include links to next and previous pages in the results.
    
    Also added maxTime and maxResults parameters for regular searches.  These will be operational
    unless they exceed server defined limits - which are enforced at the repo level.
    
    Modified link URL from "link" to "Link"
$ git reset --soft 8ff3cae8509bf5527d49166f818776cfc618555c
$ git status
# On branch my_branch
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   pki/README
#
$ git log -1
commit 8ff3cae8509bf5527d49166f818776cfc618555c
Author: Ade Lee <alee@redhat.com>
Date:   Thu Jan 12 12:24:24 2012 -0500

    Enhanced new REST search interface for keys and key requests
    
    Defined parameters that can be searched for in key and keyrequest searches.
    
    Searches for KeyRequests and Keys will perform VLV searches if those searches are defined.
    The results will include links to next and previous pages in the results.
    
    Also added maxTime and maxResults parameters for regular searches.  These will be operational
    unless they exceed server defined limits - which are enforced at the repo level.
    
    Modified link URL from "link" to "Link"

You can see that we need to supply the commit hash for the last commit that we want to keep. You can get this hash from the commit log message. After doing the soft reset, we can see that the status command shows that we still have our local modifications, but it's as if they were never committed to the local tree.

A hard reset is what you would use to completely back out a commit, or even local uncommited changes. This will wipe out the changes to the source tree associated with the commit as well as the commit history. You should be careful when doing a hard reset to make sure it's what you want to do, as you can lose local changes. Remote changes are not a concern, as you can simply re-pull them from the remote repository. Here is an example of a hard commit that continues from our soft commit example above:

$ git reset --hard 8ff3cae8509bf5527d49166f818776cfc618555c
HEAD is now at 8ff3cae Enhanced new REST search interface for keys and key requests
$ git status
# On branch my_branch
nothing to commit (working directory clean)

We can see that our local change is completely gone now.

Working with Remote Branches

When you want to make changes on a remote branch, the first thing that you need to do is to create a local branch to track the remote branch. This will work the same way that our local master branch tracks the remote master branch.

The first step is to list the remote branches. This is done the same way as listing local branches, except we add the -r option:

$ git branch -r
 origin/DOGTAG_9_BRANCH
 origin/HEAD -> origin/master
 origin/IPA_v2_RHEL_6_ERRATA_BRANCH
 origin/PKI_8_0_ERRATA_BRANCH
 origin/PKI_8_1_ERRATA_BRANCH
 origin/PKI_8_BRANCH
 origin/autoformat
 origin/autoformat2
 origin/master

In this case, we are going to assume that we want to port a change that we have on our local master branch over to the remote DOGTAG_9_BRANCH branch. To create our local branch, we simply checkout using the remote branch name:

$ git checkout DOGTAG_9_BRANCH
Branch DOGTAG_9_BRANCH set up to track remote branch DOGTAG_9_BRANCH from origin.
Switched to a new branch 'DOGTAG_9_BRANCH'

If you were simply doing new development on this branch, you could just do your development work here (or create a local branch from this point to avoid merge issues). For our example here, we're going to be porting a change from the master branch. This is done with the cherry-pick command. Here is an example of how we would do this (along with some commit log output to help show how it works):

$ git branch
* DOGTAG_9_BRANCH
  master
  my_branch
[nkinder@localhost pki]$ git log -1
commit 8ebf890b913ffbf4cb40c09ebc9e229989303095
Author: Ade Lee <alee@redhat.com>
Date:   Wed Jan 4 00:08:03 2012 -0500

    BZ 771357 - sslget does not work after FEDORA-2011-17400 update, breaking FreeIPA install
    
    Modified sslget doIO() function to be able to handle small reads.
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
$ git log -1
commit 5516ece394889facb511265e43a70340b19cea35
Author: Nathan Kinder <nkinder@redhat.com>
Date:   Mon Jan 16 11:50:52 2012 -0800

    Updated copyright in README file
    
    The date in the copyright of README needed to be updated.
$ git checkout DOGTAG_9_BRANCH
Switched to branch 'DOGTAG_9_BRANCH'
$ git cherry-pick 5516ece394889facb511265e43a70340b19cea35
[DOGTAG_9_BRANCH f4e12fc] Updated copyright in README file
 1 files changed, 1 insertions(+), 1 deletions(-)
$ git log -2
commit f4e12fc3ab173dfe43d47248f11b2f39bafd6cab
Author: Nathan Kinder <nkinder@redhat.com>
Date:   Mon Jan 16 11:50:52 2012 -0800

    Updated copyright in README file
    
    The date in the copyright of README needed to be updated.

commit 8ebf890b913ffbf4cb40c09ebc9e229989303095
Author: Ade Lee <alee@redhat.com>
Date:   Wed Jan 4 00:08:03 2012 -0500

    BZ 771357 - sslget does not work after FEDORA-2011-17400 update, breaking FreeIPA install
    
    Modified sslget doIO() function to be able to handle small reads.

You can see that we only need to provide the hash of the commit that we want to port to our branch. The end result is a committed fix on our branch. At this point, you could do a push which would push the commit to the remote DOGTAG_9_BRANCH.

References