Hello community,

here is the log from the commit of package libx86emu for openSUSE:Factory 
checked in at 2018-05-01 23:04:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libx86emu (Old)
 and      /work/SRC/openSUSE:Factory/.libx86emu.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libx86emu"

Tue May  1 23:04:38 2018 rev:21 rq:601893 version:1.14

Changes:
--------
--- /work/SRC/openSUSE:Factory/libx86emu/libx86emu.changes      2017-09-13 
21:37:19.447193303 +0200
+++ /work/SRC/openSUSE:Factory/.libx86emu.new/libx86emu.changes 2018-05-01 
23:04:42.473779409 +0200
@@ -1,0 +2,16 @@
+Fri Apr 27 12:22:24 UTC 2018 - [email protected]
+
+- merge gh#wfeldt/libx86emu#11
+- enhance documentation
+- update git2log script
+- add better description
+- 1.14
+
+--------------------------------------------------------------------
+Fri Apr 27 11:49:32 UTC 2018 - [email protected]
+
+- merge gh#wfeldt/libx86emu#7
+- add a small demo program
+- 1.13
+
+--------------------------------------------------------------------

Old:
----
  libx86emu-1.12.tar.xz

New:
----
  libx86emu-1.14.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libx86emu.spec ++++++
--- /var/tmp/diff_new_pack.KssbWJ/_old  2018-05-01 23:04:43.277750205 +0200
+++ /var/tmp/diff_new_pack.KssbWJ/_new  2018-05-01 23:04:43.277750205 +0200
@@ -24,7 +24,7 @@
 Summary:        A small x86 emulation library.
 License:        BSD-3-Clause
 Group:          System/Libraries
-Version:        1.12
+Version:        1.14
 Release:        0
 PreReq:         /sbin/ldconfig
 Source:         %{name}-%{version}.tar.xz

++++++ libx86emu-1.12.tar.xz -> libx86emu-1.14.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libx86emu-1.12/Makefile new/libx86emu-1.14/Makefile
--- old/libx86emu-1.12/Makefile 2017-09-12 13:49:46.000000000 +0200
+++ new/libx86emu-1.14/Makefile 2018-04-27 14:22:24.000000000 +0200
@@ -26,7 +26,7 @@
 LIB_NAME       = $(LIBX86).so.$(VERSION)
 LIB_SONAME     = $(LIBX86).so.$(MAJOR_VERSION)
 
-.PHONY: all shared install test clean
+.PHONY: all shared install test demo clean
 
 %.o: %.c
        $(CC) -c $(CFLAGS) $<
@@ -50,6 +50,9 @@
 test:
        make -C test
 
+demo:
+       make -C demo
+
 archive: changelog
        @if [ ! -d .git ] ; then echo no git repo ; false ; fi
        mkdir -p package
@@ -59,6 +62,7 @@
 
 clean:
        make -C test clean
