Git

From Texas Instruments Wiki
Jump to: navigation, search

Git is a software tool that is used widely in the linux community for source code management of software projects. There are many open-source projects utilizing Git for source code management, including the linux kernel as well as the Git project itself.

Git is designed to enable a distributed development environment such that multiple developers working on a project need not have a centralized server where the source code is developed and maintained. In other words, Git allows developers working on a project to create local copies of the project and permit development where network access may not exist.

Git provides full revision tracking capabilities and is designed to be efficient, fast and reliable solution for source code management.


Installing Git

Depending on what Linux distribution you are using on your Linux host there are different options for installing Git. For example, distributions such as Fedora, Ubuntu and Debian have their own software management tool for downloading and installing additional software packages. The Fedora software package manager is called “yum” and the Ubuntu/Debian software package manager is called “apt” (advanced package tool). Hence, for distributions such as Fedora, Ubuntu or Debian, you could use the software package manager to download and install Git. Alternatively, for any Linux distribution you can always download the source, build and install. The following subsections describe how to install Git on Fedora, Ubuntu and Debian distributions and how to install Git from source.

Installing Git on a Ubuntu/Debian Host

To install Git on a Ubuntu/Debian host using the “apt” package manager, as root user simply execute the following command.

apt-get install git-core

Installing Git on a Fedora Host

To install Git on a Fedora host using the “yum” package manager, as root user simply execute the following command.

yum install git-core

Installing Git using source on a Linux Host

To install Git using source on a Linux host:

  1. Download the latest Git package from here. For example, the latest available at time of writing was v1.6.0, so file git-1.6.0.tar.bz2 was downloaded.
  2. Extract the archive.
    tar jxf git-1.6.0.tar.bz2
  3. Change directory so that you are in the directory where you extracted Git into.
    cd git-1.6.0
  4. Execute the “configure” script to make sure that the Linux host has all the required software packages to run Git. Note if packages are missing then please consult the distributions website on how to obtain these packages.
    ./configure
  5. Build the Git package as user by executing the command.
    make prefix=/usr all
  6. Install the Git package as root user by executing the command.
    make prefix=/usr install

Configuring Git

Before you start using Git, there are some a few items that you may wish to configured first. When developers are making changes to a Git repository, it is very useful to track which developers made which modifications. To avoid each developer having to manually enter their details for each change they make, users can simply specify the following environment variables and Git will automatically embed these details when changes are made.

GIT_AUTHOR_NAME           ; name of author
GIT_AUTHOR_EMAIL          ; email address of author
GIT_COMMITTER_NAME        ; name of committer
GIT_COMMITTER_EMAIL       ; email address of committer

When changes are made to a repository, Git allows the user to enter a short description summarizing what modifications have been made. This is another feature that is very beneficial for monitoring changes. To allow a user to enter such a description, Git will start the default text editor on the host. You can override the default text editor used by Git, by setting the following environment variable.

GIT_EDITOR                ; editor used by git

If you are using a BASH shell, then the above environment variables can be defined as follows.

export GIT_AUTHOR_NAME='Joe Blogs'
export GIT_AUTHOR_EMAIL=jblogs@whatever.com
export GIT_COMMITTER_NAME='Joe Blogs'
export GIT_COMMITTER_EMAIL=jblogs@whatever.com
export GIT_EDITOR=vim

Cloning Git Trees

A very useful feature of Git is the ability to clone repositories that are located on remote hosts. Git supports two protocols for cloning remote repositories and these are HTTP and the Git protocol. The Git protocol is more efficient than HTTP. However, if you host is situated behind a firewall, then chances are the port used by Git will be blocked and so HTTP may be the only protocol you can use.

To clone a remote repository, such as the DaVinci Linux Git tree, you first need to find out the URL where the Git tree is located. For example, the DaVinci Linux Git tree is hosted here. Viewing this website, you will then find a description of the Git tree and you will be able to browse the history of the Git tree. This website for the DaVinci Linux Git tree provides the following information for cloning the Git tree.

To clone one of these trees:

git-clone git://source.mvista.com/git/ + project

Hence, to clone the repository using the Git protocol you would execute something like the following.

git-clone git://source.mvista.com/git/linux-davinci-2.6.git /home/jblogs/linux-davinci-2.6.git

Please note that if you are behind a firewall the Git protocol may not work. It is common for firewalls to block the port used by the Git protocol. If working behind a firewall see Using Git Behind a Firewall

Now although the website for the DaVinci Linux Git tree does not state explicitly that it supports the HTTP protocol for cloning Git tree, you may try using HTTP. However, unless you are behind a firewall where the Git protocol may not work, then it is advised that you use the Git protocol versus HTTP. Additionally, there are no guarantees that HTTP will work. To clone the Git tree using the HTTP protocol you would execute something like the following.

