Bering-uClibc 5.x - Developer Guide - Hints and Tips for using Git SCM

From bering-uClibc
Revision as of 23:45, 9 September 2016 by Kapeka (Talk | contribs) (Clone the SourceForge-hosted Git Repository change path to repo)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Hints and Tips for using Git SCM
Prev Bering-uClibc 5.x - Developer Guide Next


Background

The LEAF project successfully used the CVS (Concurrent Version System) Source Code Management (SCM) tool, hosted by SourceForge, for many years. Various developers suggested switching to an alternative SCM such as Subversion or Git (see in particular the email thread "Talks about versioning system" on the leaf-devel mailing list in late September 2010) but the consensus was that CVS met all the basic requirements and a change of SCM would only divert resources away from other more important tasks.

The consensus view changed in late January / early February 2011, when the SourceForge systems were compromised and CVS proved particularly difficult to restore (see the SourceForge report here). In particular, the indication was that the SourceForge-hosted CVS service would be retired.

The SourceForge-hosted Git SCM service was selected as the new SCM for Bering-uClibc 4.x early in February 2011.


Overview of Git

Git is a distributed Source Code Management tool whereas CVS is a client-server SCM tool. This means that every Git repository is a "peer" (equivalent) of every other repository. One repository, in our case the one hosted by SourceForge, is arbitrarily designated as the "main" or "central" repository, but every developer has their own local, working repository with the same capabilities as the central one.

A developer initiates their own local repository by "cloning" the master one, hosted by SourceForge.

The developer then interacts with their local repository rather than interacting directly with the central repository, and periodically synchronizes the local repository with the central one. Changes made locally are "pushed" to the central repository, whereas changes made by other developers (and "pushed" to the central repository by those other developers) are "pulled" from the central repository so as to update the local repository.

It is tempting to refer to the central repository as the "master" repository, but that would be confusing as will become clear later. The term "central" or "SourceForge-hosted" repository will be used instead.


Git Repositories for LEAF

The LEAF Project currently has three separate Git repositories:

leaf/bering-uclibc 
This is the main, active repository for the Bering-uClibc Project. Since 2011-06-20 this holds the live source code for Bering-uClibc 4.x and is the only repository that most developers need to use.
leaf/leaf 
This was initially (until 2011-06-20) used for Bering-uClibc 4.x development but that has now moved to leaf/bering-uclibc. The leaf/leaf repository is now reserved for general purpose LEAF items not specific to any Project,
leaf/packages 
This separate repository is used to hold the Package .lrp files generated for LEAF Project Releases, to keep them out of the other repositories which are reserved for source code.

In the future it is likely that further repositories will be added. The general rule is one Git Repository for each LEAF Project.


Getting Started

Install the Git Client

The next step is to ensure you have the Git client software installed on your build host. On Red Hat-derived Linux distributions this is accomplished with:

yum install git

Clone the SourceForge-hosted Git Repository

Next, you will want to copy ("clone") the SourceForge-hosted Git repository. The naming convention for project repository URLs is documented on the SourceForge Git page. In our case "PROJECTNAME" = "leaf" and "REPONAME" = "bering-uclibc" so the repository is accessed as:

Read-only 
git://leaf.git.sourceforge.net/p/leaf/bering-uclibc
Read-write 
ssh://USERNAME@leaf.git.sourceforge.net/p/leaf/bering-uclibc

The examples that follow assume you want read-write access and will therefore use the second (ssh) variant. Obviously you need to replace "USERNAME" with your own SourceForge username. You will be prompted to enter your SourceForge account password when using the "ssh" URL, or you can configure SSH keys as described here.

To actually create a copy of the SourceForge-hosted repository change to a suitable directory on your build host and run the following command:

git clone ssh://USERNAME@leaf.git.sourceforge.net/p/leaf/bering-uclibc

Note: The "git" commands must be entered as e.g. "git clone" (argument "clone" to command "git") but you will also see references to e.g. "git-clone" in the documentation. This is because the man pages use the latter naming style (with the hyphen) and in the very early days of Git there were several independent command executables called e.g. "git-clone" rather than a single command executable called "git".

This creates a new directory "bering-uclibc" under the current directory which contains a full copy of the "bering-uclibc" repository on SourceForge. Change to this new "bering-uclibc" directory for the following steps:

cd bering-uclibc