+       make -C demo clean
        rm -f *.o *~ include/*~ *.so.* .depend
        rm -rf package
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libx86emu-1.12/README.md new/libx86emu-1.14/README.md
--- old/libx86emu-1.12/README.md        2017-09-12 13:49:46.000000000 +0200
+++ new/libx86emu-1.14/README.md        2018-04-27 14:22:24.000000000 +0200
@@ -1,6 +1,32 @@
 # x86 emulation library
 
-libx86emu is a dependency for [hwinfo](https://github.com/openSUSE/hwinfo).
+libx86emu is a small library to emulate x86 instructions. The focus here is 
not a complete emulation (go for qemu for this) but to cover enough for typical 
firmware blobs.
+
+At the moment 'regular' 32-bit instructions are covered together with basic 
protected mode support.
+
+Not done are fpu, mmx, or any of the other instruction set extensions.
+
+The library lets you
+
+  - intercept any memory access or directly map real memory ranges
+  - intercept any i/o access, map real i/o ports, or block any real i/o
+  - intercept any interrupt
+  - provides hook to run after each instruction
+  - recognizes a special x86 instruction that can trigger logging
+  - has integrated logging to
+    - trace code execution, including register content and decoded instruction
+    - trace memory and i/o accesses
+    - provide statistics about accessed memory locations, i/o ports, and 
interrupts
+
+## Downloads
+
+Get the latest version from the [openSUSE Build 
Service](https://software.opensuse.org/package/libx86emu).
+
+## Examples
+
+Have a look at this minimalistic [demo](demo/x86emu-demo.c) program.
+
+The library is used by [hwinfo](https://github.com/openSUSE/hwinfo) to emulate 
Video BIOS (VBE) calls.
 
 ## API functions
 
@@ -298,10 +324,26 @@
 
 ## openSUSE Development
 
-The package is automatically submitted from the `master` branch to
-[system:install:head](https://build.opensuse.org/package/show/system:install:head/libx86emu)
-OBS project. From that place it is forwarded to
-[openSUSE Factory](https://build.opensuse.org/project/show/openSUSE:Factory).
+To build, simply run `make`. Install with `make install`.
+
+Basically every new commit into the master branch of the repository will be 
auto-submitted
+to all current SUSE products. No further action is needed except accepting the 
pull request.
+
+Submissions are managed by a SUSE internal [jenkins](https://jenkins.io) node 
in the InstallTools tab.
+
+Each time a new commit is integrated into the master branch of the repository,
+a new submit request is created to the openSUSE Build Service. The devel 
project
+is 
[system:install:head](https://build.opensuse.org/package/show/system:install:head/libx86emu).
+
+`*.changes` and version numbers are auto-generated from git commits, you don't 
have to worry about this.
+
+The spec file is maintained in the Build Service only. If you need to change 
it for the `master` branch,
+submit to the
+[devel 
project](https://build.opensuse.org/package/show/system:install:head/libx86emu)
+in the build service directly.
+
+Development happens exclusively in the `master` branch. The branch is used for 
all current products.
 
-You can find more information about this workflow in the [linuxrc-devtools
+You can find more information about the changes auto-generation and the
+tools used for jenkis submissions in the [linuxrc-devtools
 
documentation](https://github.com/openSUSE/linuxrc-devtools#opensuse-development).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libx86emu-1.12/VERSION new/libx86emu-1.14/VERSION
--- old/libx86emu-1.12/VERSION  2017-09-12 13:49:46.000000000 +0200
+++ new/libx86emu-1.14/VERSION  2018-04-27 14:22:24.000000000 +0200
@@ -1 +1 @@
-1.12
+1.14
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libx86emu-1.12/changelog new/libx86emu-1.14/changelog
--- old/libx86emu-1.12/changelog        2017-09-12 13:49:46.000000000 +0200
+++ new/libx86emu-1.14/changelog        2018-04-27 14:22:24.000000000 +0200
@@ -1,60 +1,78 @@
+2018-04-27:    1.14
+       - merge gh#wfeldt/libx86emu#11
+       - enhance documentation
+       - update git2log script
+       - add better description
+
+2018-04-27:    1.13
+       - merge gh#wfeldt/libx86emu#7
+       - add a small demo program
+
 2017-09-12:    1.12
-       - add proper LICENSE file (containing the actual license) and add it 
consistently to all source files
+       - merge gh#wfeldt/libx86emu#9
+       - add proper LICENSE file (containing the actual license) and add
+         it consistently to all source files
 
-2016-04-11:    1.11
-       - Add tests for retn/retf instructions
+2016-06-22:    1.11
+       - merge gh#wfeldt/libx86emu#5
        - Small compatibility fixes for msvc compiler
+       - merge gh#wfeldt/libx86emu#6
+       - Add tests for retn/retf instructions
 
 2016-04-05:    1.10
+       - merge gh#wfeldt/libx86emu#4
+       - rewrite rol/ror implementation to be more readable and to fix
+         overflow flag setting
        - add tests for rol/ror instructions
-       - rewrite rol/ror implementation to be more readable and to fix 
overflow flag setting
 
-2016-03-16:    1.9
-       - build on more archs
+2016-04-05:    1.9
+       - merge branch master
 
-2016-02-22:    1.8
-       - Fixed some small bugs
+2016-04-05:    1.8
+       - merge gh#wfeldt/libx86emu#2
        - Fixed "RET imm" instructions
+       - Fixed some small bugs
 
 2016-03-16:    1.7
        - build on more archs
 
-2015-10-01:    1.6
-       - Add what's libx86emu useful for
-       - Add documentation about how the project is sent to the distribution
+2015-10-13:    1.6
+       - merge gh#wfeldt/libx86emu#1
        - Convert README to Markdown
+       - Add documentation about how the project is sent to the distribution
+       - Add what's libx86emu useful for
 
 2015-03-02:    1.5
-       - fix typo in Makefile
-       - fix some compiler warnings
-       - prepare for obs auto-submission
        - add VERSION file back
+       - prepare for obs auto-submission
+       - fix some compiler warnings
+       - fix typo in Makefile
 
 2013-02-28:    1.4
        - avoid using reserved c++ words
 
 2011-01-26:    1.3
-       - better invalid code detection
        - fix instruction log for some instructions
+       - better invalid code detection
 
 2010-09-23:    1.2
-       - more Makefile fixes
-       - fix Makefile
-       - remove debug line
-       - create VERSION and changelog from git repo
-       - log message
-       - don't use 'i386'
        - properly decode all descriptor types
+       - don't use 'i386'
+       - log message
+       - create VERSION and changelog from git repo
+       - remove debug line
+       - fix Makefile
+       - more Makefile fixes
 
 2009-06-09:    1.1
-       - export only API functions in shared lib
-       - new mem interface
        - fix build on non-x86 arch
+       - new mem interface
+       - export only API functions in shared lib
 
 2008-12-12:    0.1
-       - basic msr support
-       - inw fix
-       - log tsc
-       - lowercase, no tabs
        - move to lowercase
+       - lowercase, no tabs
+       - log tsc
+       - inw fix
+       - basic msr support
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libx86emu-1.12/demo/Makefile 
new/libx86emu-1.14/demo/Makefile
--- old/libx86emu-1.12/demo/Makefile    1970-01-01 01:00:00.000000000 +0100
+++ new/libx86emu-1.14/demo/Makefile    2018-04-27 14:22:24.000000000 +0200
@@ -0,0 +1,10 @@
+CC         = gcc
+CFLAGS     = -g -Wall -fomit-frame-pointer -O2
+
+.PHONY: clean
+
+x86emu-demo: x86emu-demo.c
+       $(CC) $(CFLAGS) $< -lx86emu -o $@
+
+clean:
+       rm -f *~ *.o x86emu-demo
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libx86emu-1.12/demo/x86emu-demo.c 
new/libx86emu-1.14/demo/x86emu-demo.c
--- old/libx86emu-1.12/demo/x86emu-demo.c       1970-01-01 01:00:00.000000000 
+0100
+++ new/libx86emu-1.14/demo/x86emu-demo.c       2018-04-27 14:22:24.000000000 
+0200
@@ -0,0 +1,200 @@
+/*
+ * This is a simple program demonstrating the libx86emu usage.
+ *
+ * It lets you load a binary blob at some address and run the emulation.The
+ * emulation trace is logged to the console to show what it is doing.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <x86emu.h>
+
+void help(void);
+void flush_log(x86emu_t *emu, char *buf, unsigned size);
+x86emu_t *emu_new(void);
+int emu_init(x86emu_t *emu, char *file);
+void emu_run(char *file);
+
+
+struct option options[] = {
+  { "help",       0, NULL, 'h'  },
+  { "load",       1, NULL, 'l'  },
+  { "start",      1, NULL, 's'  },
+  { "max",        1, NULL, 'm'  },
+  { }
+};
+
+
+struct {
+  struct {
+    unsigned segment;
+    unsigned offset;
+  } start;
+  unsigned load;
+  unsigned max_instructions;
+  char *file;
+} opt;
+
+
+/*
+ * Parse options, then run emulation.
+ */
+int main(int argc, char **argv)
+{
+  int i;
+  char *str;
+
+  opt.start.segment = 0;
+  opt.start.offset = opt.load = 0x7c00;
+  opt.max_instructions = 10000000;
+
+  opterr = 0;
+
+  while((i = getopt_long(argc, argv, "hm:l:s:", options, NULL)) != -1) {
+    switch(i) {
+      case 'm':
+        opt.max_instructions = strtoul(optarg, NULL, 0);
+        break;
+
+      case 'l':
+        opt.load = strtoul(optarg, NULL, 0);
+        break;
+
+      case 's':
+        opt.start.offset = strtoul(optarg, &str, 0);
+        if(*str == ':') {
+          opt.start.segment = opt.start.offset;
+          opt.start.offset = strtoul(str + 1, NULL, 0);
+        }
+        break;
+
+      default:
+        help();
+        return i == 'h' ? 0 : 1;
+    }
+  }
+
+  if(argc == optind + 1) {
+    opt.file = argv[optind];
+  }
+  else {
+    help();
+    return 1;
+  }
+
+  emu_run(opt.file);
+
+  return 0;
+}
+
+
+/*
+ * Display short usage message.
+ */
+void help()
+{
+  printf(
+    "Usage: x86emu-demo [OPTIONS] FILE\n"
+    "\n"
+    "Load FILE and run x86 emulation.\n"
+   "\n"
+    "Options:\n"
+    "  -l, --load ADDRESS\n"
+    "      load FILE at ADDRESS into memory (default: 0x7c00).\n"
+    "  -s, --start ADDRESS\n"
+    "      start emulation at ADDRESS (default 0:0x7c00).\n"
+    "      Note: ADDRESS may contain a colon (':') to separate segment and 
offset values;\n"
+    "      if not, segment = 0 is assumed.\n"
+    "  -m, --max N\n"
+    "      stop after emulating N instructions.\n"
+    "  -h, --help\n"
+    "      show this text\n"
+  );
+}
+
+
+/*
+ * Write emulation log to console.
+ */
+void flush_log(x86emu_t *emu, char *buf, unsigned size)
+{
+  if(!buf || !size) return;
+
+  fwrite(buf, size, 1, stdout);
+}
+
+
+/*
+ * Create new emulation object.
+ */
+x86emu_t *emu_new()
+{
+  x86emu_t *emu = x86emu_new(X86EMU_PERM_R | X86EMU_PERM_W | X86EMU_PERM_X, 0);
+
+  /* log buf size of 1000000 is purely arbitrary */
+  x86emu_set_log(emu, 1000000, flush_log);
+
+  emu->log.trace = X86EMU_TRACE_DEFAULT;
+
+  return emu;
+}
+
+
+/*
+ * Setup registers and memory.
+ */
+int emu_init(x86emu_t *emu, char *file)
+{
+  FILE *f;
+  unsigned addr;
+  int i;
+
+  addr = opt.load;
+
+  x86emu_set_seg_register(emu, emu->x86.R_CS_SEL, opt.start.segment);
+  emu->x86.R_EIP = opt.start.offset;
+
+  if(!(f = fopen(file, "r"))) return 0;
+
+  while((i = fgetc(f)) != EOF) {
+    x86emu_write_byte(emu, addr++, i);
+  }
+
+  fclose(f);
+
+  return 1;
+}
+
+
+/*
+ * Run emulation.
+ */
+void emu_run(char *file)
+{
+  x86emu_t *emu = emu_new();
+  unsigned flags = X86EMU_RUN_MAX_INSTR | X86EMU_RUN_NO_EXEC | 
X86EMU_RUN_NO_CODE | X86EMU_RUN_LOOP;
+  int ok = 0;
+
+  if(!file) return;
+
+  ok = emu_init(emu, file);
+
+  if(ok) {
+    printf("***  running %s  ***\n\n", file);
+
+    x86emu_dump(emu, X86EMU_DUMP_MEM | X86EMU_DUMP_ASCII);
+
+    x86emu_reset_access_stats(emu);
+
+    emu->max_instr = opt.max_instructions;
+    x86emu_run(emu, flags);
+
+    x86emu_dump(emu, X86EMU_DUMP_DEFAULT | X86EMU_DUMP_ACC_MEM);
+
+    x86emu_clear_log(emu, 1);
+  }
+
+  x86emu_done(emu);
+}
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libx86emu-1.12/git2log new/libx86emu-1.14/git2log
--- old/libx86emu-1.12/git2log  2017-09-12 13:49:46.000000000 +0200
+++ new/libx86emu-1.14/git2log  2018-04-27 14:22:24.000000000 +0200
@@ -2,14 +2,13 @@
 
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 #
-# This script is maintained at https://github.com/openSUSE/ssob
+# This script is maintained at https://github.com/openSUSE/linuxrc-devtools
 #
 # If you're in another project, this is just a copy.
 # You may update it to the latest version from time to time...
 #
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
-
 use strict;
 
 use Getopt::Long;
