Patchwork [2,of,2,RFC] c-hglib: hg_log() level 1 function

login
register
mail settings
Submitter Iulian Stana
Date Aug. 18, 2013, 12:28 p.m.
Message ID <04bf311c99b8f4cf4c3e.1376828889@doppler>
Download mbox | patch
Permalink /patch/2225/
State Deferred
Headers show

Comments

Iulian Stana - Aug. 18, 2013, 12:28 p.m.
# HG changeset patch
# User Iulian Stana <julian.stana@gmail.com>
# Date 1376828454 -10800
#      Sun Aug 18 15:20:54 2013 +0300
# Node ID 04bf311c99b8f4cf4c3e2096b493ee126f861b99
# Parent  21be7b973803e02b0bb310465691f88705a2dd53
c-hglib: hg_log() level 1 function

The revision history could have a huge mass of data. To deal with this issue, I
had created a iterator-like mechanism. In this way I will get the changesets in
chunks or more-like one at the time.

The hg_log function will prepare the command and then will call cmd-server for
changesets. This function will return to the user a iterator structure, to be
used on hg_fetch_log_entry funciton. (The log command will not passing any
changeset to the user)

The hg_fetch_log_entry function will read changesets from command server and
will pass into the hg_log_entry_t structure in a parse way the changeset.
The hg_log_entry_t structure will be one of the function parameter.

In the main.c file it can be found an example on how this function can be used.
Giovanni Gherdovich - Aug. 19, 2013, 12:10 p.m.
2013/8/18 Iulian Stana <julian.stana@gmail.com>

> # HG changeset patch
> # User Iulian Stana <julian.stana@gmail.com>
> # Date 1376828454 -10800
> #      Sun Aug 18 15:20:54 2013 +0300
> # Node ID 04bf311c99b8f4cf4c3e2096b493ee126f861b99
> # Parent  21be7b973803e02b0bb310465691f88705a2dd53
> c-hglib: hg_log() level 1 function
>
> The revision history could have a huge mass of data. To deal with this
> issue, I
> had created a iterator-like mechanism. In this way I will get the
> changesets in
> chunks or more-like one at the time.
>
> The hg_log function will prepare the command and then will call cmd-server
> for
> changesets. This function will return to the user a iterator structure, to
> be
> used on hg_fetch_log_entry funciton. (The log command will not passing any
> changeset to the user)
>
> The hg_fetch_log_entry function will read changesets from command server
> and
> will pass into the hg_log_entry_t structure in a parse way the changeset.
> The hg_log_entry_t structure will be one of the function parameter.
>
> In the main.c file it can be found an example on how this function can be
> used.
>
> diff --git a/client.c b/client.c
> new file mode 100644
> --- /dev/null
> +++ b/client.c
> @@ -0,0 +1,134 @@
> +#define _GNU_SOURCE
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <sys/wait.h>
> +#include <signal.h>
> +
> +
> +#include "client.h"
> +#include "utils.h"
> +
> +#define HGPATH "hg"
> +#define CHANGESET "{rev}\\0{node}\\0{tags}\\0{branch}\\0{author}\\0{desc}\
> +                                               \\0{date|isodate}\\0"
> +
> +/**
> + * \brief A helper for building the command arguments.
> + * \param command The name for mercurial command.
> + * \param options An array pointer to command option list.
> + * \param template Template to be used for this command.
> + * \retval new_string An array of pointers, where the command it's almost
> ready
> + *                    to be send to mercurial cmdserver.
> + * */
> +char **cmdbuilder(char *command, char *options[], char *template)
> +{
> +       int cmd_size, size;
> +       char **new_cmd;
> +       char *temp = "--template";
> +
> +       for(size = 0; options[size] != NULL; size++);
> +       cmd_size = size + 1;
> +       new_cmd = malloc(sizeof(char *) * (cmd_size + 3));
> +
> +       new_cmd[0] = command;
> +       memcpy(new_cmd + 1, options, (cmd_size - 1)  * sizeof(char *));
> +       if(template){
> +               new_cmd[cmd_size] = temp;
> +               new_cmd[cmd_size + 1] = template;
> +               new_cmd[cmd_size + 2] = NULL;
> +       }else{
> +               new_cmd[cmd_size] = NULL;
> +       }
> +
> +       return new_cmd;
> +}
> +
> +/**
> + * \brief 'Parse a changeset'. It's more like pointing to the correct
> position.
> + *
> + * The changeset could be found on buff pointer. To not duplicate the
> data I
> + * choose to point every log_entry field to the right position.
> + * \param buff The pointer to the changeset.
> + * \param le   The log_entry_t structure where the changeset will be
> parse.
> + * \retval 0 if successful.
> + * */
> +int parse_changeset(char *buff, hg_log_entry_t *le)
> +{
> +       /* set pointer for revision position */
> +       le->rev = buff;
> +
> +       /* set pointer for node position */
> +       buff = buff + strlen(buff) + 1;
> +       le->node = buff;
> +
> +       /* set pointer for tag position */
> +       buff = buff + strlen(buff) + 1;
> +       le->tags = buff;
> +
> +       /* set pointer for branch position */
> +       buff = buff + strlen(buff) + 1;
> +       le->branch = buff;
> +
> +       /* set pointer for author position */
> +       buff = buff + strlen(buff) + 1;
> +       le->author = buff;
> +
> +       /* set pointer for description position */
> +       buff = buff + strlen(buff) + 1;
> +       le->desc = buff;
> +
> +       /* set pointer for data position */
> +       buff = buff + strlen(buff) + 1;
> +       le->date = buff;
> +
> +       return 0;
> +}
> +
> +/* The high level log command for hglib API. */
> +hg_log_iterator *hg_log(hg_handle *handle, char *option[])
> +{
> +       hg_log_iterator *log_iterator = malloc(sizeof(hg_log_iterator));
> +       log_iterator->handle = handle;
> +
> +       log_iterator->command = cmdbuilder("log", option, CHANGESET);
> +
> +       if(hg_rawcommand(handle, log_iterator->command) < 0){
> +               return NULL;
> +       }
> +
> +       log_iterator->changeset = malloc(0);
> +       return log_iterator;
>


What happens here is that hg_log() does not return a status code
(with the meaning "command successfully sent"), but the hg_log_iterator
struct instead.

A status code would be desirable and idiomatic I think;
the struct to be passed as an argument.



> +}
> +
> +/* The iterator next step. Getting the next changeset. */
> +int hg_fetch_log_entry(hg_log_iterator *log_iterator, hg_log_entry_t *le)
> +{
> +       hg_header head = hg_head(log_iterator->handle);
> +
> +       if(head.channel == 'r'){
> +               free(log_iterator->command);
> +               free(log_iterator->changeset);
> +               int exitcode = hg_exitcode(log_iterator->handle);
> +               free(log_iterator);
> +               return exitcode;
> +       }
> +
> +       log_iterator->changeset = realloc(log_iterator->changeset,
> head.length);
> +
> +       if(read(log_iterator->handle->p_read, log_iterator->changeset,
> +                                       head.length) < 0){
>


Here you are not using hg_rawread(). Please explain why.
Level 1 functions are meant to build on top of level 0 functions.

Also: you are not reading in 4096 B chunks.

Also: you are assuming that all information for a given log entry
(a revision) are retrieved in a "single shot" of the  'o' channel.

You are assuming that a log revision cannot be split across two
iteration of output on the 'o' channel.

Is this assumption safe?

Please ask Idan Kamara (CC the list) and make sure of this.



> +               return -1;
> +       }
> +
> +       parse_changeset(log_iterator->changeset, le);
> +
> +       if(read_header(log_iterator->handle) < 0){
> +               return -1;
> +       }
> +       return head.length;
> +}
> diff --git a/client.h b/client.h
> --- a/client.h
> +++ b/client.h
> @@ -65,6 +65,22 @@
>         int protect;
>  } hg_handle;
>
> +typedef struct hg_log_iterator{
>


I think this name is unfortunate. This is not an iterator, in any possible
sense.
This is the place where you store your data (the "changeset" field of this
structure).

I would call this something like hg_log_rawentry_t, in contrast to the
other structure where the log entry is "parsed" and not "raw".

Please note that your code works as an iterator _not_ because of this struct
(where you are storing a single element of your possibly "infinite" list),
_but_ because it all piggybacks on the syscall read(2), which works as an
iterator itself.



> +       hg_handle *handle;
> +       char **command;
> +       char *changeset;
> +}hg_log_iterator;
> +
> +typedef struct hg_log_entry_t{
> +       char *author;
> +       char *branch;
> +       char *date;
> +       char *desc;
> +       char *node;
> +       char *rev;
> +       char *tags;
> +}hg_log_entry_t;
> +
>  /**
>   * \brief Open the connection with the mercurial command server.
>   *
> @@ -196,6 +212,63 @@
>   * */
>  int hg_exitcode(hg_handle *handle);
>
> +/**
> + * \brief hg_log command for hglib API.
> + *
> + * It's an advance function to get revision history. It's more like the
> start
> + * point of the action, this function will prepare the query question and
> will
> + * send it to the cmd-server.
> + *
> + * Return the revision history of the specified files or the entire
> project.
> + * File history is shown without following rename or copy history of
> files.
> + * Use follow with a filename to follow history across renames and copies.
> + * follow without a filename will only show ancestors or descendants of
> the
> + * starting revision. followfirst only follows the first parent of merge
> + * revisions.
> + *
> + * If revrange isn't specified, the default is "tip:0" unless follow is
> set,
> + * in which case the working directory parent is used as the starting
> + * revision.
> + *
> + * \param handle The handle of the connection, wherewith I want to
> communicate
> + * \param option The option list for mercurial log command.
> + * \retval hg_log_iterator A pointer to hg_log_iterator structure if
> successful
> + * \retval NULL to indicate an error, with errno set appropriately.
> + *
> + * errno can be:
> + *      - hg_rawcommand errors
> + * */
> +hg_log_iterator *hg_log(hg_handle *handle, char *option[]);
> +
> +/**
> + * \brief The iterator step. Getting the next changeset.
> + *
> + * The revision history could have a huge mass of data. You can pass the
> entire
> + * history in one call, so we use an iterator-like mechanism. Calling the
> + * hg_fetch_log_entry, the next changeset will be read from cmd-server,
> parse
> + * and pass to hg_log_entry_t structure.
> + * The log_entry structure will handle a changeset with the following
> string
> + * fields:
> + *         - rev
> + *         - node
> + *         - tags (space delimited)
> + *         - branch
> + *         - author
> + *         - desc
> + *
> + * \param log_iterator The iterator for log command.
> + * \param log_entry The hg_log_entry_t structure where the changeset will
> be
> + *                   pass
> + * \retval number The lenght for the pass changeset.
> + * \retval exitcode To indicate the end of log_command.
> + * \retval   -1 to indicate an error, with errno set appropriately.
> + *
> + * errno can be:
> + *      - EINVAL  - Invalid argument (handle it's set to a null pointer)
> + *      - read(2) command errors
> + *      - read_header error
> + * */
> +int hg_fetch_log_entry(hg_log_iterator *log_iterator, hg_log_entry_t *le);
>
>  #endif
>
> diff --git a/main.c b/main.c
> new file mode 100644
> --- /dev/null
> +++ b/main.c
> @@ -0,0 +1,129 @@
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <sys/wait.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +#include "client.h"
> +#include "utils.h"
> +
> +#define INIT_REPO  "init_test_repo"
> +
> +/****** Convenience functions. *******/
> +
> +/**
> + * \brief Create and setup the tmp directory where the acction will
> happends.
> + * */
> +void setup_tmp()
> +{
> +       system("hg init tmp");
> +       chdir("tmp");
> +}
> +
> +/**
> + * \brief Remove the tmp directory and all his files.
> + * */
> +void clean_tmp()
> +{
> +       chdir("..");
> +       system("rm -rf tmp");
> +}
> +
> +/**
> + * \brief Fill the current repository with commits for log command.
> + * */
> +void setup_log()
> +{
> +       system("touch foo ; hg add foo ; hg commit -m 'foo file'");
> +       system("echo baloo > foo ; hg commit -m 'baloo text'");
> +       system("touch voodoo ; hg add voodoo ; hg commit -m voodoo");
> +       system("echo voodoo > voodoo ; hg commit -m 'voodoo text'");
> +}
> +
> +/******* Examples using level 1 implementations. ******/
> +
> +/**
> + * \brief Log command example.
> + *
> + * \param handle The handle of the connection, wherewith I want to
> communicate
> + * \retval exitcode
> + * */
> +int hg_log_example(hg_handle *handle)
> +{
> +       char *option[] = {"-v", NULL};
> +       int nc;
> +
> +       /* hg_log function will a iterator. */
> +       hg_log_iterator *log_iterator = hg_log(handle, option);
> +
> +       /* you need to alloc some space for log_entry_t structure */
> +       hg_log_entry_t *le = malloc(sizeof(hg_log_entry_t));
> +
> +       /* Getting the next changeset using the iterator-like mechanism.
> +          Print the changest from log_entry structure.*/
> +       while(nc = hg_fetch_log_entry(log_iterator, le), nc > 0){
> +               printf("rev = %s \n", le->rev);
> +               printf("node = %s \n", le->node);
> +               printf("tags = %s \n", le->tags);
> +               printf("branch = %s \n", le->branch);
> +               printf("author = %s \n", le->author);
> +               printf("desc = %s \n", le->desc);
> +               printf("date = %s \n", le->date);
> +               printf("\n");
> +       }
> +
> +       free(le);
> +       /* last call for hg_fetch_log_entry will pass the exitcode */
> +       return nc;
> +}
> +
> +/** \brief Printing the welcome message.
> + *
> + * Will print the options that you will have in this example.
> + **/
> +void print_select_case()
> +{
> +       printf("Select test case to run:\n");
> +       printf("1) log \n");
> +       printf("\n");
> +       printf("Your choice: ");
> +}
> +
> +
> +/***** Main function. *******/
> +/**
> + * \brief The main function
> + * */
> +int main(int argc, char **argv)
> +{
> +       int select_case;
> +       hg_handle *handle;
> +
> +       print_select_case();
> +       scanf("%d", &select_case);
> +       if(select_case < 1 || select_case > 1){
> +               printf("Your choice is not an option...\n");
> +               return -1;
> +       }
> +
> +       switch(select_case){
> +               case 1:
> +                       setup_tmp();
> +                       setup_log();
> +                       handle = hg_open(NULL, "");
> +
> +                       hg_log_example(handle);
> +
> +                       hg_close(&handle);
> +                       clean_tmp();
> +                       break;
> +               default:
> +                       break;
> +       }
> +
> +       return 0;
> +}
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
>
Idan Kamara - Aug. 19, 2013, 2:21 p.m.
On Mon, Aug 19, 2013 at 3:10 PM, Giovanni Gherdovich <g.gherdovich@gmail.com
> wrote:

>
>
> 2013/8/18 Iulian Stana <julian.stana@gmail.com>
>
>> # HG changeset patch
>> # User Iulian Stana <julian.stana@gmail.com>
>> # Date 1376828454 -10800
>> #      Sun Aug 18 15:20:54 2013 +0300
>> # Node ID 04bf311c99b8f4cf4c3e2096b493ee126f861b99
>> # Parent  21be7b973803e02b0bb310465691f88705a2dd53
>> c-hglib: hg_log() level 1 function
>>
>> The revision history could have a huge mass of data. To deal with this
>> issue, I
>> had created a iterator-like mechanism. In this way I will get the
>> changesets in
>> chunks or more-like one at the time.
>>
>> The hg_log function will prepare the command and then will call
>> cmd-server for
>> changesets. This function will return to the user a iterator structure,
>> to be
>> used on hg_fetch_log_entry funciton. (The log command will not passing any
>> changeset to the user)
>>
>> The hg_fetch_log_entry function will read changesets from command server
>> and
>> will pass into the hg_log_entry_t structure in a parse way the changeset.
>> The hg_log_entry_t structure will be one of the function parameter.
>>
>> In the main.c file it can be found an example on how this function can be
>> used.
>>
>> diff --git a/client.c b/client.c
>> new file mode 100644
>> --- /dev/null
>> +++ b/client.c
>> @@ -0,0 +1,134 @@
>> +#define _GNU_SOURCE
>> +#include <errno.h>
>> +#include <fcntl.h>
>> +#include <stdlib.h>
>> +#include <stdio.h>
>> +#include <string.h>
>> +#include <unistd.h>
>> +#include <sys/types.h>
>> +#include <sys/wait.h>
>> +#include <signal.h>
>> +
>> +
>> +#include "client.h"
>> +#include "utils.h"
>> +
>> +#define HGPATH "hg"
>> +#define CHANGESET
>> "{rev}\\0{node}\\0{tags}\\0{branch}\\0{author}\\0{desc}\
>> +                                               \\0{date|isodate}\\0"
>> +
>> +/**
>> + * \brief A helper for building the command arguments.
>> + * \param command The name for mercurial command.
>> + * \param options An array pointer to command option list.
>> + * \param template Template to be used for this command.
>> + * \retval new_string An array of pointers, where the command it's
>> almost ready
>> + *                    to be send to mercurial cmdserver.
>> + * */
>> +char **cmdbuilder(char *command, char *options[], char *template)
>> +{
>> +       int cmd_size, size;
>> +       char **new_cmd;
>> +       char *temp = "--template";
>> +
>> +       for(size = 0; options[size] != NULL; size++);
>> +       cmd_size = size + 1;
>> +       new_cmd = malloc(sizeof(char *) * (cmd_size + 3));
>> +
>> +       new_cmd[0] = command;
>> +       memcpy(new_cmd + 1, options, (cmd_size - 1)  * sizeof(char *));
>> +       if(template){
>> +               new_cmd[cmd_size] = temp;
>> +               new_cmd[cmd_size + 1] = template;
>> +               new_cmd[cmd_size + 2] = NULL;
>> +       }else{
>> +               new_cmd[cmd_size] = NULL;
>> +       }
>> +
>> +       return new_cmd;
>> +}
>> +
>> +/**
>> + * \brief 'Parse a changeset'. It's more like pointing to the correct
>> position.
>> + *
>> + * The changeset could be found on buff pointer. To not duplicate the
>> data I
>> + * choose to point every log_entry field to the right position.
>> + * \param buff The pointer to the changeset.
>> + * \param le   The log_entry_t structure where the changeset will be
>> parse.
>> + * \retval 0 if successful.
>> + * */
>> +int parse_changeset(char *buff, hg_log_entry_t *le)
>> +{
>> +       /* set pointer for revision position */
>> +       le->rev = buff;
>> +
>> +       /* set pointer for node position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->node = buff;
>> +
>> +       /* set pointer for tag position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->tags = buff;
>> +
>> +       /* set pointer for branch position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->branch = buff;
>> +
>> +       /* set pointer for author position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->author = buff;
>> +
>> +       /* set pointer for description position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->desc = buff;
>> +
>> +       /* set pointer for data position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->date = buff;
>> +
>> +       return 0;
>> +}
>> +
>> +/* The high level log command for hglib API. */
>> +hg_log_iterator *hg_log(hg_handle *handle, char *option[])
>> +{
>> +       hg_log_iterator *log_iterator = malloc(sizeof(hg_log_iterator));
>> +       log_iterator->handle = handle;
>> +
>> +       log_iterator->command = cmdbuilder("log", option, CHANGESET);
>> +
>> +       if(hg_rawcommand(handle, log_iterator->command) < 0){
>> +               return NULL;
>> +       }
>> +
>> +       log_iterator->changeset = malloc(0);
>> +       return log_iterator;
>>
>
>
> What happens here is that hg_log() does not return a status code
> (with the meaning "command successfully sent"), but the hg_log_iterator
> struct instead.
>
> A status code would be desirable and idiomatic I think;
> the struct to be passed as an argument.
>
>
>
>> +}
>> +
>> +/* The iterator next step. Getting the next changeset. */
>> +int hg_fetch_log_entry(hg_log_iterator *log_iterator, hg_log_entry_t *le)
>> +{
>> +       hg_header head = hg_head(log_iterator->handle);
>> +
>> +       if(head.channel == 'r'){
>> +               free(log_iterator->command);
>> +               free(log_iterator->changeset);
>> +               int exitcode = hg_exitcode(log_iterator->handle);
>> +               free(log_iterator);
>> +               return exitcode;
>> +       }
>> +
>> +       log_iterator->changeset = realloc(log_iterator->changeset,
>> head.length);
>> +
>> +       if(read(log_iterator->handle->p_read, log_iterator->changeset,
>> +                                       head.length) < 0){
>>
>
>
> Here you are not using hg_rawread(). Please explain why.
> Level 1 functions are meant to build on top of level 0 functions.
>
> Also: you are not reading in 4096 B chunks.
>
> Also: you are assuming that all information for a given log entry
> (a revision) are retrieved in a "single shot" of the  'o' channel.
>
> You are assuming that a log revision cannot be split across two
> iteration of output on the 'o' channel.
>
> Is this assumption safe?
>

No.
Iulian Stana - Aug. 19, 2013, 4:40 p.m.
2013/8/19 Idan Kamara <idankk86@gmail.com>

> On Mon, Aug 19, 2013 at 3:10 PM, Giovanni Gherdovich <
> g.gherdovich@gmail.com> wrote:
>
>>
>>
>> 2013/8/18 Iulian Stana <julian.stana@gmail.com>
>>
>>> # HG changeset patch
>>> # User Iulian Stana <julian.stana@gmail.com>
>>> # Date 1376828454 -10800
>>> #      Sun Aug 18 15:20:54 2013 +0300
>>> # Node ID 04bf311c99b8f4cf4c3e2096b493ee126f861b99
>>> # Parent  21be7b973803e02b0bb310465691f88705a2dd53
>>> c-hglib: hg_log() level 1 function
>>>
>>> The revision history could have a huge mass of data. To deal with this
>>> issue, I
>>> had created a iterator-like mechanism. In this way I will get the
>>> changesets in
>>> chunks or more-like one at the time.
>>>
>>> The hg_log function will prepare the command and then will call
>>> cmd-server for
>>> changesets. This function will return to the user a iterator structure,
>>> to be
>>> used on hg_fetch_log_entry funciton. (The log command will not passing
>>> any
>>> changeset to the user)
>>>
>>> The hg_fetch_log_entry function will read changesets from command server
>>> and
>>> will pass into the hg_log_entry_t structure in a parse way the changeset.
>>> The hg_log_entry_t structure will be one of the function parameter.
>>>
>>> In the main.c file it can be found an example on how this function can
>>> be used.
>>>
>>> diff --git a/client.c b/client.c
>>> new file mode 100644
>>> --- /dev/null
>>> +++ b/client.c
>>> @@ -0,0 +1,134 @@
>>> +#define _GNU_SOURCE
>>> +#include <errno.h>
>>> +#include <fcntl.h>
>>> +#include <stdlib.h>
>>> +#include <stdio.h>
>>> +#include <string.h>
>>> +#include <unistd.h>
>>> +#include <sys/types.h>
>>> +#include <sys/wait.h>
>>> +#include <signal.h>
>>> +
>>> +
>>> +#include "client.h"
>>> +#include "utils.h"
>>> +
>>> +#define HGPATH "hg"
>>> +#define CHANGESET
>>> "{rev}\\0{node}\\0{tags}\\0{branch}\\0{author}\\0{desc}\
>>> +                                               \\0{date|isodate}\\0"
>>> +
>>> +/**
>>> + * \brief A helper for building the command arguments.
>>> + * \param command The name for mercurial command.
>>> + * \param options An array pointer to command option list.
>>> + * \param template Template to be used for this command.
>>> + * \retval new_string An array of pointers, where the command it's
>>> almost ready
>>> + *                    to be send to mercurial cmdserver.
>>> + * */
>>> +char **cmdbuilder(char *command, char *options[], char *template)
>>> +{
>>> +       int cmd_size, size;
>>> +       char **new_cmd;
>>> +       char *temp = "--template";
>>> +
>>> +       for(size = 0; options[size] != NULL; size++);
>>> +       cmd_size = size + 1;
>>> +       new_cmd = malloc(sizeof(char *) * (cmd_size + 3));
>>> +
>>> +       new_cmd[0] = command;
>>> +       memcpy(new_cmd + 1, options, (cmd_size - 1)  * sizeof(char *));
>>> +       if(template){
>>> +               new_cmd[cmd_size] = temp;
>>> +               new_cmd[cmd_size + 1] = template;
>>> +               new_cmd[cmd_size + 2] = NULL;
>>> +       }else{
>>> +               new_cmd[cmd_size] = NULL;
>>> +       }
>>> +
>>> +       return new_cmd;
>>> +}
>>> +
>>> +/**
>>> + * \brief 'Parse a changeset'. It's more like pointing to the correct
>>> position.
>>> + *
>>> + * The changeset could be found on buff pointer. To not duplicate the
>>> data I
>>> + * choose to point every log_entry field to the right position.
>>> + * \param buff The pointer to the changeset.
>>> + * \param le   The log_entry_t structure where the changeset will be
>>> parse.
>>> + * \retval 0 if successful.
>>> + * */
>>> +int parse_changeset(char *buff, hg_log_entry_t *le)
>>> +{
>>> +       /* set pointer for revision position */
>>> +       le->rev = buff;
>>> +
>>> +       /* set pointer for node position */
>>> +       buff = buff + strlen(buff) + 1;
>>> +       le->node = buff;
>>> +
>>> +       /* set pointer for tag position */
>>> +       buff = buff + strlen(buff) + 1;
>>> +       le->tags = buff;
>>> +
>>> +       /* set pointer for branch position */
>>> +       buff = buff + strlen(buff) + 1;
>>> +       le->branch = buff;
>>> +
>>> +       /* set pointer for author position */
>>> +       buff = buff + strlen(buff) + 1;
>>> +       le->author = buff;
>>> +
>>> +       /* set pointer for description position */
>>> +       buff = buff + strlen(buff) + 1;
>>> +       le->desc = buff;
>>> +
>>> +       /* set pointer for data position */
>>> +       buff = buff + strlen(buff) + 1;
>>> +       le->date = buff;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +/* The high level log command for hglib API. */
>>> +hg_log_iterator *hg_log(hg_handle *handle, char *option[])
>>> +{
>>> +       hg_log_iterator *log_iterator = malloc(sizeof(hg_log_iterator));
>>> +       log_iterator->handle = handle;
>>> +
>>> +       log_iterator->command = cmdbuilder("log", option, CHANGESET);
>>> +
>>> +       if(hg_rawcommand(handle, log_iterator->command) < 0){
>>> +               return NULL;
>>> +       }
>>> +
>>> +       log_iterator->changeset = malloc(0);
>>> +       return log_iterator;
>>>
>>
>>
>> What happens here is that hg_log() does not return a status code
>> (with the meaning "command successfully sent"), but the hg_log_iterator
>> struct instead.
>>
>> A status code would be desirable and idiomatic I think;
>> the struct to be passed as an argument.
>>
>>
>>
>>> +}
>>> +
>>> +/* The iterator next step. Getting the next changeset. */
>>> +int hg_fetch_log_entry(hg_log_iterator *log_iterator, hg_log_entry_t
>>> *le)
>>> +{
>>> +       hg_header head = hg_head(log_iterator->handle);
>>> +
>>> +       if(head.channel == 'r'){
>>> +               free(log_iterator->command);
>>> +               free(log_iterator->changeset);
>>> +               int exitcode = hg_exitcode(log_iterator->handle);
>>> +               free(log_iterator);
>>> +               return exitcode;
>>> +       }
>>> +
>>> +       log_iterator->changeset = realloc(log_iterator->changeset,
>>> head.length);
>>> +
>>> +       if(read(log_iterator->handle->p_read, log_iterator->changeset,
>>> +                                       head.length) < 0){
>>>
>>
>>
>> Here you are not using hg_rawread(). Please explain why.
>> Level 1 functions are meant to build on top of level 0 functions.
>>
>> Also: you are not reading in 4096 B chunks.
>>
>> Also: you are assuming that all information for a given log entry
>> (a revision) are retrieved in a "single shot" of the  'o' channel.
>>
>> You are assuming that a log revision cannot be split across two
>> iteration of output on the 'o' channel.
>>
>> Is this assumption safe?
>>
>
> No.
>

Your answer is not clear... You want to say that the server will
give a revision in more "shots"? From my test I saw that the cmdserver
will send a log entry in a single call ( when I am using a template)
even if that revision have 20 kB.
The header from cmdserver is something like channel = 'o' length = 20000
data = [...]

Or the operation of getting the entire data in one call, is the bad thing?

Also: If there will be more iterations for a revision, How could I know how
much iterations there are?
Iulian Stana - Aug. 19, 2013, 5:18 p.m.
2013/8/19 Giovanni Gherdovich <g.gherdovich@gmail.com>

>
>
> 2013/8/18 Iulian Stana <julian.stana@gmail.com>
>
>> # HG changeset patch
>> # User Iulian Stana <julian.stana@gmail.com>
>> # Date 1376828454 -10800
>> #      Sun Aug 18 15:20:54 2013 +0300
>> # Node ID 04bf311c99b8f4cf4c3e2096b493ee126f861b99
>> # Parent  21be7b973803e02b0bb310465691f88705a2dd53
>> c-hglib: hg_log() level 1 function
>>
>> The revision history could have a huge mass of data. To deal with this
>> issue, I
>> had created a iterator-like mechanism. In this way I will get the
>> changesets in
>> chunks or more-like one at the time.
>>
>> The hg_log function will prepare the command and then will call
>> cmd-server for
>> changesets. This function will return to the user a iterator structure,
>> to be
>> used on hg_fetch_log_entry funciton. (The log command will not passing any
>> changeset to the user)
>>
>> The hg_fetch_log_entry function will read changesets from command server
>> and
>> will pass into the hg_log_entry_t structure in a parse way the changeset.
>> The hg_log_entry_t structure will be one of the function parameter.
>>
>> In the main.c file it can be found an example on how this function can be
>> used.
>>
>> diff --git a/client.c b/client.c
>> new file mode 100644
>> --- /dev/null
>> +++ b/client.c
>> @@ -0,0 +1,134 @@
>> +#define _GNU_SOURCE
>> +#include <errno.h>
>> +#include <fcntl.h>
>> +#include <stdlib.h>
>> +#include <stdio.h>
>> +#include <string.h>
>> +#include <unistd.h>
>> +#include <sys/types.h>
>> +#include <sys/wait.h>
>> +#include <signal.h>
>> +
>> +
>> +#include "client.h"
>> +#include "utils.h"
>> +
>> +#define HGPATH "hg"
>> +#define CHANGESET
>> "{rev}\\0{node}\\0{tags}\\0{branch}\\0{author}\\0{desc}\
>> +                                               \\0{date|isodate}\\0"
>> +
>> +/**
>> + * \brief A helper for building the command arguments.
>> + * \param command The name for mercurial command.
>> + * \param options An array pointer to command option list.
>> + * \param template Template to be used for this command.
>> + * \retval new_string An array of pointers, where the command it's
>> almost ready
>> + *                    to be send to mercurial cmdserver.
>> + * */
>> +char **cmdbuilder(char *command, char *options[], char *template)
>> +{
>> +       int cmd_size, size;
>> +       char **new_cmd;
>> +       char *temp = "--template";
>> +
>> +       for(size = 0; options[size] != NULL; size++);
>> +       cmd_size = size + 1;
>> +       new_cmd = malloc(sizeof(char *) * (cmd_size + 3));
>> +
>> +       new_cmd[0] = command;
>> +       memcpy(new_cmd + 1, options, (cmd_size - 1)  * sizeof(char *));
>> +       if(template){
>> +               new_cmd[cmd_size] = temp;
>> +               new_cmd[cmd_size + 1] = template;
>> +               new_cmd[cmd_size + 2] = NULL;
>> +       }else{
>> +               new_cmd[cmd_size] = NULL;
>> +       }
>> +
>> +       return new_cmd;
>> +}
>> +
>> +/**
>> + * \brief 'Parse a changeset'. It's more like pointing to the correct
>> position.
>> + *
>> + * The changeset could be found on buff pointer. To not duplicate the
>> data I
>> + * choose to point every log_entry field to the right position.
>> + * \param buff The pointer to the changeset.
>> + * \param le   The log_entry_t structure where the changeset will be
>> parse.
>> + * \retval 0 if successful.
>> + * */
>> +int parse_changeset(char *buff, hg_log_entry_t *le)
>> +{
>> +       /* set pointer for revision position */
>> +       le->rev = buff;
>> +
>> +       /* set pointer for node position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->node = buff;
>> +
>> +       /* set pointer for tag position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->tags = buff;
>> +
>> +       /* set pointer for branch position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->branch = buff;
>> +
>> +       /* set pointer for author position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->author = buff;
>> +
>> +       /* set pointer for description position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->desc = buff;
>> +
>> +       /* set pointer for data position */
>> +       buff = buff + strlen(buff) + 1;
>> +       le->date = buff;
>> +
>> +       return 0;
>> +}
>> +
>> +/* The high level log command for hglib API. */
>> +hg_log_iterator *hg_log(hg_handle *handle, char *option[])
>> +{
>> +       hg_log_iterator *log_iterator = malloc(sizeof(hg_log_iterator));
>> +       log_iterator->handle = handle;
>> +
>> +       log_iterator->command = cmdbuilder("log", option, CHANGESET);
>> +
>> +       if(hg_rawcommand(handle, log_iterator->command) < 0){
>> +               return NULL;
>> +       }
>> +
>> +       log_iterator->changeset = malloc(0);
>> +       return log_iterator;
>>
>
>
> What happens here is that hg_log() does not return a status code
> (with the meaning "command successfully sent"), but the hg_log_iterator
> struct instead.
>
> A status code would be desirable and idiomatic I think;
> the struct to be passed as an argument.
>

I don't really know what is the best way of passing the status code...
but the NULL pointer could mean that an error had occur.


>
>
>
>> +}
>> +
>> +/* The iterator next step. Getting the next changeset. */
>> +int hg_fetch_log_entry(hg_log_iterator *log_iterator, hg_log_entry_t *le)
>> +{
>> +       hg_header head = hg_head(log_iterator->handle);
>> +
>> +       if(head.channel == 'r'){
>> +               free(log_iterator->command);
>> +               free(log_iterator->changeset);
>> +               int exitcode = hg_exitcode(log_iterator->handle);
>> +               free(log_iterator);
>> +               return exitcode;
>> +       }
>> +
>> +       log_iterator->changeset = realloc(log_iterator->changeset,
>> head.length);
>> +
>> +       if(read(log_iterator->handle->p_read, log_iterator->changeset,
>> +                                       head.length) < 0){
>>
>
>
> Here you are not using hg_rawread(). Please explain why.
> Level 1 functions are meant to build on top of level 0 functions.
>
> Also: you are not reading in 4096 B chunks.
>

UPSSSS...Yes I don't know way I choose to not using the hg_rawread()
function... I will change this....

About using chunks of 4k, like I said in the earlier mail I don't know
if this is the best option. In parsing process you will need the entire
revision. This means that you need somehow to concatenate those
chunks. I prefer to read the entire revision instead of making this
expensive operation (concatenate data).

For example:
If you have a revision of 20MB, it's more easy to read in a single
call this data, (if it's possible) instead of performing 20MB/ 4096B
calls and some concatenate operation calls for every call.
concatenate operation(realloc memory and copy the memory)



>
> Also: you are assuming that all information for a given log entry
> (a revision) are retrieved in a "single shot" of the  'o' channel.
>

> You are assuming that a log revision cannot be split across two
> iteration of output on the 'o' channel.
>
> Is this assumption safe?
>
> Please ask Idan Kamara (CC the list) and make sure of this.
>
>
>
>> +               return -1;
>> +       }
>> +
>> +       parse_changeset(log_iterator->changeset, le);
>> +
>> +       if(read_header(log_iterator->handle) < 0){
>> +               return -1;
>> +       }
>> +       return head.length;
>> +}
>> diff --git a/client.h b/client.h
>> --- a/client.h
>> +++ b/client.h
>> @@ -65,6 +65,22 @@
>>         int protect;
>>  } hg_handle;
>>
>> +typedef struct hg_log_iterator{
>>
>
>
> I think this name is unfortunate. This is not an iterator, in any possible
> sense.
> This is the place where you store your data (the "changeset" field of this
> structure).
>
> I would call this something like hg_log_rawentry_t, in contrast to the
> other structure where the log entry is "parsed" and not "raw".
>

Hmmm, agreed with you. Let's talk about this on IRC channel.
Also this iterator-like mechanism probably will be used in other places
too. So a more common name probably will fit better...



>
> Please note that your code works as an iterator _not_ because of this
> struct
> (where you are storing a single element of your possibly "infinite" list),
> _but_ because it all piggybacks on the syscall read(2), which works as an
> iterator itself.
>
>
>
>> +       hg_handle *handle;
>> +       char **command;
>> +       char *changeset;
>> +}hg_log_iterator;
>> +
>> +typedef struct hg_log_entry_t{
>> +       char *author;
>> +       char *branch;
>> +       char *date;
>> +       char *desc;
>> +       char *node;
>> +       char *rev;
>> +       char *tags;
>> +}hg_log_entry_t;
>> +
>>  /**
>>   * \brief Open the connection with the mercurial command server.
>>   *
>> @@ -196,6 +212,63 @@
>>   * */
>>  int hg_exitcode(hg_handle *handle);
>>
>> +/**
>> + * \brief hg_log command for hglib API.
>> + *
>> + * It's an advance function to get revision history. It's more like the
>> start
>> + * point of the action, this function will prepare the query question
>> and will
>> + * send it to the cmd-server.
>> + *
>> + * Return the revision history of the specified files or the entire
>> project.
>> + * File history is shown without following rename or copy history of
>> files.
>> + * Use follow with a filename to follow history across renames and
>> copies.
>> + * follow without a filename will only show ancestors or descendants of
>> the
>> + * starting revision. followfirst only follows the first parent of merge
>> + * revisions.
>> + *
>> + * If revrange isn't specified, the default is "tip:0" unless follow is
>> set,
>> + * in which case the working directory parent is used as the starting
>> + * revision.
>> + *
>> + * \param handle The handle of the connection, wherewith I want to
>> communicate
>> + * \param option The option list for mercurial log command.
>> + * \retval hg_log_iterator A pointer to hg_log_iterator structure if
>> successful
>> + * \retval NULL to indicate an error, with errno set appropriately.
>> + *
>> + * errno can be:
>> + *      - hg_rawcommand errors
>> + * */
>> +hg_log_iterator *hg_log(hg_handle *handle, char *option[]);
>> +
>> +/**
>> + * \brief The iterator step. Getting the next changeset.
>> + *
>> + * The revision history could have a huge mass of data. You can pass the
>> entire
>> + * history in one call, so we use an iterator-like mechanism. Calling the
>> + * hg_fetch_log_entry, the next changeset will be read from cmd-server,
>> parse
>> + * and pass to hg_log_entry_t structure.
>> + * The log_entry structure will handle a changeset with the following
>> string
>> + * fields:
>> + *         - rev
>> + *         - node
>> + *         - tags (space delimited)
>> + *         - branch
>> + *         - author
>> + *         - desc
>> + *
>> + * \param log_iterator The iterator for log command.
>> + * \param log_entry The hg_log_entry_t structure where the changeset
>> will be
>> + *                   pass
>> + * \retval number The lenght for the pass changeset.
>> + * \retval exitcode To indicate the end of log_command.
>> + * \retval   -1 to indicate an error, with errno set appropriately.
>> + *
>> + * errno can be:
>> + *      - EINVAL  - Invalid argument (handle it's set to a null pointer)
>> + *      - read(2) command errors
>> + *      - read_header error
>> + * */
>> +int hg_fetch_log_entry(hg_log_iterator *log_iterator, hg_log_entry_t
>> *le);
>>
>>  #endif
>>
>> diff --git a/main.c b/main.c
>> new file mode 100644
>> --- /dev/null
>> +++ b/main.c
>> @@ -0,0 +1,129 @@
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +
>> +#include <sys/wait.h>
>> +#include <sys/types.h>
>> +#include <unistd.h>
>> +#include <sys/stat.h>
>> +#include <fcntl.h>
>> +
>> +#include "client.h"
>> +#include "utils.h"
>> +
>> +#define INIT_REPO  "init_test_repo"
>> +
>> +/****** Convenience functions. *******/
>> +
>> +/**
>> + * \brief Create and setup the tmp directory where the acction will
>> happends.
>> + * */
>> +void setup_tmp()
>> +{
>> +       system("hg init tmp");
>> +       chdir("tmp");
>> +}
>> +
>> +/**
>> + * \brief Remove the tmp directory and all his files.
>> + * */
>> +void clean_tmp()
>> +{
>> +       chdir("..");
>> +       system("rm -rf tmp");
>> +}
>> +
>> +/**
>> + * \brief Fill the current repository with commits for log command.
>> + * */
>> +void setup_log()
>> +{
>> +       system("touch foo ; hg add foo ; hg commit -m 'foo file'");
>> +       system("echo baloo > foo ; hg commit -m 'baloo text'");
>> +       system("touch voodoo ; hg add voodoo ; hg commit -m voodoo");
>> +       system("echo voodoo > voodoo ; hg commit -m 'voodoo text'");
>> +}
>> +
>> +/******* Examples using level 1 implementations. ******/
>> +
>> +/**
>> + * \brief Log command example.
>> + *
>> + * \param handle The handle of the connection, wherewith I want to
>> communicate
>> + * \retval exitcode
>> + * */
>> +int hg_log_example(hg_handle *handle)
>> +{
>> +       char *option[] = {"-v", NULL};
>> +       int nc;
>> +
>> +       /* hg_log function will a iterator. */
>> +       hg_log_iterator *log_iterator = hg_log(handle, option);
>> +
>> +       /* you need to alloc some space for log_entry_t structure */
>> +       hg_log_entry_t *le = malloc(sizeof(hg_log_entry_t));
>> +
>> +       /* Getting the next changeset using the iterator-like mechanism.
>> +          Print the changest from log_entry structure.*/
>> +       while(nc = hg_fetch_log_entry(log_iterator, le), nc > 0){
>> +               printf("rev = %s \n", le->rev);
>> +               printf("node = %s \n", le->node);
>> +               printf("tags = %s \n", le->tags);
>> +               printf("branch = %s \n", le->branch);
>> +               printf("author = %s \n", le->author);
>> +               printf("desc = %s \n", le->desc);
>> +               printf("date = %s \n", le->date);
>> +               printf("\n");
>> +       }
>> +
>> +       free(le);
>> +       /* last call for hg_fetch_log_entry will pass the exitcode */
>> +       return nc;
>> +}
>> +
>> +/** \brief Printing the welcome message.
>> + *
>> + * Will print the options that you will have in this example.
>> + **/
>> +void print_select_case()
>> +{
>> +       printf("Select test case to run:\n");
>> +       printf("1) log \n");
>> +       printf("\n");
>> +       printf("Your choice: ");
>> +}
>> +
>> +
>> +/***** Main function. *******/
>> +/**
>> + * \brief The main function
>> + * */
>> +int main(int argc, char **argv)
>> +{
>> +       int select_case;
>> +       hg_handle *handle;
>> +
>> +       print_select_case();
>> +       scanf("%d", &select_case);
>> +       if(select_case < 1 || select_case > 1){
>> +               printf("Your choice is not an option...\n");
>> +               return -1;
>> +       }
>> +
>> +       switch(select_case){
>> +               case 1:
>> +                       setup_tmp();
>> +                       setup_log();
>> +                       handle = hg_open(NULL, "");
>> +
>> +                       hg_log_example(handle);
>> +
>> +                       hg_close(&handle);
>> +                       clean_tmp();
>> +                       break;
>> +               default:
>> +                       break;
>> +       }
>> +
>> +       return 0;
>> +}
>> _______________________________________________
>> Mercurial-devel mailing list
>> Mercurial-devel@selenic.com
>> http://selenic.com/mailman/listinfo/mercurial-devel
>>
>
>
Idan Kamara - Aug. 19, 2013, 7:37 p.m.
On Mon, Aug 19, 2013 at 7:40 PM, Iulian Stana <julian.stana@gmail.com>wrote:

> 2013/8/19 Idan Kamara <idankk86@gmail.com>
>
>> On Mon, Aug 19, 2013 at 3:10 PM, Giovanni Gherdovich <
>> g.gherdovich@gmail.com> wrote:
>>
>>>
>>>
>>> 2013/8/18 Iulian Stana <julian.stana@gmail.com>
>>>
>>>> # HG changeset patch
>>>> # User Iulian Stana <julian.stana@gmail.com>
>>>> # Date 1376828454 -10800
>>>> #      Sun Aug 18 15:20:54 2013 +0300
>>>> # Node ID 04bf311c99b8f4cf4c3e2096b493ee126f861b99
>>>> # Parent  21be7b973803e02b0bb310465691f88705a2dd53
>>>> c-hglib: hg_log() level 1 function
>>>>
>>>> The revision history could have a huge mass of data. To deal with this
>>>> issue, I
>>>> had created a iterator-like mechanism. In this way I will get the
>>>> changesets in
>>>> chunks or more-like one at the time.
>>>>
>>>> The hg_log function will prepare the command and then will call
>>>> cmd-server for
>>>> changesets. This function will return to the user a iterator structure,
>>>> to be
>>>> used on hg_fetch_log_entry funciton. (The log command will not passing
>>>> any
>>>> changeset to the user)
>>>>
>>>> The hg_fetch_log_entry function will read changesets from command
>>>> server and
>>>> will pass into the hg_log_entry_t structure in a parse way the
>>>> changeset.
>>>> The hg_log_entry_t structure will be one of the function parameter.
>>>>
>>>> In the main.c file it can be found an example on how this function can
>>>> be used.
>>>>
>>>> diff --git a/client.c b/client.c
>>>> new file mode 100644
>>>> --- /dev/null
>>>> +++ b/client.c
>>>> @@ -0,0 +1,134 @@
>>>> +#define _GNU_SOURCE
>>>> +#include <errno.h>
>>>> +#include <fcntl.h>
>>>> +#include <stdlib.h>
>>>> +#include <stdio.h>
>>>> +#include <string.h>
>>>> +#include <unistd.h>
>>>> +#include <sys/types.h>
>>>> +#include <sys/wait.h>
>>>> +#include <signal.h>
>>>> +
>>>> +
>>>> +#include "client.h"
>>>> +#include "utils.h"
>>>> +
>>>> +#define HGPATH "hg"
>>>> +#define CHANGESET
>>>> "{rev}\\0{node}\\0{tags}\\0{branch}\\0{author}\\0{desc}\
>>>> +                                               \\0{date|isodate}\\0"
>>>> +
>>>> +/**
>>>> + * \brief A helper for building the command arguments.
>>>> + * \param command The name for mercurial command.
>>>> + * \param options An array pointer to command option list.
>>>> + * \param template Template to be used for this command.
>>>> + * \retval new_string An array of pointers, where the command it's
>>>> almost ready
>>>> + *                    to be send to mercurial cmdserver.
>>>> + * */
>>>> +char **cmdbuilder(char *command, char *options[], char *template)
>>>> +{
>>>> +       int cmd_size, size;
>>>> +       char **new_cmd;
>>>> +       char *temp = "--template";
>>>> +
>>>> +       for(size = 0; options[size] != NULL; size++);
>>>> +       cmd_size = size + 1;
>>>> +       new_cmd = malloc(sizeof(char *) * (cmd_size + 3));
>>>> +
>>>> +       new_cmd[0] = command;
>>>> +       memcpy(new_cmd + 1, options, (cmd_size - 1)  * sizeof(char *));
>>>> +       if(template){
>>>> +               new_cmd[cmd_size] = temp;
>>>> +               new_cmd[cmd_size + 1] = template;
>>>> +               new_cmd[cmd_size + 2] = NULL;
>>>> +       }else{
>>>> +               new_cmd[cmd_size] = NULL;
>>>> +       }
>>>> +
>>>> +       return new_cmd;
>>>> +}
>>>> +
>>>> +/**
>>>> + * \brief 'Parse a changeset'. It's more like pointing to the correct
>>>> position.
>>>> + *
>>>> + * The changeset could be found on buff pointer. To not duplicate the
>>>> data I
>>>> + * choose to point every log_entry field to the right position.
>>>> + * \param buff The pointer to the changeset.
>>>> + * \param le   The log_entry_t structure where the changeset will be
>>>> parse.
>>>> + * \retval 0 if successful.
>>>> + * */
>>>> +int parse_changeset(char *buff, hg_log_entry_t *le)
>>>> +{
>>>> +       /* set pointer for revision position */
>>>> +       le->rev = buff;
>>>> +
>>>> +       /* set pointer for node position */
>>>> +       buff = buff + strlen(buff) + 1;
>>>> +       le->node = buff;
>>>> +
>>>> +       /* set pointer for tag position */
>>>> +       buff = buff + strlen(buff) + 1;
>>>> +       le->tags = buff;
>>>> +
>>>> +       /* set pointer for branch position */
>>>> +       buff = buff + strlen(buff) + 1;
>>>> +       le->branch = buff;
>>>> +
>>>> +       /* set pointer for author position */
>>>> +       buff = buff + strlen(buff) + 1;
>>>> +       le->author = buff;
>>>> +
>>>> +       /* set pointer for description position */
>>>> +       buff = buff + strlen(buff) + 1;
>>>> +       le->desc = buff;
>>>> +
>>>> +       /* set pointer for data position */
>>>> +       buff = buff + strlen(buff) + 1;
>>>> +       le->date = buff;
>>>> +
>>>> +       return 0;
>>>> +}
>>>> +
>>>> +/* The high level log command for hglib API. */
>>>> +hg_log_iterator *hg_log(hg_handle *handle, char *option[])
>>>> +{
>>>> +       hg_log_iterator *log_iterator = malloc(sizeof(hg_log_iterator));
>>>> +       log_iterator->handle = handle;
>>>> +
>>>> +       log_iterator->command = cmdbuilder("log", option, CHANGESET);
>>>> +
>>>> +       if(hg_rawcommand(handle, log_iterator->command) < 0){
>>>> +               return NULL;
>>>> +       }
>>>> +
>>>> +       log_iterator->changeset = malloc(0);
>>>> +       return log_iterator;
>>>>
>>>
>>>
>>> What happens here is that hg_log() does not return a status code
>>> (with the meaning "command successfully sent"), but the hg_log_iterator
>>> struct instead.
>>>
>>> A status code would be desirable and idiomatic I think;
>>> the struct to be passed as an argument.
>>>
>>>
>>>
>>>> +}
>>>> +
>>>> +/* The iterator next step. Getting the next changeset. */
>>>> +int hg_fetch_log_entry(hg_log_iterator *log_iterator, hg_log_entry_t
>>>> *le)
>>>> +{
>>>> +       hg_header head = hg_head(log_iterator->handle);
>>>> +
>>>> +       if(head.channel == 'r'){
>>>> +               free(log_iterator->command);
>>>> +               free(log_iterator->changeset);
>>>> +               int exitcode = hg_exitcode(log_iterator->handle);
>>>> +               free(log_iterator);
>>>> +               return exitcode;
>>>> +       }
>>>> +
>>>> +       log_iterator->changeset = realloc(log_iterator->changeset,
>>>> head.length);
>>>> +
>>>> +       if(read(log_iterator->handle->p_read, log_iterator->changeset,
>>>> +                                       head.length) < 0){
>>>>
>>>
>>>
>>> Here you are not using hg_rawread(). Please explain why.
>>> Level 1 functions are meant to build on top of level 0 functions.
>>>
>>> Also: you are not reading in 4096 B chunks.
>>>
>>> Also: you are assuming that all information for a given log entry
>>> (a revision) are retrieved in a "single shot" of the  'o' channel.
>>>
>>> You are assuming that a log revision cannot be split across two
>>> iteration of output on the 'o' channel.
>>>
>>> Is this assumption safe?
>>>
>>
>> No.
>>
>
> Your answer is not clear... You want to say that the server will
> give a revision in more "shots"? From my test I saw that the cmdserver
> will send a log entry in a single call ( when I am using a template)
> even if that revision have 20 kB.
> The header from cmdserver is something like channel = 'o' length = 20000
> data = [...]
>

The command server is oblivious to the data it writes. It simply forwards
whatever hg normally writes to stdout, but in the command protocol.
So nothing guarantees what you're seeing, even though that's what happens
in practice.


> Or the operation of getting the entire data in one call, is the bad thing?
>

What data is that? All the output generated by 'hg log ..'? Or the data for
one
log entry?


>
> Also: If there will be more iterations for a revision, How could I know
> how
> much iterations there are?
>

Your template defines how to parse the output data. The result channel
tells you the current command ended.
Iulian Stana - Aug. 19, 2013, 7:53 p.m.
2013/8/19 Idan Kamara <idankk86@gmail.com>

> On Mon, Aug 19, 2013 at 7:40 PM, Iulian Stana <julian.stana@gmail.com>wrote:
>
>> 2013/8/19 Idan Kamara <idankk86@gmail.com>
>>
>>> On Mon, Aug 19, 2013 at 3:10 PM, Giovanni Gherdovich <
>>> g.gherdovich@gmail.com> wrote:
>>>
>>>>
>>>>
>>>> 2013/8/18 Iulian Stana <julian.stana@gmail.com>
>>>>
>>>>> # HG changeset patch
>>>>> # User Iulian Stana <julian.stana@gmail.com>
>>>>> # Date 1376828454 -10800
>>>>> #      Sun Aug 18 15:20:54 2013 +0300
>>>>> # Node ID 04bf311c99b8f4cf4c3e2096b493ee126f861b99
>>>>> # Parent  21be7b973803e02b0bb310465691f88705a2dd53
>>>>> c-hglib: hg_log() level 1 function
>>>>>
>>>>> The revision history could have a huge mass of data. To deal with this
>>>>> issue, I
>>>>> had created a iterator-like mechanism. In this way I will get the
>>>>> changesets in
>>>>> chunks or more-like one at the time.
>>>>>
>>>>> The hg_log function will prepare the command and then will call
>>>>> cmd-server for
>>>>> changesets. This function will return to the user a iterator
>>>>> structure, to be
>>>>> used on hg_fetch_log_entry funciton. (The log command will not passing
>>>>> any
>>>>> changeset to the user)
>>>>>
>>>>> The hg_fetch_log_entry function will read changesets from command
>>>>> server and
>>>>> will pass into the hg_log_entry_t structure in a parse way the
>>>>> changeset.
>>>>> The hg_log_entry_t structure will be one of the function parameter.
>>>>>
>>>>> In the main.c file it can be found an example on how this function can
>>>>> be used.
>>>>>
>>>>> diff --git a/client.c b/client.c
>>>>> new file mode 100644
>>>>> --- /dev/null
>>>>> +++ b/client.c
>>>>> @@ -0,0 +1,134 @@
>>>>> +#define _GNU_SOURCE
>>>>> +#include <errno.h>
>>>>> +#include <fcntl.h>
>>>>> +#include <stdlib.h>
>>>>> +#include <stdio.h>
>>>>> +#include <string.h>
>>>>> +#include <unistd.h>
>>>>> +#include <sys/types.h>
>>>>> +#include <sys/wait.h>
>>>>> +#include <signal.h>
>>>>> +
>>>>> +
>>>>> +#include "client.h"
>>>>> +#include "utils.h"
>>>>> +
>>>>> +#define HGPATH "hg"
>>>>> +#define CHANGESET
>>>>> "{rev}\\0{node}\\0{tags}\\0{branch}\\0{author}\\0{desc}\
>>>>> +                                               \\0{date|isodate}\\0"
>>>>> +
>>>>> +/**
>>>>> + * \brief A helper for building the command arguments.
>>>>> + * \param command The name for mercurial command.
>>>>> + * \param options An array pointer to command option list.
>>>>> + * \param template Template to be used for this command.
>>>>> + * \retval new_string An array of pointers, where the command it's
>>>>> almost ready
>>>>> + *                    to be send to mercurial cmdserver.
>>>>> + * */
>>>>> +char **cmdbuilder(char *command, char *options[], char *template)
>>>>> +{
>>>>> +       int cmd_size, size;
>>>>> +       char **new_cmd;
>>>>> +       char *temp = "--template";
>>>>> +
>>>>> +       for(size = 0; options[size] != NULL; size++);
>>>>> +       cmd_size = size + 1;
>>>>> +       new_cmd = malloc(sizeof(char *) * (cmd_size + 3));
>>>>> +
>>>>> +       new_cmd[0] = command;
>>>>> +       memcpy(new_cmd + 1, options, (cmd_size - 1)  * sizeof(char *));
>>>>> +       if(template){
>>>>> +               new_cmd[cmd_size] = temp;
>>>>> +               new_cmd[cmd_size + 1] = template;
>>>>> +               new_cmd[cmd_size + 2] = NULL;
>>>>> +       }else{
>>>>> +               new_cmd[cmd_size] = NULL;
>>>>> +       }
>>>>> +
>>>>> +       return new_cmd;
>>>>> +}
>>>>> +
>>>>> +/**
>>>>> + * \brief 'Parse a changeset'. It's more like pointing to the correct
>>>>> position.
>>>>> + *
>>>>> + * The changeset could be found on buff pointer. To not duplicate the
>>>>> data I
>>>>> + * choose to point every log_entry field to the right position.
>>>>> + * \param buff The pointer to the changeset.
>>>>> + * \param le   The log_entry_t structure where the changeset will be
>>>>> parse.
>>>>> + * \retval 0 if successful.
>>>>> + * */
>>>>> +int parse_changeset(char *buff, hg_log_entry_t *le)
>>>>> +{
>>>>> +       /* set pointer for revision position */
>>>>> +       le->rev = buff;
>>>>> +
>>>>> +       /* set pointer for node position */
>>>>> +       buff = buff + strlen(buff) + 1;
>>>>> +       le->node = buff;
>>>>> +
>>>>> +       /* set pointer for tag position */
>>>>> +       buff = buff + strlen(buff) + 1;
>>>>> +       le->tags = buff;
>>>>> +
>>>>> +       /* set pointer for branch position */
>>>>> +       buff = buff + strlen(buff) + 1;
>>>>> +       le->branch = buff;
>>>>> +
>>>>> +       /* set pointer for author position */
>>>>> +       buff = buff + strlen(buff) + 1;
>>>>> +       le->author = buff;
>>>>> +
>>>>> +       /* set pointer for description position */
>>>>> +       buff = buff + strlen(buff) + 1;
>>>>> +       le->desc = buff;
>>>>> +
>>>>> +       /* set pointer for data position */
>>>>> +       buff = buff + strlen(buff) + 1;
>>>>> +       le->date = buff;
>>>>> +
>>>>> +       return 0;
>>>>> +}
>>>>> +
>>>>> +/* The high level log command for hglib API. */
>>>>> +hg_log_iterator *hg_log(hg_handle *handle, char *option[])
>>>>> +{
>>>>> +       hg_log_iterator *log_iterator =
>>>>> malloc(sizeof(hg_log_iterator));
>>>>> +       log_iterator->handle = handle;
>>>>> +
>>>>> +       log_iterator->command = cmdbuilder("log", option, CHANGESET);
>>>>> +
>>>>> +       if(hg_rawcommand(handle, log_iterator->command) < 0){
>>>>> +               return NULL;
>>>>> +       }
>>>>> +
>>>>> +       log_iterator->changeset = malloc(0);
>>>>> +       return log_iterator;
>>>>>
>>>>
>>>>
>>>> What happens here is that hg_log() does not return a status code
>>>> (with the meaning "command successfully sent"), but the hg_log_iterator
>>>> struct instead.
>>>>
>>>> A status code would be desirable and idiomatic I think;
>>>> the struct to be passed as an argument.
>>>>
>>>>
>>>>
>>>>> +}
>>>>> +
>>>>> +/* The iterator next step. Getting the next changeset. */
>>>>> +int hg_fetch_log_entry(hg_log_iterator *log_iterator, hg_log_entry_t
>>>>> *le)
>>>>> +{
>>>>> +       hg_header head = hg_head(log_iterator->handle);
>>>>> +
>>>>> +       if(head.channel == 'r'){
>>>>> +               free(log_iterator->command);
>>>>> +               free(log_iterator->changeset);
>>>>> +               int exitcode = hg_exitcode(log_iterator->handle);
>>>>> +               free(log_iterator);
>>>>> +               return exitcode;
>>>>> +       }
>>>>> +
>>>>> +       log_iterator->changeset = realloc(log_iterator->changeset,
>>>>> head.length);
>>>>> +
>>>>> +       if(read(log_iterator->handle->p_read, log_iterator->changeset,
>>>>> +                                       head.length) < 0){
>>>>>
>>>>
>>>>
>>>> Here you are not using hg_rawread(). Please explain why.
>>>> Level 1 functions are meant to build on top of level 0 functions.
>>>>
>>>> Also: you are not reading in 4096 B chunks.
>>>>
>>>> Also: you are assuming that all information for a given log entry
>>>> (a revision) are retrieved in a "single shot" of the  'o' channel.
>>>>
>>>> You are assuming that a log revision cannot be split across two
>>>> iteration of output on the 'o' channel.
>>>>
>>>> Is this assumption safe?
>>>>
>>>
>>> No.
>>>
>>
>> Your answer is not clear... You want to say that the server will
>> give a revision in more "shots"? From my test I saw that the cmdserver
>> will send a log entry in a single call ( when I am using a template)
>> even if that revision have 20 kB.
>> The header from cmdserver is something like channel = 'o' length = 20000
>> data = [...]
>>
>
> The command server is oblivious to the data it writes. It simply forwards
> whatever hg normally writes to stdout, but in the command protocol.
> So nothing guarantees what you're seeing, even though that's what happens
> in practice.
>
>
>> Or the operation of getting the entire data in one call, is the bad thing?
>>
>
> What data is that? All the output generated by 'hg log ..'? Or the data
> for one
> log entry?
>

By data I mean a log entry in a call... What I understand from your
python-hglib
implementation, on the hg log command you are doing something similar. In
 a
read call from command server you will receive a changeset(a log entry).
Something like this I am trying to make, getting a changeset in a single
call.


>
>
>>
>> Also: If there will be more iterations for a revision, How could I know
>> how
>> much iterations there are?
>>
>
> Your template defines how to parse the output data. The result channel
> tells you the current command ended.
>

What I understand from this is that, if you are using such kind of template
the command server will pass a changeset in a single call.

What I am trying to ask is what happens if the command server will
send a changeset in 2 calls, or more like if the hg could perform this
kind of operation?
Martin Geisler - Aug. 19, 2013, 9:51 p.m.
Iulian Stana <julian.stana@gmail.com> writes:

> 2013/8/19 Idan Kamara <idankk86@gmail.com>
>
>> On Mon, Aug 19, 2013 at 3:10 PM, Giovanni Gherdovich <
>> g.gherdovich@gmail.com> wrote:
>>
>>>
>>>
>>> 2013/8/18 Iulian Stana <julian.stana@gmail.com>
>>>
>>>> # HG changeset patch
>>>> # User Iulian Stana <julian.stana@gmail.com>
>>>> # Date 1376828454 -10800
>>>> #      Sun Aug 18 15:20:54 2013 +0300
>>>> # Node ID 04bf311c99b8f4cf4c3e2096b493ee126f861b99
>>>> # Parent  21be7b973803e02b0bb310465691f88705a2dd53
>>>> c-hglib: hg_log() level 1 function
>>>>

[...]

Please remember to quote only the relevant part of your messages.

>>>> +/* The iterator next step. Getting the next changeset. */
>>>> +int hg_fetch_log_entry(hg_log_iterator *log_iterator, hg_log_entry_t
>>>> *le)
>>>> +{
>>>> +       hg_header head = hg_head(log_iterator->handle);
>>>> +
>>>> +       if(head.channel == 'r'){
>>>> +               free(log_iterator->command);
>>>> +               free(log_iterator->changeset);
>>>> +               int exitcode = hg_exitcode(log_iterator->handle);
>>>> +               free(log_iterator);
>>>> +               return exitcode;
>>>> +       }
>>>> +
>>>> +       log_iterator->changeset = realloc(log_iterator->changeset,
>>>> head.length);
>>>> +
>>>> +       if(read(log_iterator->handle->p_read, log_iterator->changeset,
>>>> +                                       head.length) < 0){
>>>>
>>>
>>>
>>> Here you are not using hg_rawread(). Please explain why.
>>> Level 1 functions are meant to build on top of level 0 functions.
>>>
>>> Also: you are not reading in 4096 B chunks.
>>>
>>> Also: you are assuming that all information for a given log entry
>>> (a revision) are retrieved in a "single shot" of the  'o' channel.
>>>
>>> You are assuming that a log revision cannot be split across two
>>> iteration of output on the 'o' channel.
>>>
>>> Is this assumption safe?
>>>
>>
>> No.
>>
>
> Your answer is not clear... You want to say that the server will
> give a revision in more "shots"? From my test I saw that the cmdserver
> will send a log entry in a single call ( when I am using a template)
> even if that revision have 20 kB.

The example here shows pretty well what the command server output may
look like:

  http://mercurial.selenic.com/wiki/CommandServer#runcommand-1

That is, 'hg log' output for a single revision may be broken up into
several writes on the 'o' channel.

> The header from cmdserver is something like channel = 'o' length =
> 20000 data = [...]

The point is that this is not guaranteed by the command server.

Think of the command server as you would think of a normal child process
that you've started using, say, the subprocess in Python. When you run

  proc = subprocess.Popen(['hg, 'log'], stdout=subprocess.PIPE)

then you cannot expect that

  proc.stdout.read(1024)

will give you data back in chunks that correspond to the stdout.write
calls done in 'hg log' -- the boundaries between the write calls
disappear. This is the same situation as when working with TCP sockets:
a client cannot "see" the chunks the server used when writing the reply
to the socket. All the client sees is a stream of data.

> Or the operation of getting the entire data in one call, is the bad thing?

I think it was established that it is okay to return the data for a
single changeset as a single value -- the fields in a changeset are not
that big.

> Also: If there will be more iterations for a revision, How could I know how
> much iterations there are?

The channel specifies how data you should expect in each chunk. After
putting the chunks together, you have to parse them to figure out when
you're reached the end of the data.

For log output you probably use a custom template and so you could
include a marker that you can recognize. In JavaHg we used

  {node}{rev}\n{author}\n{date|hgdate}\n{branch}\n{parents}{desc}\0

as the template and then parsed the output like this:

  https://bitbucket.org/aragost/javahg/src/2b111ee65545/src/main/java/com/aragost/javahg/Changeset.java?at=default#cl-108

We decided that a high-level client would get a ArrayList back with all
the parsed changesets and made the internal iterator API private. One
could also have exposed the iterator API instead.
Iulian Stana - Aug. 19, 2013, 11:05 p.m.
I don't really know if this is a problem or not, but I think that users
could have all kinds of settings in there configuration files, and
the changesets could be received different from user to user in case
of using the 'hg log' command without any template.

Meanwhile I've made some examples, using a template. From
those examples the single notable thing is that the command server
is sending a changeset in a call. (channel, data_length, changeset
fit the template). Probably, the concern thing is that somehow in
the message data, there will be just a part of the changeset.

I agree, that :


 This is the same situation as when working with TCP sockets:
> a client cannot "see" the chunks the server used when writing the reply
> to the socket. All the client sees is a stream of data.



But in our case the, our message has a header, that tells use the
length of data. Probably is not what you want to point.


Also: about putting chunks together and then parse, I think that is a
very costly process. If the data comes in fields I think that is better
to "parse"/"put directly in a place".



> We decided that a high-level client would get a ArraysList back with all
> the parsed changesets and made the internal iterator API private. One
> could also have exposed the iterator API instead.
>

It would be nice to have the changesets in an ArraysList, but the C language
is not providing a List implementation, yes I could simulate the list, but
there will be a problem with the length of data. If I passing the ArrayList
back to the user, this will mean that I must save in my memory the entire
amount of data(probably 500GB of data), or there is any other possibility of
doing this operation without saving the data in memory?
Martin Geisler - Aug. 20, 2013, 2:31 p.m.
Iulian Stana <julian.stana@gmail.com> writes:

Hi Iulian,

Thanks for quoting the reply! :)

> I don't really know if this is a problem or not, but I think that
> users could have all kinds of settings in there configuration files,
> and the changesets could be received different from user to user in
> case of using the 'hg log' command without any template.
>
> Meanwhile I've made some examples, using a template. From those
> examples the single notable thing is that the command server is
> sending a changeset in a call. (channel, data_length, changeset fit
> the template). Probably, the concern thing is that somehow in the
> message data, there will be just a part of the changeset.

I believe that the command server is allowed to send data back to you as

  'o', 32, 'changeset:   19527:f37b5a17e6a0\n'
  'o', 32, 'branch:      stable\ntag:        '
  'o', 32, ' 2.7\nuser:        Kevin Bullock '
  'o', 32, '<kbullock@ringworld.org>\ndate:  '
  'o', 32, '      Thu Aug 01 21:43:14 2013 -'
  'o', 32, '0500\nsummary:     bookmarks: pul'
  'o', 32, 'l --update updates to active boo'
  'o', 29, 'kmark if it moved (issue4007)'

if we somehow found out that it was more efficient to buffer data in a
32 byte buffer before writing it to stdout.

> I agree, that :
>
>> This is the same situation as when working with TCP sockets: a client
>> cannot "see" the chunks the server used when writing the reply to the
>> socket. All the client sees is a stream of data.
>
> But in our case the, our message has a header, that tells use the
> length of data. Probably is not what you want to point.

You're right that there is a header -- but that header only tells you
the size of the chunk being written on stdout. You don't generally know
what a "chunk" is. A chunk may be a line in 'hg log' output, but that's
not part of the specification of the command server.

> Also: about putting chunks together and then parse, I think that is a
> very costly process. If the data comes in fields I think that is
> better to "parse"/"put directly in a place".

You can only rely on this if it is specified somewhere. I don't know of
such a specification.

>> We decided that a high-level client would get a ArraysList back with
>> all the parsed changesets and made the internal iterator API private.
>> One could also have exposed the iterator API instead.
>
> It would be nice to have the changesets in an ArraysList, but the C
> language is not providing a List implementation, yes I could simulate
> the list, but there will be a problem with the length of data. If I
> passing the ArrayList back to the user, this will mean that I must
> save in my memory the entire amount of data(probably 500GB of data),
> or there is any other possibility of doing this operation without
> saving the data in memory?

We're just talking about the meta data you see when you call 'hg log',
not the file content itself. JavaHg wont let you use the --patch flag
for log so the output shouldn't be very big (if you want patches you're
supposed to call 'hg diff' later).

Patch

diff --git a/client.c b/client.c
new file mode 100644
--- /dev/null
+++ b/client.c
@@ -0,0 +1,134 @@ 
+#define _GNU_SOURCE 
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+
+#include "client.h"
+#include "utils.h"
+
+#define HGPATH "hg"
+#define CHANGESET "{rev}\\0{node}\\0{tags}\\0{branch}\\0{author}\\0{desc}\
+						\\0{date|isodate}\\0"
+
+/**
+ * \brief A helper for building the command arguments.
+ * \param command The name for mercurial command.
+ * \param options An array pointer to command option list.
+ * \param template Template to be used for this command.
+ * \retval new_string An array of pointers, where the command it's almost ready
+ *                    to be send to mercurial cmdserver. 
+ * */
+char **cmdbuilder(char *command, char *options[], char *template)
+{
+	int cmd_size, size;
+	char **new_cmd;
+	char *temp = "--template";
+
+	for(size = 0; options[size] != NULL; size++);
+	cmd_size = size + 1;
+	new_cmd = malloc(sizeof(char *) * (cmd_size + 3));
+	
+	new_cmd[0] = command;
+	memcpy(new_cmd + 1, options, (cmd_size - 1)  * sizeof(char *));	
+	if(template){
+		new_cmd[cmd_size] = temp;
+		new_cmd[cmd_size + 1] = template;
+		new_cmd[cmd_size + 2] = NULL;
+	}else{
+		new_cmd[cmd_size] = NULL;
+	}
+
+	return new_cmd;
+}
+
+/**
+ * \brief 'Parse a changeset'. It's more like pointing to the correct position.
+ *
+ * The changeset could be found on buff pointer. To not duplicate the data I 
+ * choose to point every log_entry field to the right position.
+ * \param buff The pointer to the changeset.
+ * \param le   The log_entry_t structure where the changeset will be parse.
+ * \retval 0 if successful.
+ * */
+int parse_changeset(char *buff, hg_log_entry_t *le)
+{
+	/* set pointer for revision position */
+	le->rev = buff;
+
+	/* set pointer for node position */
+	buff = buff + strlen(buff) + 1;
+	le->node = buff;
+
+	/* set pointer for tag position */
+	buff = buff + strlen(buff) + 1;
+	le->tags = buff;
+
+	/* set pointer for branch position */
+	buff = buff + strlen(buff) + 1;
+	le->branch = buff;
+
+	/* set pointer for author position */
+	buff = buff + strlen(buff) + 1;
+	le->author = buff;
+
+	/* set pointer for description position */
+	buff = buff + strlen(buff) + 1;
+	le->desc = buff;
+
+	/* set pointer for data position */
+	buff = buff + strlen(buff) + 1;
+	le->date = buff;
+
+	return 0;
+}
+
+/* The high level log command for hglib API. */
+hg_log_iterator *hg_log(hg_handle *handle, char *option[])
+{
+	hg_log_iterator *log_iterator = malloc(sizeof(hg_log_iterator));
+	log_iterator->handle = handle;
+
+	log_iterator->command = cmdbuilder("log", option, CHANGESET);
+
+	if(hg_rawcommand(handle, log_iterator->command) < 0){
+		return NULL;
+	}
+
+	log_iterator->changeset = malloc(0);
+	return log_iterator;
+}
+
+/* The iterator next step. Getting the next changeset. */
+int hg_fetch_log_entry(hg_log_iterator *log_iterator, hg_log_entry_t *le)
+{
+	hg_header head = hg_head(log_iterator->handle); 
+
+	if(head.channel == 'r'){
+		free(log_iterator->command);
+		free(log_iterator->changeset);
+		int exitcode = hg_exitcode(log_iterator->handle);
+		free(log_iterator);
+		return exitcode;
+	}
+
+	log_iterator->changeset = realloc(log_iterator->changeset, head.length);
+
+	if(read(log_iterator->handle->p_read, log_iterator->changeset,
+					head.length) < 0){
+		return -1;
+	}
+
+	parse_changeset(log_iterator->changeset, le);
+
+	if(read_header(log_iterator->handle) < 0){
+		return -1;
+	}
+	return head.length;
+}
diff --git a/client.h b/client.h
--- a/client.h
+++ b/client.h
@@ -65,6 +65,22 @@ 
 	int protect;
 } hg_handle;
 
+typedef struct hg_log_iterator{
+	hg_handle *handle;
+	char **command;
+	char *changeset;
+}hg_log_iterator;
+
+typedef struct hg_log_entry_t{
+	char *author; 
+	char *branch; 
+	char *date;
+	char *desc;
+	char *node;
+	char *rev;
+	char *tags;
+}hg_log_entry_t;
+
 /**
  * \brief Open the connection with the mercurial command server.
  *
@@ -196,6 +212,63 @@ 
  * */
 int hg_exitcode(hg_handle *handle);
 
+/**
+ * \brief hg_log command for hglib API.
+ *
+ * It's an advance function to get revision history. It's more like the start 
+ * point of the action, this function will prepare the query question and will 
+ * send it to the cmd-server.
+ *
+ * Return the revision history of the specified files or the entire project.
+ * File history is shown without following rename or copy history of files.
+ * Use follow with a filename to follow history across renames and copies.
+ * follow without a filename will only show ancestors or descendants of the
+ * starting revision. followfirst only follows the first parent of merge
+ * revisions.
+ *
+ * If revrange isn't specified, the default is "tip:0" unless follow is set,
+ * in which case the working directory parent is used as the starting
+ * revision.
+ *
+ * \param handle The handle of the connection, wherewith I want to communicate
+ * \param option The option list for mercurial log command.
+ * \retval hg_log_iterator A pointer to hg_log_iterator structure if successful
+ * \retval NULL to indicate an error, with errno set appropriately.
+ *
+ * errno can be:
+ *      - hg_rawcommand errors
+ * */
+hg_log_iterator *hg_log(hg_handle *handle, char *option[]);
+
+/**
+ * \brief The iterator step. Getting the next changeset.
+ *
+ * The revision history could have a huge mass of data. You can pass the entire 
+ * history in one call, so we use an iterator-like mechanism. Calling the 
+ * hg_fetch_log_entry, the next changeset will be read from cmd-server, parse
+ * and pass to hg_log_entry_t structure.
+ * The log_entry structure will handle a changeset with the following string 
+ * fields:
+ *         - rev
+ *         - node
+ *         - tags (space delimited)
+ *         - branch
+ *         - author
+ *         - desc
+ *
+ * \param log_iterator The iterator for log command.
+ * \param log_entry The hg_log_entry_t structure where the changeset will be 
+ *                   pass
+ * \retval number The lenght for the pass changeset.
+ * \retval exitcode To indicate the end of log_command.
+ * \retval   -1 to indicate an error, with errno set appropriately.
+ *
+ * errno can be:
+ *      - EINVAL  - Invalid argument (handle it's set to a null pointer)
+ *      - read(2) command errors
+ *      - read_header error
+ * */
+int hg_fetch_log_entry(hg_log_iterator *log_iterator, hg_log_entry_t *le);
 
 #endif
 
diff --git a/main.c b/main.c
new file mode 100644
--- /dev/null
+++ b/main.c
@@ -0,0 +1,129 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "client.h"
+#include "utils.h"
+
+#define INIT_REPO  "init_test_repo"
+
+/****** Convenience functions. *******/
+
+/** 
+ * \brief Create and setup the tmp directory where the acction will happends.
+ * */
+void setup_tmp()
+{
+	system("hg init tmp");
+	chdir("tmp");
+}
+
+/**
+ * \brief Remove the tmp directory and all his files.
+ * */
+void clean_tmp()
+{
+	chdir("..");
+	system("rm -rf tmp");
+}
+
+/** 
+ * \brief Fill the current repository with commits for log command. 
+ * */
+void setup_log()
+{
+	system("touch foo ; hg add foo ; hg commit -m 'foo file'");
+	system("echo baloo > foo ; hg commit -m 'baloo text'");
+	system("touch voodoo ; hg add voodoo ; hg commit -m voodoo");
+	system("echo voodoo > voodoo ; hg commit -m 'voodoo text'");
+}
+
+/******* Examples using level 1 implementations. ******/
+
+/**
+ * \brief Log command example.
+ *
+ * \param handle The handle of the connection, wherewith I want to communicate
+ * \retval exitcode
+ * */
+int hg_log_example(hg_handle *handle)
+{
+	char *option[] = {"-v", NULL};
+	int nc;
+
+	/* hg_log function will a iterator. */
+	hg_log_iterator *log_iterator = hg_log(handle, option);
+
+	/* you need to alloc some space for log_entry_t structure */
+	hg_log_entry_t *le = malloc(sizeof(hg_log_entry_t));
+
+	/* Getting the next changeset using the iterator-like mechanism. 
+	   Print the changest from log_entry structure.*/
+	while(nc = hg_fetch_log_entry(log_iterator, le), nc > 0){
+		printf("rev = %s \n", le->rev);
+		printf("node = %s \n", le->node);
+		printf("tags = %s \n", le->tags);
+		printf("branch = %s \n", le->branch);
+		printf("author = %s \n", le->author);
+		printf("desc = %s \n", le->desc);
+		printf("date = %s \n", le->date);
+		printf("\n");
+	}
+
+	free(le);
+	/* last call for hg_fetch_log_entry will pass the exitcode */
+	return nc;
+}
+
+/** \brief Printing the welcome message.
+ * 
+ * Will print the options that you will have in this example.
+ **/
+void print_select_case()
+{
+	printf("Select test case to run:\n");
+	printf("1) log \n");
+	printf("\n");
+	printf("Your choice: ");
+}
+
+
+/***** Main function. *******/
+/**
+ * \brief The main function
+ * */
+int main(int argc, char **argv)
+{
+	int select_case;
+	hg_handle *handle;
+
+	print_select_case();
+	scanf("%d", &select_case);
+	if(select_case < 1 || select_case > 1){
+		printf("Your choice is not an option...\n");
+		return -1;
+	}
+
+	switch(select_case){
+		case 1:
+			setup_tmp();
+			setup_log();
+			handle = hg_open(NULL, "");
+
+			hg_log_example(handle);
+
+			hg_close(&handle);
+			clean_tmp();
+			break;
+		default:
+			break;
+	}
+
+	return 0;
+}