This directory "bering-uclibc" represents the local repository which is a full Git repository in its own right, although it understands that it was derived from (and is therefore "linked" back to) the SourceForge repository. (Actually it is the .git directory under "bering-uclibc" which is the real Git repository.)

Note: The SourceForge repository is known by the alias "origin". That alias will be used in some of the commands.

Any git commands executed in or below the "bering-uclibc" directory operate on this repository.

Configure your User Identity

Any changes you make are identified with your user details as specified in a Git configuration file. To populate these on a "per repository" basis run the following commands:

git config user.name "Your Full Name"
git config user.email "USERNAME@users.sourceforge.net"

Tip: Review the contents of file .git/config to see where these setting are maintained and what else is in this file.

An alternative is to configure these details on a "per user" basis, which means they are instead written to file $HOME/.gitconfig and apply to all repositories (unless overridden by a "per repository" setting). To do this, add the --global argument as shown below:

git config --global user.name "Your Full Name"
git config --global user.email "USERNAME@users.sourceforge.net"

Change an Existing File

That's all of the preparation complete; now we can actually change something. The following examples show an update to buildtool/conf/sources.cfg.

Edit the file

First change to the directory containing the file to be modified:

cd buildtool/conf

Modify the file using your favourite text editor:

edit sources.cfg

Tell Git about the changes

When the modifications are complete you need to tell Git about them. This is accomplished using the "git add" command:

git add sources.cfg

Note: With CVS you would only "add" new files or directories. With Git you need to also need to "add" changes to existing files.

Running "git add" has the effect of adding the differences in the specified file(s) to a local "index" of pending changes which is then processed by a subsequent "git commit" transaction.

Commit the changes to the local Git repository

At this point you can review the contents of the "index" of pending changes with the following command:

git diff --cached

It is good practice to always do this before running "git commit".

Note: The "--cached" argument is required once changes have been added to the index. Before running "git add" a simple "git diff filename" will show the deltas.

Another command to show what changes are pending is:

git status

This also shows what files have been modified but not yet added to the list of changes to be processed.

Next, you need to commit the changes to your local Git repository:

git commit -m "Log message for changes"

Note: With CVS this would commit the changes directly to the shared repository on SourceForge however with Git the "commit" command only commits the changes to the local Git repository.

Publish the changes to the SourceForge-hosted Git repository

Publishing the local changes to the shared Git repository hosted on SourceForge needs a further step:

git push

Tip: All of the "git" commands take a "--dry-run" command-line parameter. This processes the command as normal but does not actually update the repository. Use this while you are learning to check what effect a command will have before running it for real.

Add a New Directory

When adding a new Package it is necessary to add a new directory (under repo/) together with (some of) the files in that directory. This is easier with Git than with CVS, since adding the directory automatically adds the files as well.

git add packagedir/

Note that any number of related changes can be stacked up with multiple "git add" commands before running "git commit". This means that adding a new Package entry to sources.cfg can be done as part of the same commit as adding the source files themselves.

Update the Local Repository with Changes from SourceForge

Other developers will be making their own changes and publishing updates to the SourceForge-hosted Git repository. Update the local repository to re-align it with the master repository using the following command:

git pull

If there are no changes this responds as follows:

Already up-to-date.


More Advanced Topics

Ignoring Generated Files

Any files generated within the Git repository will show up in a "git status" and are at risk of being accidentally added to the repository. To specify that such files should be ignored, Git checks for files called .gitignore in each directory within the repository.

The contents of a .gitignore file take effect on files in the same directory and in all sub-directories, and the contents are basically just filename glob patterns which specify the files to be ignored.

For example, there is a buildtool/image/.gitignore file which specifies that any generated disk Image files (in the various sub-directories) should be excluded. The contents are:

# Exclude any generated image files
*.tar.gz
*.iso

Moving and Removing Files

When Removing files, or Moving them around within the repository, simply use the "git rm" and "git mv" rather than "rm" and "mv". These "git" commands do what you would expect at the file system level and also keep the Git "index" updated. The changes then need to be committed and pushed.

Tagging Releases

It is customary to "tag" each Bering-uClibc 4.x release (including Betas and Release Candidates) in the SCM so that it is clear which version of the source code was used to build the executables distributed in the released disk images. This is done by running commands like:

git tag -a 4.0-rc1
git push origin 4.0-rc1

The first command creates an unsigned "annotated" tag label for the most recent commit and the second pushes it to the SourceForge-hosted Git repository.