@@ -20,289 +19,927 @@
 $Data::Dumper::Indent = 1;
 
 sub usage;
-sub get_branch_tags;
-sub get_branch;
-sub get_parent_branch;
+sub changelog_outdated;
+sub get_github_project;
 sub get_version;
+sub get_tags;
+sub get_log;
+sub is_formatted_tag;
+sub get_branch;
+sub choose_tags;
+sub add_head_tag;
+sub tags_to_str;
+sub format_log;
+sub format_all_logs;
+sub fix_dates;
+sub add_line_breaks;
+sub format_date_obs;
+sub format_date_iso;
+sub raw_date_to_s;
 
 usage 0 if !@ARGV;
 
-my @deps = qw ( .git/HEAD .git/refs/heads .git/refs/tags );
+my @changelog_deps = qw ( .git/HEAD .git/refs/heads .git/refs/tags );
 
 my $branch;
 my $current_version;
 my @tags;
 my @all_tags;
+my $config;
 
 my $opt_log;
 my $opt_version;
 my $opt_branch;
 my $opt_update;
 my $opt_file;
+my $opt_start;
+my $opt_max;
+my $opt_width = 66;
+my $opt_width_fuzz = 8;
+my $opt_sep_width = 68;
+my $opt_format = 'internal';           # obs, internal
+my $opt_merge_msg_before = 1;          # log auto generated pr merge message 
before the commit messages (vs. after)
+my $opt_join_author = 1;               # join consecutive commit messages as 
long as they are by the same author
+my $opt_keep_date = 1;                 # don't join consecutive commit 
messages if they have different time stamps
+my $opt_default_email = '[email protected]';     # default email 
to use in changelog
 
 GetOptions(
   'help'          => sub { usage 0 },
   'version'       => \$opt_version,
   'branch'        => \$opt_branch,
   'update'        => \$opt_update,
+  'start=s'       => \$opt_start,
+  'format=s'      => \$opt_format,
+  'max=i'         => \$opt_max,
+  'width=i'       => \$opt_width,
+  'fuzz=i'        => \$opt_width_fuzz,
+  'merge-msg=s'   => sub { $opt_merge_msg_before = ($_[1] eq 'after' ? 0 : 1) 
},
+  'join-author!'  => \$opt_join_author,
+  'keep-date!'    => \$opt_keep_date,
   'log|changelog' => \$opt_log,
+  'default-email=s' => \$opt_default_email,
 ) || usage 1;
 
