Sunday, March 11, 2012

How to use git to avoid writing ChangeLog by hand?


The standard GNU defines what must be a ChangeLog file (see:
http://www.gnu.org/prep/standards/html_node/Change-Logs.html). The
main goal of this is to be able to track bugs, and to understand the
history of a project.


1 Why keeping a ChangeLog?


In the past, we must keep a ChangeLog file for each project, since
there was no tool able to give all the history in every condition.
I am too young to know the work flow with CVS and other tool. I
learn the control version with SVN. But to have access to all the
history of a project, we'll need to be connected. And it is long.

Now we have git (or Mercurial, but I don't know this one), which are
distributed system, and they allow to keep all the history of a project
in local. So, why should we keep a ChangeLog file?

Pros

  • When the project is released, the `git log` is not accessible.
  • There is copyright issue in free software.
  • It is easy to write a good ChangeLog with Emacs (and I'm sure it
    is easy with vim too).

Cons

  • There is several tools to generate a ChangeLog file with the output
    of git log. I think about the tool 'gitlog-to-changelog' from the
    gnulib project (see: http://www.gnu.org/software/gnulib/).
  • It is common, when playing with branches and rebasing a lot, to have
    conflict only in the ChangeLog file.

By generating the ChangeLog when making a release (or an archive), we
solve the problem of the history and the copyright. We can use a
ChangeLog file, not in the repository (maybe it is a good idea to put
the "ChangeLog" in the '.gitignore'), to write the log message, and
then we can use a little script to take the first entry and give it to
git.

For example, a simple function like this can do the trick:


commit()
{
   [[ ! -f ChangeLog ]] && {
   echo 'no ChangeLog in current directory' >&2
   return 1
   }

   git commit -m "`sed'1d;/^....-..-../Q;s/^\t//;' ChangeLog`" "$@"
}

This script is highly enhanceable. This is just an idea of what could
be the script I am talking about.



2 Using Emacs to write the ChangeLog


Now let's talk about the work flow, and the use of Emacs for writing
the log. Let's suppose we have a ChangeLog file at the root of the
project. The main idea is each time you modify something in a file,
you hit "C-x 4 a", and its open the ChangeLog, and add an entry
(which follows the GNU Coding Standard), you just have to write the
meaning of your change.

Before committing, think about add a one-line summary !

There is a little problem with Emacs at this level. In the past, the
common work flow was one commit by day. And the One True Editor
follows this standard. So there is a way to bypass this, an option
allows the function behind "C-x 4 a" to create a new entry. But it
does it each time, and this is not what we want. So I create a little
wrapper around this. Here is the function:


(defun new-changelog-entry()
  (interactive)
  (setq add-log-always-start-new-record t)
  (add-change-log-entry-other-window)
  (setq add-log-always-start-new-record nil))

; "C-x 5 a" runs new-changelog-entry.
(global-set-key "5a" (quote new-changelog-entry))

The idea is to set the option before calling, and unset it after. So,
only when we want to create a new entry, a new entry is created. :D


3 Be sure the log follows a good format


In the aim to be able to translate the output of `git log` into a
GNU standard compliant ChangeLog, the commit message must follow
a strict format. So, how to achieve this goal?

Git provides several kind of hooks. A hook is a script called when a
specific operation occurs. There is a lot of source on the web to know
what is a hook. Here I'll talk about the use of a script (developed by
me and one of my teacher) for solving the format of the git log.

It is a script which can be run server-side or client-side. You can
find this script here:
https://github.com/Enki-Prog/tools/blob/master/git/update. Here I will
talk about the problem of getting all the commit between two push. And
the way to know easily the file modified when committing.

The strategy we applied, is to authorize any kind of log in a personal
branch (`pseudo/feature'), and to reject a push when it is not (either
a `candidates/feature' or a branch with no `/') and don't follow the
format.

There are several thing checked, and it is shared by the two way to
call this script. The explanation above are talking about the way
to get the commits, and the information to be able to check.

The way we check after, is less interesting I think, because if you
read this article, maybe it is because you want to develop yourself
this kind of tools. And you just need the way to don't have to look
a lot on the web how to make this, all the information you need are
here or at worst on the script.



3.1 Server-side

We receive three arguments: the ref name, the old revision, and the
new revision.

  1. If the new revision is a null sha1, it means it is a branch deletion.
    So, nothing to do here.
  2. If the old revision is a null sha1, it is a branch creation.

    In this case, to get the new revision, the command to get the
    commits, we need to call:


    git rev-parse --not $otherbranches | git rev-list --stdin newrev
    

  3. In the other case, we need to replace newrev by "oldrev..newrev".

To get the `$otherbranches' the command is:


git for-each-ref --format='%(refname)' refs/heads |
  grep -F -x -v $refname |
  grep -x 'refs/heads/\(candidates/.*\|[^/]*\)'

The first line gets the list of branches. The second filters out the
current branch. And the last one, keeps only the one which are non
personal branch.



3.2 Client-side

In this case, we have only one argument: the path to the temporary
file which contains the log which will be tested. In this case it is
easier, because there is only one commit. The question is, how to get
the list of modified file? There is several way, but the one I found
the simpler, is to make:


git status --porcelain

The output is simple: Two characters, and the filename (eventually two,
in the case of a `git mv`). If the first character is not a ? or a space,
the file is in the index and ready to be committed.



4 Conclusion


We have talk about the question "should I keep a ChangeLog in my
project?". And I developed on how to make this change in a good way.
Thanks to git, Emacs, a tool to check if the log is correct and
`gitlog-to-changelog'. Obviously, this is the way I choose for me,
and each part I present can be switched.

Feel free to leave a comment with your opinion and/or your suggestion :)

2 comments:

  1. hey good work man!!!


    Nike Mission: To bring inspiration and innovation to every athlete in the word, if you have a body you are an athlete. Das Leben ist ein Sport: Nike hat sich als Ziel gesetzt, mit innovativen produkten Athleten zu besserer Leistung zu verhelfen und jeden zu mehr Sport zu motivieren. Nike ist aber auch die Marke für einen sportlichen Lebensstil neben dem Sportplatz. Mit sportlichen Designs brillieren auch die Freitzeitmodelle in jeder Hinsicht. Coole Farbkombinationen und ein dynamisches Design motivieren im Sport die volle Leistung zu



    Nike Schuhe.


    Thanks a lot for sharing such a wonderful post, it is a very nice site i really enjoyed to visit this site,

    ReplyDelete
  2. hey good work man!!!
    please you like my web sites ?

    Nike Mission: To bring inspiration and innovation to every athlete in the word, if you have a

    body you are an athlete. Das Leben ist ein Sport: Nike hat sich als Ziel gesetzt, mit

    innovativen produkten Athleten zu besserer Leistung zu verhelfen und jeden zu mehr Sport zu

    motivieren. Nike ist aber auch die Marke für einen sportlichen Lebensstil neben dem Sportplatz.

    Nike Schuhe.
    Mit sportlichen Designs brillieren auch die Freitzeitmodelle in jeder Hinsicht. Coole

    Farbkombinationen und ein dynamisches Design motivieren im Sport die volle Leistung zu

    erbringen.


    Thanks a lot for sharing such a wonderful post, it is a very nice site i really enjoyed to visit

    this site,

    ReplyDelete