The tags defined for a Git repository can be listed with "git tag -l". For example:

git tag -l
5.0-beta1
5.0-rc1

It is then a simple matter to adjust the working copy of the files to reflect the state of the repository when the tag was applied. Simply use the tag name as an argument to "git checkout". For example:

git checkout 5.0-rc1

If you need to refer to an earlier version of the repository which was not specifically tagged this is a little more complex. The main challenge is that without a tag there is no friendly string which refers to the relevant previous commit. There is a shorthand way to refer to e.g. "8 commits ago" on the branch called "master":

git checkout master~8

In addition, each commit is labelled with a less friendly unique identifier which can be used instead.

Branching

Default Branch

By default, Git works with a single local branch called "master", as can be seen by running the "git branch" command with no arguments:

git branch
* master

The asterisk (*) indicates that this is the "current" branch.

This local branch "master" is synchronized with the "master" branch in the "origin" repository (known as "origin/master").

Creating a Branch

A new local branch can be created very easily. For example:

git branch new-branch

creates a new local branch called "new-branch" which is based on the latest commit ("HEAD") on branch "master".

If you want the branch to refer to an older snapshot of the repository, for example the release tagged as "4.0", specify this as the "<start-point>", for example:

git branch new-branch 4.0

Working with a new Branch

When a new branch is created it is not automatically made the default branch. If you run

git branch

you’ll get a list of all existing branches:

  new-branch
* master

In order to use the new branch you need to switch to it using "git checkout". For example:

git checkout new-branch

If you now run

git branch

you’ll see that new-branch is the active branch:

* new-branch
  master

So you can check the active branch with

git branch

and switch the current active branch with

git checkout branch

That way one can easily work on different branches on the local host.

Note: Alternatively, a new branch based on a snapshot of the tagged version 5.0, can be created using the "-b" argument to "git checkout". For example:

git checkout -b new-branch 5.0

in which case new-branch is based on version 5.0 and automatically made the default.

Comparing Branches

Can report on the differences between the current branch and another branch using "git diff branchname". For example, assuming that a new branch has been made the current/default branch this can be compared to the master branch:

git diff master

Merging a Branch

To merge the changes made in new-branch into master, run

git merge new-branch

This also works the other way around. Say you are working on a new development in new-branch but there have been changes in master which you want to incorporate:

git merge master

Deleting a Branch

A local branch can be deleted using e.g.

git branch -d new-branch

Remote Branches

In general, all Bering-uClibc 5.x development takes place along a single trunk, with identifying labels being used to signify milestones such as "5.0-beta1" and "5.0". However, in some cases it is necessary to work on multiple developments in parallel.

Imagine that "4.1-beta1" has already been released but a serious bug is found in "5.0". The bug can be fixed in "5.1-beta2" but that might take many weeks to be formally released as "5.1" and will include a large number of other changes. One solution is to fix the bug in a release called "5.0.1" which is based on "5.0" and omits the other changes in "5.1-beta1".

The best way to handle this is to create a new branch (called e.g. "5.0-fixes") which is based on the "5.0" label. In order for this branch to be shared by all of the developers it needs to be present on the main, SourceForge-hosted Git repository. Such a branch is referred to as a remote branch. In general, each developer will create their own local branch which is set up to "track" this remote branch (a tracking branch).

Creating Remote Branches

If you want to share your local branch with other developers, you push it to the remote server.

git push -u origin 5.0-fixes:5.0-fixes

The option '-u' enables tracking of the remote branch for the branch creator.

Listing Remote Branches

Add the "-r" argument to the "git branch" command to have it show remote branches. For example:

git branch -r
 origin/HEAD -> origin/master
 origin/master
 origin/4.0-fixes

Tracking a Remote Branch

If the remote branch already exists, a developer can set up their own local branch to track this with a command like the following:

git branch --track 5.0-fixes origin/5.0-fixes

The "tracking" means that git pull and git push will automatically read from / write to the correct branch in the remote repository.

Confirming Remote Branch Tracking

To be certain which local branches are mapped to which remote branches, run git remote show origin. For example:

git remote show origin
* remote origin
  Fetch URL: ssh://USERNAME@leaf.git.sourceforge.net/gitroot/leaf/bering-uclibc
  Push  URL: ssh://USERNAME@leaf.git.sourceforge.net/gitroot/leaf/bering-uclibc
  HEAD branch: master
  Remote branches:
    5.0-fixes tracked
    master    tracked
  Local branches configured for 'git pull':
    5.0-fixes merges with remote 5.0-fixes
    master    merges with remote master
  Local refs configured for 'git push':
    5.0-fixes pushes to 5.0-fixes (up to date)
    master    pushes to master    (up to date)