+# ensure we are used correctly
 usage 1 if @ARGV > 1 || !($opt_log || $opt_version || $opt_branch);
 $opt_file = @ARGV ? shift : '-';
 
 die "no git repo\n" unless -d ".git";
 
-if($opt_update && $opt_file ne '-' && -f($opt_file)) {
-  my $ok = 1;
+# if update option has been give write changelog only if git refs are newer
+exit 0 if $opt_update && $opt_file ne '-' && -f($opt_file) && 
!changelog_outdated($opt_file);
 
-  my $t = (stat $opt_file)[9];
+$opt_max = 2 if $opt_version || $opt_branch;
 
-  for (@deps) {
-    $ok = 0 if (stat)[9] > $t;
-  }
+# gather some data
+get_github_project;
+get_branch;
+get_log;
+fix_dates;
+get_tags;
+choose_tags;
+add_head_tag;
+get_version;
 
-  exit 0 if $ok;
-}
-
-@all_tags = `git tag`;
-chomp @all_tags;
+# just print current branch
+if($opt_branch) {
+  open my $f, ">$opt_file";
+  print $f $config->{branch} ? $config->{branch} : "master", "\n";
+  close $f;
 
-$branch = get_branch;
-die "no branch?\n" unless $branch;
+  exit 0;
+}
 