git-clone http://source.mvista.com/git/linux-davinci-2.6.git /home/jblogs/linux-davinci-2.6.git

Updating Your Git Tree Clone

The Git trees are frequently updated with new features and patches to fix issues. Once you have made an initial clone of the Git tree, periodically you may wish to sync your local clone with the Git tree you created your clone from. To do this, go to the directory where your Git tree resides and execute the "git pull" command.

cd /home/jblogs/linux-davinci-2.6.git
git pull


Viewing the Git History

To view the Git history, simply execute the following command.

git log

The "git log" command displays a summary of the updates or commits made to the Git repository in the order of newest to oldest. The below shows a snippet of the output from "git log" for the DaVinci Git tree.

commit 832e2db0642e148d8a30ec998c1e38910a96acc4
Author: Kevin Hilman <khilman@deeprootsystems.com>
Date:   Mon Sep 15 04:09:14 2008 -0700

    ARM: DaVinci: Update IO address pointer typechecking

    Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>

commit 612b7a31e1054e09896dfe8119b498f647607bfa
Author: Kevin Hilman <khilman@deeprootsystems.com>
Date:   Mon Sep 15 11:22:05 2008 -0700

    ARM: DaVinci: update default defconfig for EEPROM, ASoC

    Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>

Every commit made to the Git tree has a unique 40 digit hexadecimal number that is used to identify the commit. The 40 digit hexadecimal number is usually referred to as the “object name.” From the output of “git log” above, the 40 digit object names for the commits can be seen. These object names are calculated using a secure hash algorithm, namely the SHA1 hash. There are many advantages to using the SHA1 hash, because not only does it generate a unique number to track changes, but it also provides a mechanism to detect corruption in the repository.

The "git log" command only provides a summary of the changes that were made. To view the details of a specific commit the following command can be used.

git show <object name>

For example, to view the details of that commit titled "ARM: DaVinci: Update IO address pointer typechecking" from the above output of the "git log", the following command would be executed.

git show 832e2db0642e148d8a30ec998c1e38910a96acc4

Tags

In the previous section, it was shown that Git uses 40 digit object names, calculated using the SHA1 hash to identify the various commits made to the Git tree. These 40 digit numbers are not something that are easy to remember. Hence, whenever making commits it can be useful to create a “tag” that will be associated with this particular commit. It is up to the user which commits you tag, however, by doing so, it can make it much easier perform merges, comparisons, etc.

When a Git tree is cloned, tags are also inherited from the parent. To view all tags present in the Git tree execute the following command.

git tag -l

Tags are created using the following command.

git tag <tag-name>

Tags can be deleted using the following command.

git tag -d <tag-name>

Often times you may want to compare two versions of a file based on tags. For example, suppose you created two tags for "hello.c", one called "alpha" and another called "beta". To diff between the two tags use the following command.

git diff alpha beta -- hello.c

The -- separates the tag names from the file to be diff'd.

Creating a Development Branch

Git repositories have the concept of “heads” and “branches”. A branch reflects one path of development from a particular state of the repository. A given repository can have unlimited numbers of branches and branches can be created from other branches. Every branch in a repository has an associated branch head. The head is a pointer to the latest commit for the branch.

To view all current branches in a repository simply execute the following command.

git branch 

After cloinng the DaVinci Git tree, executing "git branch" would show something like the following. Please note that the “*” symbol indicates the current branch.

  for-rmk
* master
  origin

The above shows that the Git repository has three branches. The master branch represents the main branch of the repository. Every Git repository has a master branch. Whenever you create or clone at Git repository a master branch is created automatically. When you clone a repository a branch called "origin" is also created and this represents the Git repository that was cloned. The other branch called "for-rmk" is a custom branch that is present in the Git repository that was cloned. Whenever a repository is cloned, as the name of the command implies, all custom branches are also inherited.

Developers can create custom branches using the “git branch” command for doing experimental work. For example, a new branch called “testing”, to be used for development purposes, can be created by executing the following command. This command will create a new branch with the same state as the current branch.

git branch testing

You may also create a branch based upon any previous state of the Git tree too. For example, executing "git tag -l" to list all the tags for the DaVinci Git tree would show something like the following.

asoc-merge-0002
pre-2.6.24-merge
soc-merge0001
v2.6.11
v2.6.11-tree
v2.6.12
v2.6.12-rc2
v2.6.12-rc3
v2.6.12-rc4
v2.6.12-rc5
v2.6.12-rc6
v2.6.13
...

You may use any of the above tags to create a new branch from that point in time. For example, to create a new branch called "testing" based on release "v2.6.13" the following command would be executed.

git checkout -b testing v2.6.13