Deleting a Remote Branch

Just in case you want to delete the remote branch 5.0-fixes, you push "nothing" to the remote branch 5.0-fixes.

git push origin :5.0-fixes


Graphical User Interfaces

There are some GUI tools which can help manage Git repositories.

git gui - A portable graphical interface to Git

git gui is written in Tcl/Tk. The default display is a GUI equivalent of "git status", showing Staged and Unstaged changes. It has buttons for entering Commit messages, for Pushing changes etc.

TODO - Add screenshot.

gitk - The git repository browser

gitk is written in Tcl/Tk and provides a view of the current Git repository, with an emphasis on showing the history of commits. A particular feature is the "commit graph structure" (shown here for the 4.1 release - the yellow "flag" is a version tag"). Click on the image for a larger view:

Cropped screenshot of gitk showing leaf/bering-uclibc Git repository for Bering-uClibc 4.1



Git Command Reference for CVS Users

For developers familiar with CVS, here are the equivalent Git commands for some basic SCM tasks:

Task CVS Command Git Command
Check-in a modified file cvs commit -m "Log Message" filename git add filename; git commit -m "Log Message"; git push
Update local repository with changes from SourceForge cvs update git pull


Hints and Tips

To use all the tricks below it's better to have a recent version of git: >= 1.7.9.

Compiling your own version of git

  • You can compile your own version of git (it's easy). On my fedora 15 i've done:
sudo yum install gcc libcurl-devel openssl-devel expat-devel perl-ExtUtils-MakeMaker tcl gettext asciidoc xmlto
git clone git://github.com/git/git.git
cd git
git checkout v1.7.12
make prefix=/usr all man # You can change the prefix if you don't want to install on /usr
# Then remove the old git package
sudo yum remove git
# Then install git into you system as root:
sudo make prefix=/usr install install-man # Change the prefix if you have change it in the first make command
# And install the git completion script(s)
sudo cp contrib/completion/* /etc/bash_completion.d/

Useful configurations

  • Add the refnames and date in local time when using the git log command:
git config --global log.decorate  short
git config --global log.date  local
  • Update you PS1 with git informations:
  1. Add $(__git_ps1) at the end of your PS1 (usually configure in your $HOME/.bashrc). For example:
    PS1="[\u@\h \W]$(__git_ps1) \\$ "
  2. Add some variables in your $HOME/.bashrc:
    export GIT_PS1_SHOWDIRTYSTATE=true
    export GIT_PS1_SHOWSTASHSTATE=true
    export GIT_PS1_SHOWUNTRACKEDFILES=true
    export GIT_PS1_SHOWUPSTREAM="verbose"
  3. Check that the bash-completion package is installed:
    sudo yum install bash-completion
  4. Reload your $HOME/.bashrc:
    source $HOME/.bashrc

Add Colors

Git can color its output to your terminal, which can help you visually parse the output quickly and easily. This is the configuration i used:

git config --global color.ui  auto
git config --global color.branch.current  green
git config --global color.branch.local  yellow
git config --global color.branch.remote  red
git config --global color.status.added  yellow
git config --global color.status.changed  green
git config --global color.status.untracked  cyan

Having multiple worktrees and only one shared local git repository

With this trick you can create multiple worktrees (like BuC-4.x and BuC-5.x) but with only one shared local git repository.

  • First of all you need to be sure that there was no files waiting to be commiting. If so, commit all you modifications or stash (git-stash) them. Then move your .git directory to a dedicated directory (this will be your local git repository). For example if you working tree is bering-uClibc:
mv bering-uClibc/.git  bering-uClibc.git
  • Now get the last version of the git-new-workdir from the git project:
wget --quiet https://raw.github.com/git/git/master/contrib/workdir/git-new-workdir && chmod +rx git-new-workdir
  • Then create your new worktrees. Suppose you want to have BuC-4.x pointed to the maint branch and BuC-5.x pointed to the master branch, do:
./git-new-workdir bering-uClibc.git BuC-4.x maint
./git-new-workdir bering-uClibc.git BuC-5.x master

There we go.

We can remove the git-new-workdir script because it is no more useful now.

Further Reading



Prev Up Next