-@tags = get_branch_tags;
-die "no tags at all?\n" unless @tags;
+# just print current version
+if($opt_version) {
+  my $old_version;
 
-if($branch ne 'master') {
-  if(!grep { /^$branch\-/ } @tags) {
-    $branch = get_parent_branch;
-    die "sorry, can't determine branch\n" unless $branch;
+  if($opt_file ne '-' && open(my $f, $opt_file)) {
+    chomp($old_version = <$f>);
+    close $f;
+  }
 
-    @tags = get_branch_tags;
-    die "no tags at all?\n" unless @tags;
+  if($config->{version} ne $old_version) {
+    open my $f, ">$opt_file";
+    print $f "$config->{version}\n";
+    close $f;
   }
+
+  exit 0;
 }
-else {
-  @tags = get_branch_tags;
-  die "no tags at all?\n" unless @tags;
+
+# set start tag
+if($opt_start) {
+  my $x = is_formatted_tag $opt_start;
+  die "$opt_start: not a valid start tag\n" if !$x;
+  $x->{branch} = $config->{branch} if !$x->{branch};
+  $config->{start} = $x;
 }
 
-if($opt_branch) {
-  open my $f, ">$opt_file";
-  print $f "$branch\n";
-  close $f;
+format_all_logs;
 
-  exit 0;
+open my $f, ">$opt_file";
+
+print $f $_->{formatted} for @{$config->{log}};
+
+close $f;
+
+exit 0;
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# usage(exit_code)
+#
+# Print help message and exit.
+# - exit_code: exit code
+#
+# Function does not return.
+#
+sub usage
+{
+  my $err = shift;
+
+  print <<"  usage";
+Usage: git2log [OPTIONS] [FILE]
+Create changelog and project version from git repo.
+  --changelog         Write changelog to FILE.
+  --version           Write version number to FILE.
+  --branch            Write current branch to FILE.
+  --start START_TAG   Start with tag START_TAG.
+  --max N             Write at most MAX long entries.
+  --update            Write changelog or version only if FILE is outdated.
+  --format FORMAT     Write log using FORMAT. Supported FORMATs are 'internal' 
(default) and 'obs'.
+  --width WIDTH       Reformat log entries to be max WIDTH chars wide.
+  --fuzz FUZZ         Allow log lines to be up to FUZZ chars longer as WIDTH 
to avoid
+                      line breaks leaving tiny bits on the last line.
+  --merge-msg WHERE   Log message about merges before or after the actual 
merge commit messages.
+                      Valid values for WHERE are 'after' and 'before' 
(default).
+  --join-author       Join consecutive commits as long as they are by the same 
author. (default)
+  --no-join-author    Keep consecutive commits by the same author separate.
+  --keep-date         Join consecutive commits only if they have the same 
date. (default)
+  --no-keep-date      Join consecutive commits even if dates differ.
+  --default-email     Use this email in changelog entries if no other suitable 
email could be
+                      determined (default: opensuse-packaging\@opensuse.org).
+  --help              Print this help text.
+  usage
+
+  exit $err;
 }
 
-$current_version = get_version;
 
-if($opt_version) {
-  open my $f, ">$opt_file";
-  print $f "$current_version\n";
-  close $f;
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# res = changelog_outdated(file)
+#
+# Return status of changelog file.
+# - file: changelog file name
+# - res: status
+#     1: file is newer than the last git repo change and should be updated
+#     0: file is still recent enough
+#
+# Relies on global var @changelog_deps.
+#
+sub changelog_outdated
+{
+  my $file = $_[0];
 
-  exit 0;
+  my $changelog_time = (stat $file)[9];
+
+  return 1 if !defined $changelog_time;
+
+  for (@changelog_deps) {
+    return 1 if (stat)[9] > $changelog_time;
+  }
+
+  return 0;
 }
 
-if($branch ne 'master') {
-  my ($i1, $i2, $bi);
 
-  for (my $i = 0; $i < @tags; $i++) {
-    if($tags[$i] =~ /^$branch\-(\S+)/) {
-      $i2 = $i;
-      $bi = $1;
-      last;
-    }
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# get_github_project()
+#
+# Set $config->{github_project} to the github project name.
+#
+sub get_github_project
+{
+  if(`git config remote.origin.url` =~ m#github.com[:/]+(\S+/\S+)#) {
+    $config->{github_project} = $1;
+    $config->{github_project} =~ s/\.git$//;
   }
+}
 
-  # print STDERR ">> $branch-$bi\n";
 
-  warn "no tags in this branch yet\n" unless $bi;
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# get_version()
+#
+# Set $config->{branch} and $config->{version} to the current branch and
+# version info.
+#
+# This might be taken directly from HEAD if HEAD is tagged or otherwise be
+# exprapolated from the most recent tag (cf. add_head_tag()).
+#
+sub get_version
+{
+  $config->{version} = "0.0";
+
+  my $tag = $config->{log}[0]{tags}[0];
+
+  if($tag->{version}) {
+    $config->{version} = $tag->{version};
+    $config->{branch} = $tag->{branch};
+  }
+}
 
-  for (my $i = 0; $i < $i2; $i++) {
-    if($tags[$i] ge $bi) {
-      if($tags[$i] eq $bi) {
-        $i1 = $i;
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# get_tags()
+#
+# Parse $config->{raw_log}, extract tag names, and split into per-tag
+# sections.
+#
+# Only tags recognized by is_formatted_tag() are considered.
+#
+# The parsed logs is stored in $config->{log}, an array of log sections.
+# Each section is a hash with these keys:
+#   - 'tags': array of tags for this section
+#   - 'commit': git commit id associated with these tags
+#   - 'lines': git log lines
+#
+sub get_tags
+{
+  my $log_entry;
+
+  for (@{$config->{raw_log}}) {
+    if(/^commit (\S+)( \((.*)\))?/) {
+      my $commit = $1;
+      my $tag_list = $3;
+      my $xtag;
+
+      for my $t (split /, /, $tag_list) {
+        if($t =~ /tag: (\S+)/) {
+          my $tag = $1;
+          my $x = is_formatted_tag $tag;
+          push @$xtag, $x if $x;
+        }
+      }
+
+      if($xtag) {
+        if($log_entry) {
+          push @{$config->{log}}, $log_entry;
+          last if $opt_max && @{$config->{log}} >= $opt_max;
+        }
+        $log_entry = { commit => $commit, tags => $xtag };
       }
-      elsif($i > 0) {
-        $i1 = $i - 1;
+      else {
+        $log_entry = { commit => $commit } if !$log_entry;
       }
-      last;
     }
+
+    push @{$log_entry->{lines}}, $_ if $log_entry;
   }
+}
+
 
-  splice @tags, $i1, $i2 - $i1;
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# get_log()
+#
+# Read git log and store lines as array in $config->{raw_log} (trailing
+# newlines removed).
+#
+sub get_log
+{
+  chomp(@{$config->{raw_log}} = `git log --pretty=medium --date=raw 
--topo-order --decorate`);
 }
 
-map { s/(\d+)/$1 + 0/eg } @tags;
 
-push @tags, "HEAD";
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# hash_ref = is_formatted_tag(tag_name)
+#
+# Parse tag and return hash ref with branch and version number parts or
+# undef if it doesn't match.
+# - tag_name: tag as string
+# - hash_ref: hash ref with internal tag representation (with keys 'branch' 
and 'version').
+#
+# This expects tags of the form "VERSION" or "BRANCH-VERSION" where VERSION
+# consists of decimal numbers separated by dots '.' and BRANCH can be any
+# string.
+# (Note: it doesn't really have to be the name of an existing branch.)
+#
+# Tags not conforming to this convention are ignored.
+#
+sub is_formatted_tag
+{
+  if($_[0] =~ /^((.+)-)?((\d+\.)*\d+)$/) {
+    return { branch => $2, version => $3 }
+  }
 
-# print Dumper(\@tags);
+  return undef;
+}
 
-open F, ">$opt_file";
 
-for (my $i = @tags - 1; $i > 0; $i--) {
-  my ($date, @t2);
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# get_branch()
+#
+# Get currently active git branch and store in $config->{branch}.
+#
+# 'master' branch is represented by empty 'branch' key.
+#
+sub get_branch
+{
+  chomp(my $branch = `git rev-parse --abbrev-ref HEAD`);
 
-  my @t = `git log --pretty=medium --date=iso '$tags[$i-1]..$tags[$i]'`;
+  $branch = "" if $branch eq 'master';
 
-  # print "\n--- $tags[$i-1]..$tags[$i] ---\n", @t, "---\n";
+  $config->{branch} = $branch;
+}
 
-  my $merge = 0;
-  for (@t) {
-    $merge = 1 if /^Merge: /;
-    $merge = 0 if /^commit /;
-    push @t2, $_ if !$merge;
-  }
-  @t = @t2;
 
-  undef @t2;
-  my $detail = 0;
-  for (@t) {
-    $detail = 1 if /^    $/;
-    $detail = 2 if /^    Conflicts:$/;
-    $detail = 0 if /^commit /;
-    if(!$detail) {
-      push @t2, $_ if $detail < 2;
-    }
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# res = tag_sort(a, b)
+#
+# Compare 2 tags.
+# - a, b: refs to tag hash
+# - res: -1, 0, 1
+#
+# This is used when we have to decide between alternative tags.
+# (Prefer 'lesser' variant.)
+#
+sub tag_sort
+{
+  my ($x, $y);
+
+  $x = length $a->{version};
+  $y = length $b->{version};
+
+  # longer version number first
+  return $y <=> $x if $y <=> $x;
+
+  return $a->{branch} cmp $b->{branch};
+}
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# str = tag_to_str(tag_ref)
+#
+# Convert tag into string.
+# - tag_ref: ref to hash with 'branch' and 'version' keys
+# - str: string (e.g. "foo-1.44")
+#
+# 'master' branch is represented by missing/empty 'branch' key.
+#
+sub tag_to_str
+{
+  my $tag = $_[0];
+  my $str;
+
+  $str = "$tag->{branch}-" if $tag->{branch} ne "";
+  $str .= $tag->{version};
+
+  return $str;
+}
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# str = tags_to_str(tag_array_ref)
+#
+# Convert array of tags into string.
+# - tag_array_ref: ref to array of tags
+# - str: string (e.g. "(tag1, tag2)"
+#
+# This function is used only internally for debugging.
+#
+sub tags_to_str
+{
+  my $tags = $_[0];
+  my $str;
+
+  for my $t (@$tags) {
+    $str .= ", " if $str;
+    $str .= tag_to_str $t;
   }
-  @t = @t2;
 
-  # print "\n--- $tags[$i-1]..$tags[$i] ---\n", @t;
+  return "($str)";
+}
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# choose_tags()
+#
+# Scan commit messages and extract tag & branch information.
+#
+# This stores the tag/branch info in $config->{log}[]{tags}.
+#
+sub choose_tags
+{
+  my $branch = $config->{branch};
+
+  for my $x (@{$config->{log}}) {
+    # printf "# %s\n", tags_to_str($x->{tags});
+
+    # no tag info? -> ignore
+    next if !$x->{tags};
 
-  chomp @t;
-  for (@t) {
-    if(/^Date:\s*(\S+)/) {
-      $date = $1;
-      last;
+    # single tag? -> remember branch info
+    if(@{$x->{tags}} == 1) {
+      $branch = $x->{tags}[0]{branch};
+      next;
     }
-  }
 
-  # handle white space in every first line once and for all
-  my $empty = 1;
-  for (@t) {
-    $empty = 1, $_ = "", next if $_ =~ /^\s*$/;
-    next if !$empty;
-    s/^\s*//;
-    $empty = 0;
-  }
+    # several tags? -> choose one
+
+    # any with current branch name?
+    my @t = grep { $_->{branch} eq $branch } @{$x->{tags}};
 
-  @t = grep { !/^(commit|Author:|Date:|Merge:|\s*$)|created.*tag/ } @t;
-  if(@t) {
-    # rewrite a bit to have it look more consistent
-    map { s/(fate|bnc|bsc)#/$1 #/g } @t;
-    map { s/(fate|bnc|bsc)\s*(\d{4})/$1 #$2/g } @t;
-    map { s/\(#/(bnc #/g } @t;
-    map { s/bug\s*#/bnc #/g } @t;
-    map { s/feat(\.|ure)?\s*#?(\d+)/fate #$2/g } @t;
-    map { s/^ {4}// } @t;
-    map { s/^ {8}// } @t;
-    map { s/^ +/  / } @t;
-    map { s/^\s*[+\-][\-\s]*/- / } @t;
-    map { s/^([^ \-])/- $1/ } @t;
-    map { s/^/\t/ } @t;
-    map { s/\\'/'/ } @t;
+    # no? -> choose among all
+    @t = @{$x->{tags}} if @t == 0;
 
-    # print "\n--- $tags[$i-1]..$tags[$i] ---\n", join("\n", @t);
+    # prefer longest version number, then alphanumerically smallest branch name
+    @t = sort tag_sort @t;
 
-    my $t = $tags[$i];
-    $t = "${branch}-$t" if $branch ne 'master' && $t eq "HEAD";
-    $t =~ s/HEAD/$current_version/;
-    print F "$date:\t$t\n";
-    print F join("\n", @t), "\n\n";
+    $branch = $t[0]{branch};
+    $x->{tags} = [ $t[0] ];
+
+    # printf "X %s\n", tags_to_str($x->{tags});
   }
 }
 
-close F;
-
 
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-sub usage
+# add_head_tag()
+#
+# Suggest tag for HEAD if there isn't one.
+#
+# Basically, use branch + version from most recent tag and increment version.
+#
+sub add_head_tag
 {
-  my $err = shift;
+  return if @{$config->{log}} < 2;
 
-  print <<"  usage";
-Usage: git2log [OPTIONS] [FILE]
-Create changelog and project version from git repo.
-  --changelog   Write changelog to FILE.
-  --version     Write version number to FILE.
-  --branch      Write current branch to FILE.
-  --update      Write changelog or version only if FILE is outdated.
-  --help        Print this help text.
-  usage
+  # HEAD tagged already?
+  return if $config->{log}[0]{tags};
 
-  exit $err;
+  # the first tagged commit if HEAD isn't tagged
+  my $tag = { %{$config->{log}[1]{tags}[0]} };
+
+  # increment version
+  $tag->{version} =~ s/(\d+)$/$1 + 1/e;
+
+  $config->{log}[0]{tags}[0] = $tag;
+
+  # remember that the tag was generated
+  $config->{log}[0]{was_untagged} = 1;
 }
 
 
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-sub get_branch_tags
+# fix_dates()
+#
+# Adjust time stamps in entire git log.
+#
+# The time stamps of the git commits are not necessarily ordered by date.
+# But the generated changelog is required to have a strictly monotonic time.
+#
+# We do this by going through the log in reverse and rewriting any dates we
+# find whenever the date decreases.
+#
+# A minimum time difference of 1 second beween entries is maintained.
+#
+# Not very subtle but it works.
+#
+sub fix_dates
 {
-  my @ntags;
+  my $last_date;
+
+  for (reverse @{$config->{raw_log}}) {
+    # e.g. "Date: 1443184889 +0200"
+    if(/^(Date:\s+)(\S+)(\s+\S+)/) {
+      if(defined $last_date && $2 < $last_date) {
+        $_ = "$1$last_date$3\n";
+      }
+      else {
+        $last_date = $2;
+      }
 
-  for (@all_tags) {
-    if(/^\d/) {
-      s/(\d+)/sprintf "%04d", $1/eg;
-      push @ntags, $_;
+      # ensure a minimal time gap of 1 second
+      $last_date += 1;
     }
-    elsif(s/^$branch\-//) {
-      s/(\d+)/sprintf "%04d", $1/eg;
-      push @ntags, "$branch-$_";
+  }
+}
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# format_all_logs()
+#
+# Format the entire git log.
+#
+# This is done for every code version individually (the log has already been
+# split accordingly).
+#
+# If $config->{start} is set, use this as starting point. Else format the
+# entire git log.
+#
+sub format_all_logs
+{
+  # check if start tag actually exists - if not, print nothing
+  if($config->{start}) {
+    my $tag_found;
+    for (@{$config->{log}}) {
+      $tag_found = 1, last if grep { tag_to_str($config->{start}) eq 
tag_to_str($_) } @{$_->{tags}};
     }
+    return if !$tag_found;
   }
 
-  return sort @ntags;
+  for (@{$config->{log}}) {
+    if($config->{start}) {
+      # stop if we meet the start tag
+      last if grep { tag_to_str($config->{start}) eq tag_to_str($_) } 
@{$_->{tags}};
+    }
+    format_log $_;
+  }
 }
 
 
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-sub get_branch
+# format_log(log)
+#
+# Format log messages.
+# - log: is an array ref with individual commits
+#
+# All commits belong to a specific code version (stored in $log->{tag}).
+# $log->{formatted} holds the result.
+#
+# The process is done in several individual steps, documented below in the 
code.
+#
+sub format_log
 {
-  my $b;
+  my $log = $_[0];
+
+  my $merge;
+  my $commit;
+  my $saved_commit;
+  my $commits;
+
+  for (@{$log->{lines}}) {
+    if(/^commit (\S+)/) {
+      $commit = { ref => $1 };
+      push @{$commits}, $commit;
+
+      if(
+        $merge &&
+        $merge->{merge_end} eq substr($commit->{ref}, 0, 
length($merge->{merge_end}))
+      ) {
+        undef $merge;
+      }
+
+      if($merge) {
+        $commit->{merge_ref} = $merge->{ref};
+        $commit->{date} = $merge->{date};
+        # add to all commits so it's not lost when we re-arrange
+        $commit->{merge_msg} = $merge->{msg} if $merge->{msg};
+        # saved entry no longer needed
+        undef $saved_commit;
+      }
+
+      next;
+    }
+
+    if(/^Merge: (\S+)/) {
+      if($commit) {
+        $merge = { merge_end => $1, ref => $commit->{ref} } unless $merge;
+        $saved_commit = pop @{$commits};
+      }
+      undef $commit;
+      next;
+    }
+
+    if(/^Date:\s+(\S.*)/) {
+      if($commit) {
+        $commit->{date} = $1 if !$commit->{date};
+      }
+      elsif($merge) {
+        $merge->{date} = $1 if !$merge->{date};
+      }
+      next;
+    }
+
+    if(/^Author:\s+(\S.*)/) {
+      $commit->{author} = $1 if $commit;
+      $merge->{author} = $1 if $merge && !$merge->{author};
+      next;
+    }
+
+    if($commit) {
+      push @{$commit->{lines}}, $_ if s/^    //;
+    }
+    elsif($merge && !$merge->{msg}) {
+      if(/^    Merge pull request (#\d+) from (\S+)/) {
+        if($config->{github_project}) {
+          $merge->{msg} = "merge gh#$config->{github_project}$1";
+        }
+        else {
+          $merge->{msg} = "merge pr $2";
+        }
+      }
+      elsif(/^    Merge branch '([^']+)'/) {
+        $merge->{msg} = "merge branch $1";
+      }
+    }
+  }
+
+  # it can happen that there's a lonely merge commit left at the end
+  if($merge && $saved_commit) {
+    $saved_commit->{merge_ref} = $merge->{ref};
+    $saved_commit->{date} = $merge->{date};
+    $saved_commit->{author} = $merge->{author};
+    $saved_commit->{merge_msg} = $merge->{msg} if $merge->{msg};
+    $saved_commit->{formatted} = [];
+
+    push @{$commits}, $saved_commit;
+  }
+
+  # Note: the individual steps below work on the array @$commits and modify
+  # its content.
+
+  # step 1
+  # - if there are paragraphs starting with '@log@' or '@+log@'
+  #     - delete first paragraph (short summary)
+  # - else
+  #     - keep only first paragraph
+  # - if there is a paragraph starting with '@-log', delete entire log
+  # - tag commits that have a '@log@' tag so we can delete untagged commits
+  #   belonging to the same merge commit later
+
+  my $tagged_merges = {};
+
+  for my $commit (@$commits) {
+    my $para_cnt = 0;
+    my $delete_all = 0;
+    my $delete_first = 0;
+    for (@{$commit->{lines}}) {
+      $para_cnt++ if $_ eq "";
+      $para_cnt = 0, $delete_first = 1 if /^\@\+log\@/;
+      $delete_all = 1 if /^\@\-log\@/;
+      if(/^\@log\@/) {
+        $para_cnt = 0;
+        $commit->{clear} = 1;
+        $tagged_merges->{$commit->{merge_ref}} = 1 if $commit->{merge_ref} || 
$log->{was_untagged};
+      }
+      $_ = undef if $para_cnt;
+    }
+    shift @{$commit->{lines}} if $delete_first;
+    $commit->{lines} = [] if $delete_all;
+  }
 
-  for (`git branch`) {
-    if(/^\*\s+(\S+)/) {
-      $b = $1;
-      last;
+  # step 2
+  # - clean up tagged commits or commits belonging to tagged merges
+
+  for my $commit (@$commits) {
+    next unless $commit->{clear} || $tagged_merges->{$commit->{merge_ref}};
+    for (@{$commit->{lines}}) {
+      last if /^\@\+?log\@/;
+      $_ = undef;
+    }
+  }
+
+  # step 3
+  # - join lines
+
+  for my $commit (@$commits) {
+    my $lines;
+    my $line;
+
+    for (@{$commit->{lines}}) {
+      next if $_ eq "";
+      if(
+        s/^\s*[+\-][\-\s]*// ||
+        s/^\@\+?log\@// ||
+        $line eq ""
+      ) {
+        s/^\s*//;
+        push @$lines, $line if $line ne "";
+        $line = $_;
+      }
+      else {
+        s/^\s*//;
+        $line .= " " if $line ne "";
+        $line .= $_;
+      }
+    }
+    push @$lines, $line if $line ne "";
+
+    $commit->{formatted} = $lines if $lines;
+  }
+
+  # step 4
+  # - fix small glitches
+
+  for my $commit (@$commits) {
+    next unless $commit->{formatted};
+    for (@{$commit->{formatted}}) {
+      s/(fate|bnc|bsc)\s*(#\d+)/\L$1\E$2/ig;
     }
   }
 
-  $b = "master" if $b eq '(no';
+  # step 5
+  # - add merge info at the top or bottom (depending on $opt_merge_msg_before)
+
+  my $merge_logged;
+
+  for my $commit ($opt_merge_msg_before ? reverse(@$commits) : @$commits) {
+    next unless $commit->{formatted};
+
+    if($commit->{merge_ref} && !$merge_logged->{$commit->{merge_ref}}) {
+      $merge_logged->{$commit->{merge_ref}} = 1;
+      if($commit->{merge_msg}) {
+        if($opt_merge_msg_before) {
+          unshift @{$commit->{formatted}}, $commit->{merge_msg};
+        }
+        else {
+          push @{$commit->{formatted}}, $commit->{merge_msg};
+        }
+      }
+    }
+  }
+
+  # step 6
+  # - join commit messages with same author (optionally even with different 
dates)
+
+  my $commit0;
+
+  for my $commit (@$commits) {
+    next if !$commit->{formatted};
+    $commit0 = $commit, next if !$commit0;
+
+    if(
+      # $commit->{merge_ref} eq $commit0->{merge_ref} &&
+      (
+        $opt_join_author && ($commit->{author} eq $commit0->{author})
+        && (!$opt_keep_date || $commit->{date} eq $commit0->{date})
+      )
+      || $opt_format eq 'internal'
+    ) {
+      unshift @{$commit0->{formatted}}, @{$commit->{formatted}};
+      delete $commit->{formatted};
+    }
+    else {
+      $commit0 = $commit;
+    }
+  }
+
+  # step 7
+  # - add version tag at the end of the first log entry
+
+  for my $commit (@$commits) {
+    next unless $commit->{formatted};
+
+    if($opt_format eq 'obs') {
+      push @{$commit->{formatted}}, $log->{tags}[0]{version} if defined 
$log->{tags}[0]{version};
+    }
+    else {
+      # push @{$commit->{formatted}}, tag_to_str($log->{tags}[0]);
+    }
+
+    last;
+  }
+
+  # step 8
+  # - add line breaks
+
+  for my $commit (@$commits) {
+    next unless $commit->{formatted};
+    for (@{$commit->{formatted}}) {
+      $_ = add_line_breaks $_;
+    }
+  }
 
-  return $b;
+  # step 9
+  # - generate final log message
+  #
+  # note: non-(open)suse email addresses are replaced by $opt_default_email
+
+  my $formated_log;
+
+  for my $commit (@$commits) {
+    next unless $commit->{formatted} && @{$commit->{formatted}};
+
+    if($opt_format eq 'obs') {
+      $formated_log .= "-" x $opt_sep_width . "\n";
+      $formated_log .= format_date_obs($commit->{date});
+    }
+    else {
+      $formated_log .= format_date_iso($commit->{date});
+    }
+    if($opt_format eq 'obs') {
+      my $auth = $commit->{author};
+      $auth =~ s/^.*<//;
+      $auth =~ s/>.*$//;
+      # replace non-suse e-mail addresses with a generic one
+      if($auth !~ /\@(suse\.(com|cz|de)|opensuse\.org)$/) {
+        $auth = $opt_default_email;
+      }
+      $formated_log .= " - $auth\n\n";
+    }
+    else {
+      $formated_log .= ":\t" . tag_to_str($log->{tags}[0]) . "\n";
+    }
+
+    for (@{$commit->{formatted}}) {
+      s/^/\t/mg if $opt_format eq 'internal';
+      $formated_log .= "$_\n";
+    }
+
+    $formated_log .= "\n";
+  }
+
+  $log->{formatted} = $formated_log;
 }
 
 
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-sub get_parent_branch
+# new_text = add_line_breaks(text)
+#
+# Add line breaks to text.
+# - text: some text
+# - new_text: same text, reformatted
+#
+# Lines are formatted to have a maximal length of $opt_width. If this causes
+# the last line to be shorter than $opt_width_fuzz, it is appended to the
+# previous line.
+#
+sub add_line_breaks
 {
-  my $p;
+  my @words = split /\s+/, @_[0];
+  my $remaining_len = length(join '', @words);
 
-  for (`git log -g --pretty=oneline`) {
-    $p = $1 if /checkout: moving from (\S+) to $branch/;
-  }
+  my $str = shift(@words);
+  my $len = length $str;
+
+  my $next_len;
+  my $word_len;
 
-  # print "parent = $p\n";
+  for (@words) {
+    $word_len = length;
+    $remaining_len -= $word_len;
+    $next_len = $len + $word_len + 1;
+    if(
+      $next_len >= $opt_width &&
+      $next_len + $remaining_len + 1 >= $opt_width + $opt_width_fuzz
+    ) {
+      $str .= "\n  $_";
+      $len = $word_len;
+    }
+    else {
+      $str .= " $_";
+      $len += $word_len + 1;
+    }
+  }
 
-  return $p || "master";
+  return "- " . $str;
 }
 
 
 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-sub get_version
+# seconds = raw_date_to_s(git_date)
+#
+# Convert git raw date to seconds.
+# - git_date: raw git format (e.g. "1443184889 +0200")
+# - seconds: the seconds part (e.g. "1443184889")
+#
+sub raw_date_to_s
 {
-  my $v = $tags[-1];
-  $v =~ s/(\d+)/$1 + 0/eg;
+  return (split / /, $_[0])[0];
+}
 
-  if(`git log --pretty=medium --date=iso '$v..HEAD'`) {
-    $v =~ s/(\d+)$/$1 + 1/e;
-  }
 
-  $v =~ s/^$branch\-//;
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# date = format_date_obs(git_date)
+#
+# Convert git raw date to obs format.
+# - git_date: raw git format (e.g. "1443184889 +0200")
+# - date: obs format ("Fri Sep 25 12:41:29 UTC 2015")
+#
+sub format_date_obs
+{
+  my @d = gmtime(raw_date_to_s($_[0]));
 
-  return $v;
+  return
+    qw(Sun Mon Tue Wed Thu Fri Sat)[$d[6]] . " " .
+    qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)[$d[4]] . " " .
+    $d[3] . " " .
+    sprintf("%02d:%02d:%02d", $d[2], $d[1], $d[0]) . " UTC " .
+    (1900 + $d[5]);
 }
 
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# date = format_date_iso(git_date)
+#
+# Convert git raw date to iso format.
+# - git_date: raw git format (e.g. "1443184889 +0200")
+# - date: obs format ("2015-09-25")
+#
+sub format_date_iso
+{
+  my @d = gmtime(raw_date_to_s($_[0]));
+
+  return sprintf("%04d-%02d-%02d", 1900 + $d[5], $d[4] + 1, $d[3]);
+}


Reply via email to