Creating a new branch as shown above does not automatically switch the repository in to the context of the new branch. Switching between branches is performed by using the “git checkout” command. For example, the following command would be used to switch to the testing branch.

git checkout testing

Similarly to switch from "testing" back to the master branch the following command would be executed.

git checkout master

Branches can always be removed. Therefore, if you created a branch called "testing" and sometime later wished to delete it, you could do so using the below command.

git branch -D testing


Adding, Editing, Renaming and Removing Files

Once a Git repository has been created or cloned, it may be necessary to add, edit, rename or delete source files in the repository. The following commands allow you to do this.

git add <filename>                     // Add a new file to the repository
git mv <filename> <new-filename>       // Rename a file 
git rm <filename>                      // Remove a file

Please note that after executing any of the above commands, you also need to execute the “git commit” command to commit this modification to the repository. For example, suppose you wish to add a new file called "myfile.c" to the repository. To do so the following commands would be executed.

git add myfile.c 
git commit

Executing "git commit", commits the changes to the Git repository and starts a text editor where you may to enter an commit message to summarise the changes made. The commit messages entered can be viewed by running the "git log" command.

# Please enter the commit message for your changes.
# (Comment lines starting with '#' will not be included)
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: myfile.c
#

Using Git Behind a Firewall

If you are trying to clone git trees from behind a firewall, you may find that it doesn't work and this is typically due to the fact that the firewall is blocking the port that is used by the Git protocol. You may use HTTP as opposed to Git but there are no guarantees that HTTP will work. You can workaround firewalls and still use the Git protocol by using a tool called corkscrew.

To install corkscrew on a Ubuntu/Debian host using the “apt” package manager, simply execute the following command.

sudo apt-get install corkscrew

Alternatively, you may download the corkscrew source from here and install as follows.

tar zxf corkscrew-2.0.tar.gz
cd corkscrew-2.0
./configure
make
cp corkscrew /usr/bin/

Create a new file called "git-proxy.sh" with the following contents and replace "<proxy-name>" and "<proxy-port>" with the actual proxy name and proxy port information.

#!/bin/sh
exec /usr/bin/corkscrew <proxy-name> <proxy-port> $*

For example...

#!/bin/sh
exec /usr/bin/corkscrew myproxy.com 80 $*

Make sure the file "git-proxy.sh" is executable. To make the file executable execute the following command.

chmod a+x git-proxy.sh

To configure git to always use the proxy whenever you use the git protocol execute the following command.

git config --global core.gitproxy '<path to proxy>/git-proxy.sh'

To configure git to only use the proxy for specific URLs, execute something similar to the following. The below command configures git to use the proxy for all URLs with a kernel.org suffix.

git config --global core.gitproxy '<path to proxy>/git-proxy.sh for kernel.org' 'for kernel.org$'

Setting up a Git Server

To setup a simple Git server on a Linux host to enable other users on the same network to clone your Git repositories, first you need to install the "git-daemon" package. To install the git-daemon package on a Ubuntu/Debian host using the “apt” package manager, as root user simply execute the following command.

apt-get install git-daemon-run

After installing the "git-daemon" package, it is necessary to configure the server. You may configure the server by editing the file "/etc/sv/git-daemon/run." To configure the server to export all the git trees in the directory "/pub/gittrees" on the host the file "/etc/sv/git-daemon/run" would be edited as follows.

#!/bin/sh
exec 2>&1
echo 'git-daemon starting.'
exec git-daemon --verbose --export-all --base-path=/pub/gittrees /pub/gittrees

The path at the end of the "exec" command above indicates where the git trees are located that you wish the servre to export. The option "--export-all" simply tells the server to export all the git trees found in this location. You may configure the server to only export specific git trees. To do so, please see the "git-daemon" manual page for more details. The option "--base-path" remaps all the path requests as relative to the given path. For example, if you run git-daemon with --base-path=/pub/gittrees on example.com, then if you later try to pull git://example.com/hello.git, git-daemon will interpret the path as /pub/gittrees/hello.git.

Git rebase tricks

by Kevin Hilman -- ver 0.2

Overview/Intro

Here's a short overview of a couple of git rebase tricks that I use all the time.

Recommended background reading: Git User's manual

This has been written with some quilt analogies so folks who are already familiar with using quilt for this will see the equivalents easily. If you're not familiar with quilt, just ignore the 'Quilt analogy' parts.

Quilt analogy: Quilt users are probably used to doing things like:

$ quilt pop -a
<change kernel base>
$ quilt push -a
<manually fix conflicts>

In git, this is called a rebase.

Interactive rebase: git rebase --interactive (git rebase -i)

Quilt analogy: An interactive rebase is like editing the series file between a 'quilt pop -a' and 'quilt push -a'.

Assume you have a dev branch 'dev-branch' based at v2.6.30:

v2.6.30------v2.6.31-rc1---------v2.6.31-rc2------------linus/master
   |
   |
   |
   \
    ---A--B--C--D--E--F--G--dev-branch

and you want to rebase it on a different upstream head, such as v2.6.31-rc2. However, you'd like to reorganize your commits while doing the rebase. Using interactive rebase, you can drop commits, reorder commits, squash commits (combine multiple commits into a single commit) or edit them interactively before they are rebased.

NOTE: I use A, B, C... above as shorthand for git commit IDs, or git tags.

Assume you want to rebase your dev-branch onto v2.6.31-rc2. To start the interactive rebase, do:

$ git rebase -i v2.6.31-rc2 dev-branch

This will bring up an edit window with a list of all the commits to be rebased, and will look something like this:

pick 0c2a40e changelog for commit A
pick debd519 changelog for commit B
pick 39780da changelog for commit C
pick e319a58 changelog for commit D
pick c3969e3 changelog for commit E
pick d6c3bb9 changelog for commit F
pick ef37d5b changelog for commit G
pick ed83a57 changelog for commit H

It is in editing this file that all the magic happens...

Example 1: dropping or re-ordering patches

Quilt analogy: removing or re-ordering patches in the series files

In the edit window, just like a quilt series file, just remove the lines of the commits, or re-order them as you wish.

When done, save the file and rebase will continue.

Resulting tree, assuming C and D dropped, and G moved to the beginning:

v2.6.30------v2.6.31-rc1---------v2.6.31-rc2------------linus/master
                                   |
                                   |
                                   |
                                   \
                                    ---G--A--B--E--F--dev-branch

Example 2: squashing (combining commits together)

Quilt analogy: quilt fold

Let's assume that commits D and E are only very minor changes or fixes to commit C and for the rebase, they can just be combined (squashed) into commit C. This kind of thing is often useful for upstream submissions where the upsteram maintainer may not be interested in all the minor edits and fixes along the way, but only in the final result.

To squash D and E into C, change the 'pick' for D and E into 'squash' (or 's'), save the file and rebase will continue.

Git will also combine all the changelogs for all squashed commits into a single changelog and give you a chance to edit/combine the changelog entries as well.

Resulting tree:

v2.6.30------v2.6.31-rc1---------v2.6.31-rc2------------linus/master
                                   |
                                   |
                                   |
                                   \
                                    ---A--B--C'--F--dev-branch

(where C' is the squashed version of C, D and E)

Example 3: editing commits

Assume that while rebasing, you might want to change a commit slightly. This might be simply to modify the changelog, or to change the patch.

In the edit window, change the 'pick' to 'edit' (or 'e'), save the file and rebase will continue until it applies that commit.

When it reaches the 'edit' commit it will stop, and give you the chance to make any changes. Make any changes, add them to the index using 'git add <files-that-were-changed>' and commit them using 'git commit --amend' (NOTE: see rewriting history section of Git User's manual for more details on --amend.)

When changes are committed, continue rebase with 'git rebase --continue'.

Also note that additional commits could also be inserted here before continuing either by cherry-picking from other places or by the normal edit/commit cycle.

Partial-tree rebase: git rebase --onto

Example 1

Assume you have a dev branch 'dev-branch' based at v2.6.30:

v2.6.30------v2.6.31-rc1---------v2.6.31-rc2------------linus/master
   |
   |
   |
   \
    ---A--B--C--D--dev-branch

and you want to rebase it on a different upstream head, such as v2.6.31-rc2. However, many of the changes in your dev-branch are already upstream, but may have gone upstream in a different form or were changed by upstream maintainers for various reasons.

In this case, you only want to rebase the parts of your dev-branch that are not upstream.

In this example, assume that commits A..B are already upstream, and you know the ones in your branch will conflict with the upstream versions, so you really only want to rebase commits C..dev-branch.

First, optionally create a branch that keeps track of the old branch in case you need to undo/redo

$ git branch old-dev-branch dev-branch

then, using --onto, to rebase commits C..dev-branch onto v2.6.31-rc2, do:

$ git checkout -f v2.6.31-rc2
$ git rebase --onto HEAD B dev-branch

Alternatively, this can be done in a single step:

$ git rebase --onto v2.6.31-rc2 B dev-branch

Either way, you will end up on the new 'dev-branch' and the result will be:

v2.6.30------v2.6.31-rc1---------v2.6.31-rc2------------linus/master
   |                                |
   |                                |
   |                                |
   \                                \
    ---A--B--C--D--old-dev-branch    ---C'--D'--dev-branch

Note in this case, C' and D' are the same as C and D except they now have different commit IDs.

References

  1. Git Homepage
  2. Git Manual Page
  3. Git Documentation Wiki
  4. Git User's Manual
  5. Fixing a mistake by rewriting history