Hello community,

here is the log from the commit of package mksusecd for openSUSE:Factory 
checked in at 2018-11-18 23:30:55
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/mksusecd (Old)
 and      /work/SRC/openSUSE:Factory/.mksusecd.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "mksusecd"

Sun Nov 18 23:30:55 2018 rev:54 rq:649317 version:1.66

Changes:
--------
--- /work/SRC/openSUSE:Factory/mksusecd/mksusecd.changes        2018-09-15 
15:41:16.708787661 +0200
+++ /work/SRC/openSUSE:Factory/.mksusecd.new/mksusecd.changes   2018-11-18 
23:31:14.861549463 +0100
@@ -1,0 +2,8 @@
+Thu Nov 15 15:30:07 UTC 2018 - snw...@suse.de
+
+- merge gh#openSUSE/mksusecd#39
+- add HOWTO describing some typical uses
+- update git2log script
+- 1.66
+
+--------------------------------------------------------------------

Old:
----
  mksusecd-1.65.tar.xz

New:
----
  mksusecd-1.66.tar.xz

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

Other differences:
------------------
++++++ mksusecd.spec ++++++
--- /var/tmp/diff_new_pack.JxAwMC/_old  2018-11-18 23:31:15.549548634 +0100
+++ /var/tmp/diff_new_pack.JxAwMC/_new  2018-11-18 23:31:15.549548634 +0100
@@ -18,7 +18,7 @@
 
 
 Name:           mksusecd
-Version:        1.65
+Version:        1.66
 Release:        0
 Summary:        Create SUSE Linux installation ISOs
 License:        GPL-3.0+

++++++ mksusecd-1.65.tar.xz -> mksusecd-1.66.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mksusecd-1.65/HOWTO.md new/mksusecd-1.66/HOWTO.md
--- old/mksusecd-1.65/HOWTO.md  1970-01-01 01:00:00.000000000 +0100
+++ new/mksusecd-1.66/HOWTO.md  2018-11-15 16:30:07.000000000 +0100
@@ -0,0 +1,116 @@
+# How to modify a SUSE Linux installation medium
+
+Sometimes it's convenient to make small modifications to the regular SUSE 
installation media.
+
+For this, `mksusecd` exists.
+
+## Replace or add files
+
+You can replace or add any file to the medium. Simply create a directory 
structure and put the file into it.
+Then specify all sources to be merged on the command line. For example
+
+```sh
+mkdir -p /tmp/foo/boot/x86_64/loader/
+cp some_suitable_picture.jpg /tmp/foo/boot/x86_64/loader/welcome.jpg
+mksusecd --create foo.iso suse.iso /tmp/foo
+```
+will create a new image `foo.iso` from `suse.iso` that has a new startup 
picture.
+
+
+## Change default boot options
+
+If you always have to enter the same boot options for each installation, add 
them:
+
+```sh
+mksusecd --create foo.iso --boot "textmode=1 sshd=1 password=XXX" suse.iso
+```
+
+It is also possible to add a new boot entry instead of modifying the existing 
one:
+
+```sh
+mksusecd --create foo.iso --add-entry "XXX" --boot "sshd=1 password=XXX" 
suse.iso
+```
+
+
+## Change default repository
+
+If you run your own installation server you might want a medium that connects 
to it per default. To change the
+default network repository do, for example:
+
+```sh
+mksusecd --create foo.iso --net "https://example.com/suse"; suse.iso
+```
+
+
+## Create small network iso
+
+If you are regularly installing via network or need to hand out a small image 
with your modifications, create a network iso.
+For this, simply add the `--nano` option:
+
+```sh
+mksusecd --create foo.iso --nano suse.iso
+```
+
+
+## Integrate driver updates
+
+To seamlessly integrate any driver updates you want applied, add them to the 
initrd. `mksusecd` has special code for this. For example
+
+```sh
+mksusecd --create foo.iso --initrd bar.dud suse.iso
+```
+
+creates a new `foo.iso` that will load the driver update `bar.dud` without any 
extra user interaction.
+
+
+## Include add-ons
+
+If you have a collection of rpms you would like to be able to install you can 
have `mksusecd` create an add-on repository and put it on the medium:
+
+```sh
+mksusecd --create foo.iso --addon foo.rpm bar.rpm -- suse.iso
+```
+
+
+## Update kernel (and kernel modules)
+
+If you need an updated kernel to be able to run the installation, it can get 
really tricky. `mksusecd` lets you rework the installation
+system included on the medium so that a new kernel is used during the 
installation. You just need the new kernel packages:
+
+```sh
+mksusecd --create foo.iso --kernel kernel-default.rpm kernel-firmware.rpm -- 
suse.iso
+```
+
+If you need also KMP packages, add them, too.
+
+`mksusecd` will try to include the same modules as in the original 
installation medium. If some modules are missing, it will show
+the differences.
+
+Sometimes just a module is missing in the installation system that you really 
need. It's possible to add it this way:
+
+```sh
+mksusecd --create foo.iso --kernel kernel-default.rpm kernel-firmware.rpm 
--modules bar,zap -- suse.iso
+```
+
+This replaces the kernel and adds modules `bar` and `zap`. Module dependencies 
will be automatically taken into account.
+
+Note that the `--kernel` option does **not** change the kernel that is going 
to be installed! For this, either create a driver update
+with the kernel rpms and integrate the driver update or create an add-on with 
the kernel packages.
+
+
+
+## SLES 15 and later: modules and repositories
+
+SLES 15 supports so-called 'modules' (not to be confused with kernel modules) 
which are basically repositories for different product components.
+
+`mksusecd` lets you create a single medium containing the parts you need.
+
+```sh
+mksusecd --list-repos suse.iso extra.iso
+```
+
+shows the modules that are on the medium. Pick the modules you need and do, 
for example:
+
+```sh
+mksusecd --create foo.iso --include-repos SLES15,Basesystem-Module,HPC-Module 
suse.iso extra.iso
+```
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mksusecd-1.65/VERSION new/mksusecd-1.66/VERSION
--- old/mksusecd-1.65/VERSION   2018-09-14 10:53:41.000000000 +0200
+++ new/mksusecd-1.66/VERSION   2018-11-15 16:30:07.000000000 +0100
@@ -1 +1 @@
-1.65
+1.66
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mksusecd-1.65/changelog new/mksusecd-1.66/changelog
--- old/mksusecd-1.65/changelog 2018-09-14 10:53:41.000000000 +0200
+++ new/mksusecd-1.66/changelog 2018-11-15 16:30:07.000000000 +0100
@@ -1,102 +1,141 @@
+2018-11-15:    1.66
+       - merge gh#openSUSE/mksusecd#39
+       - add HOWTO describing some typical uses
+       - update git2log script
+
 2018-09-14:    1.65
-       - no longer assume repo-md repositories don't include the installer 
(bsc #1093145)
+       - merge gh#openSUSE/mksusecd#38
+       - no longer assume repo-md repositories don't include the installer
+         (bsc#1093145)
 
 2018-08-31:    1.64
-       - adjust also isozipl to isoinfo changes (bsc #1097814)
+       - merge gh#openSUSE/mksusecd#37
+       - adjust also isozipl to isoinfo changes (bsc#1097814)
 
 2018-08-29:    1.63
+       - merge gh#openSUSE/mksusecd#36
        - don't forget to sign addon repository
 
 2018-07-05:    1.62
-       - don't miss zstd compression module (bsc #1100236)
+       - merge gh#openSUSE/mksusecd#35
+       - don't miss zstd compression module (bsc#1100236)
 
 2018-06-27:    1.61
-       - keep some meta data for media checking even if no iso9660 filesystem 
is used (bnc #1000947)
+       - merge gh#openSUSE/mksusecd#34
+       - keep some meta data for media checking even if no iso9660
+         filesystem is used (#1000947)
 
 2018-06-15:    1.60
+       - merge gh#openSUSE/mksusecd#33
+       - adjust to isoinfo and syslinux changes (bsc#1097814)
        - don't pass partition offset to tagmedia
-       - adjust to isoinfo and syslinux changes (bsc #1097814)
 
 2018-05-25:    1.59
-       - add -joliet-long to mkisofs call as some file names on our media are 
too long (bsc #1094687)
+       - merge gh#openSUSE/mksusecd#32
+       - add -joliet-long to mkisofs call as some file names on our media
+         are too long (bsc#1094687)
 
 2018-05-08:    1.58
-       - add links to related blog articles
+       - merge gh#openSUSE/mksusecd#31
        - added link to obs builds
+       - add links to related blog articles
 
 2018-05-07:    1.57
-       - fix cpio archive parser (bsc #1092147)
+       - merge gh#openSUSE/mksusecd#30
+       - fix cpio archive parser (bsc#1092147)
 
-2018-01-18:    1.56
-       - fix handling of repo-md repositories with encrypted media
+2018-01-19:    1.56
+       - merge gh#openSUSE/mksusecd#29
        - allow building of encrypted installation media
+       - fix handling of repo-md repositories with encrypted media
 
-2017-12-01:    1.55
+2017-12-05:    1.55
+       - merge gh#openSUSE/mksusecd#28
        - support new CHECKSUMS file
 
-2017-11-29:    1.54
+2017-11-30:    1.54
+       - merge gh#openSUSE/mksusecd#26
        - manage (sle15-style modules) repomd repositories
 
 2017-10-09:    1.53
+       - merge gh#openSUSE/mksusecd#24
+       - rework cpio parsing function to handle blobs added by our product
+         creator
+       - ensure initrd has really been unpacked when --rebuild-initrd
+         option is used
        - beautify code
-       - ensure initrd has really been unpacked when --rebuild-initrd option 
is used
-       - rework cpio parsing function to handle blobs added by our product 
creator
 
-2017-05-18:    1.52
+2017-05-22:    1.52
+       - merge gh#openSUSE/mksusecd#23
+       - remove iso9660 header when creating image for usb media
+         (bsc#939456)
        - clarify description of --fat option
-       - remove iso9660 header when creating image for usb media (bsc #939456)
 
 2017-04-28:    1.51
-       - document all functions
-       - enhanced README.md
+       - merge gh#openSUSE/mksusecd#22
        - fix getopt config
+       - enhanced README.md
+       - document all functions
 
 2017-04-11:    1.50
+       - merge gh#openSUSE/mksusecd#21
+       - distinguish between repomd/classical repo types and allow to
+         specify the default repo location
        - fix typo in help text
-       - distinguish between repomd/classical repo types and allow to specify 
the default repo location
 
 2017-04-05:    1.49
+       - merge gh#openSUSE/mksusecd#20
        - add support for disk images with FAT file system
 
 2017-04-03:    1.48
        - undo accidental mksusecd patch
 
 2017-04-03:    1.47
-       - some small doc fixes
+       - merge gh#openSUSE/mksusecd#19
        - add dvd/disk image layout description
+       - some small doc fixes
 
-2017-03-31:    1.46
+2017-04-03:    1.46
+       - merge gh#openSUSE/mksusecd#16
        - isohybrid: add --size option to specify image size
 
-2017-03-17:    1.45
-       - fix typo
+2017-03-20:    1.45
+       - merge gh#openSUSE/mksusecd#15
        - add --rebuild-initrd option for smaller initrds
+       - fix typo
 
 2017-03-16:    1.44
+       - merge gh#openSUSE/mksusecd#14
        - support old mksquashfs version
 
 2017-03-16:    1.43
-       - fix to work with older modules.dep format (bsc #1027636)
+       - merge gh#openSUSE/mksusecd#13
+       - fix to work with older modules.dep format (bsc#1027636)
 
 2016-08-12:    1.42
-       - extended help text and added some small fixes
-       - fix choosing an add-on name
+       - merge gh#openSUSE/mksusecd#12
+       - support creating add-ons on the target iso (bsc#991935)
        - be less restrictive when generating add-on aliases
-       - support creating add-ons on the target iso (bsc #991935)
+       - fix choosing an add-on name
+       - extended help text and added some small fixes
 
 2016-08-02:    1.41
-       - catch error when we fail to unpack squashfs images
+       - merge gh#openSUSE/mksusecd#11
        - handle modules.order and modules.builtin when updating a kernel
+       - catch error when we fail to unpack squashfs images
 
-2016-07-14:    1.40
-       - tw support: also sign image files
+2016-07-15:    1.40
+       - merge gh#openSUSE/mksusecd#10
        - sanitize function that removes files from iso
        - remove tumbleweed images in 'micro' format
+       - tw support: also sign image files
 
 2016-06-20:    1.39
+       - merge gh#openSUSE/mksusecd#9
        - create missing 'content' file
 
 2016-05-10:    1.38
+       - merge gh#openSUSE/mksusecd#8
        - adjust list of specially handled initrd modules
 
 2016-01-18:    1.37
@@ -106,25 +145,25 @@
        - add hashes for license and control file
 
 2015-12-04:    1.35
-       - support uncompressed initrd (bsc #957847)
+       - support uncompressed initrd (bsc#957847)
 
 2015-10-21:    1.34
        - fix ppc boot iso creation
-
-2015-09-28:    1.33
+       - merge branch master
+       - merge gh#openSUSE/mksusecd#6
        - Add link to linux-devtools documentation
 
 2015-08-12:    1.32
-       - move isozipl to /usr/bin
        - make it work also with mkisofs from cdrtools
+       - move isozipl to /usr/bin
 
 2015-08-10:    1.31
        - fix cpio unpacking bug
 
 2015-07-23:    1.30
+       - add zipl binary blobs
        - integrate isozipl in mksusecd
        - add some options to isohybrid to make it more flexible
-       - add zipl binary blobs
 
 2015-07-22:    1.29
        - added isozipl to make an iso zipl bootable
@@ -133,20 +172,25 @@
        - fix cpio archive unpacking
 
 2015-07-07:    1.27
+       - merge gh#openSUSE/mksusecd#5
        - README: link to mkdud; grammar; formatting
 
 2015-07-06:    1.26
+       - merge gh#openSUSE/mksusecd#4
        - extended readme
 
 2015-06-25:    1.25
-       - keep /content file up-to-date even when new files are added to the iso
+       - keep /content file up-to-date even when new files are added to
+         the iso
 
 2015-06-19:    1.24
        - ensure '/content' is re-signed when necessary
 
 2015-06-19:    1.23
-       - implement --kernel option to replace kernel & modules used for booting
-       - added --add-entry option to create a new boot menu entry for 
modifications
+       - implement --kernel option to replace kernel & modules used for
+         booting
+       - added --add-entry option to create a new boot menu entry for
+         modifications
 
 2015-06-03:    1.22
        - work properly when different iso sources are specified
@@ -173,13 +217,13 @@
        - stick to initrd compression format when extending initrd
 
 2015-02-26:    1.14
-       - replace content.key with actual signing key used
        - handle duplicate filenames
+       - replace content.key with actual signing key used
 
 2015-02-24:    1.13
-       - support initrds with pre-sle12 key management
-       - support adding driver updates to initrd
        - no-sign option should also prevent updating 'content' file
+       - support adding driver updates to initrd
+       - support initrds with pre-sle12 key management
 
 2015-02-10:    1.12
        - simplify key handling
@@ -188,8 +232,8 @@
        - allow to use a user-supplied signing key
 
 2015-02-06:    1.10
-       - re-sign '/content' if necessary
        - fix git2log script
+       - re-sign '/content' if necessary
 
 2015-01-26:    1.9
        - update git2log script
@@ -205,18 +249,18 @@
        - handle more than 2 el torito entries
 
 2014-10-13:    1.5
+       - added '--pico' option
        - added 'archive' Makefile target
        - updated git2log script
-       - added '--pico' option
 
 2014-08-13:    1.4
-       - support adding rpms to the initrd
        - update checksums in /content file
+       - support adding rpms to the initrd
 
 2014-05-23:    1.3
-       - fix typo
        - fix micro & nano options
        - update help text
+       - fix typo
 
 2014-04-29:    1.2
        - better import of old iso9600 metadata
@@ -225,25 +269,28 @@
        - fix --micro option
 
 2014-04-24:    1.0
-       - add ISO meta info
-       - added --micro and --nano to produce small test isos
        - use exclude file instead of individual options
+       - added --micro and --nano to produce small test isos
+       - add ISO meta info
 
 2014-04-15:    0.5
        - fix hybrid iso creation when boot options are added
 
 2014-03-18:    0.4
-       - support adding boot options
-       - support s390x media
-       - ignore isohybrid warnings
        - fix file magic usage
+       - ignore isohybrid warnings
+       - support s390x media
+       - support adding boot options
 
 2014-01-31:    0.3
-       - add 'initrd' option to add files directly to initrd
-       - patch VERSION into mksusecd
-       - added install target
-       - added README
-       - add another example
-       - added help text
        - support fat
+       - added help text
+       - merge gh#openSUSE/mksusecd#1
+       - add another example
+       - merge gh#openSUSE/mksusecd#2
+       - added README
+       - added install target
+       - merge gh#openSUSE/mksusecd#3
+       - patch VERSION into mksusecd
+       - add 'initrd' option to add files directly to initrd
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/mksusecd-1.65/git2log new/mksusecd-1.66/git2log
--- old/mksusecd-1.65/git2log   2018-09-14 10:53:41.000000000 +0200
+++ new/mksusecd-1.66/git2log   2018-11-15 16:30:07.000000000 +0100
@@ -1,5 +1,14 @@
 #! /usr/bin/perl
 
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+#
+# 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;
@@ -10,289 +19,966 @@
 $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 = 'opensuse-packag...@opensuse.org';     # 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);
+
+$opt_max = 2 if $opt_version || $opt_branch;
 
-  my $t = (stat $opt_file)[9];
+# gather some data
+get_github_project;
+get_branch;
+get_log;
+fix_dates;
+get_tags;
+choose_tags;
+add_head_tag;
+get_version;
+
+# just print current branch
+if($opt_branch) {
+  open my $f, ">$opt_file";
+  print $f $config->{branch} ? $config->{branch} : "master", "\n";
+  close $f;
+
+  exit 0;
+}
+
+# just print current version
+if($opt_version) {
+  my $old_version;
+
+  if($opt_file ne '-' && open(my $f, $opt_file)) {
+    chomp($old_version = <$f>);
+    close $f;
+  }
 
-  for (@deps) {
-    $ok = 0 if (stat)[9] > $t;
+  if($config->{version} ne $old_version) {
+    open my $f, ">$opt_file";
+    print $f "$config->{version}\n";
+    close $f;
   }
 
-  exit 0 if $ok;
+  exit 0;
+}
+
+# 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;
 }
 
-@all_tags = `git tag`;
-chomp @all_tags;
+format_all_logs;
 
-$branch = get_branch;
-die "no branch?\n" unless $branch;
+open my $f, ">$opt_file";
 
-@tags = get_branch_tags;
-die "no tags at all?\n" unless @tags;
+print $f $_->{formatted} for @{$config->{log}};
 
-if($branch ne 'master') {
-  if(!grep { /^$branch\-/ } @tags) {
-    $branch = get_parent_branch;
-    die "sorry, can't determine branch\n" unless $branch;
+close $f;
 
-    @tags = get_branch_tags;
-    die "no tags at all?\n" unless @tags;
-  }
+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;
 }
-else {
-  @tags = get_branch_tags;
-  die "no tags at all?\n" unless @tags;
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# 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];
+
+  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($opt_branch) {
-  open my $f, ">$opt_file";
-  print $f "$branch\n";
-  close $f;
 
-  exit 0;
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# 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$//;
+  }
 }
 
-$current_version = get_version;
 
-if($opt_version) {
-  open my $f, ">$opt_file";
-  print $f "$current_version\n";
-  close $f;
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# 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";
 
-  exit 0;
+  my $tag = $config->{log}[0]{tags}[0];
+
+  if($tag->{version}) {
+    $config->{version} = $tag->{version};
+    $config->{branch} = $tag->{branch};
+  }
 }
 
-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_tags()
+#
+# Parse $config->{raw_log}, extract tag names, and split into per-tag
+# sections.
+#
+# Only tags recognized by is_formatted_tag() are considered.
+#
+# Tags inside merge commits are ignored.
+#
+# 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;
 
-  # print STDERR ">> $branch-$bi\n";
+  # the end of the merge commit if in a merge
+  my $merge;
 
-  warn "no tags in this branch yet\n" unless $bi;
+  for (@{$config->{raw_log}}) {
+    if(/^commit (\S+)( \((.*)\))?/) {
+      my $commit = $1;
+      my $tag_list = $3;
+      my $xtag;
+
+      # we have reached the end of the merge commit
+      undef $merge if $merge && $commit =~ /^$merge/;
+
+      # ignore tag info inside a merge commit
+      $tag_list = "" if $merge;
+
+      for my $t (split /, /, $tag_list) {
+        if($t =~ /tag: (\S+)/) {
+          my $tag = $1;
+          my $x = is_formatted_tag $tag;
+          push @$xtag, $x if $x;
+        }
+      }
 
-  for (my $i = 0; $i < $i2; $i++) {
-    if($tags[$i] ge $bi) {
-      if($tags[$i] eq $bi) {
-        $i1 = $i;
+      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;
     }
+    elsif(!$merge && /^Merge: (\S+)/) {
+      # remember end of merge
+      $merge = $1;
+    }
+
+    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 || !/^    [^\-\s]/) {
-      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});
 
-  chomp @t;
-  for (@t) {
-    if(/^Date:\s*(\S+)/) {
-      $date = $1;
-      last;
+    # no tag info? -> ignore
+    next if !$x->{tags};
+
+    # 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}};
+
+    # no? -> choose among all
+    @t = @{$x->{tags}} if @t == 0;
 
-  @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;
+    # prefer longest version number, then alphanumerically smallest branch name
+    @t = sort tag_sort @t;
 
-    # print "\n--- $tags[$i-1]..$tags[$i] ---\n", join("\n", @t);
+    $branch = $t[0]{branch};
+    $x->{tags} = [ $t[0] ];
 
-    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";
+    # 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 (@all_tags) {
-    if(/^\d/) {
-      s/(\d+)/sprintf "%04d", $1/eg;
-      push @ntags, $_;
+  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;
+      }
+
+      # 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 $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};
+        $commit->{author} = $merge->{author};
+        # add to all commits so it's not lost when we re-arrange
+        $commit->{merge_msg} = $merge->{msg};
+      }
+
+      next;
+    }
+
+    if(/^Merge: (\S+)/ && !$merge) {
+      if($commit) {
+        $merge = { merge_end => $1, ref => $commit->{ref} } unless $merge;
+      }
+      next;
+    }
+
+    if(/^Date:\s+(\S.*)/) {
+      $commit->{date} ||= $1 if $commit;
+      $merge->{date} ||= $1 if $merge;
+      next;
+    }
+
+    if(/^Author:\s+(\S.*)/) {
+      $commit->{author} ||= $1 if $commit;
+      $merge->{author} ||= $1 if $merge;
+      next;
+    }
+
+    if($merge) {
+      if(/^    Merge pull request (#\d+) from (\S+)/) {
+        if($config->{github_project}) {
+          push @{$merge->{msg}}, "merge gh#$config->{github_project}$1";
+        }
+        else {
+          push @{$merge->{msg}}, "merge pr $2";
+        }
+      }
+      elsif(/^    Merge branch '([^']+)'( into)?/) {
+        push @{$merge->{msg}}, "merge branch $1" if $2 eq "";
+      }
+      elsif(/^    Merge remote-tracking branch /) {
+        # ignore
+      }
+      elsif(s/^    //) {
+        push @{$commit->{lines}}, $_ unless /^# /;
+      }
+    }
+    elsif($commit) {
+      if(s/^    //) {
+        push @{$commit->{lines}}, $_ unless /^# /;
+      }
+    }
+  }
+
+  # 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) {
+    # skip leading empty lines
+    for (@{$commit->{lines}}) {
+      last if !/^\s*$/;
+      shift @{$commit->{lines}};
+    }
+    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;
+  }
+
+  # 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|boo)\s*(#\d+)/\L$1\E$2/ig;
+    }
+  }
+
+  # step 5
+  # - add merge info at the top or bottom (depending on $opt_merge_msg_before)
+
+  my $merge_logged;
 
-  for (`git branch`) {
-    if(/^\*\s+(\S+)/) {
-      $b = $1;
-      last;
+  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}};
+        }
+      }
     }
   }
 
-  $b = "master" if $b eq '(no';
+  # 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
+  # - remove identical lines
+
+  for my $commit (@$commits) {
+    next unless $commit->{formatted};
+    my %k;
+    $commit->{formatted} = [ grep { !$k{$_}++ } @{$commit->{formatted}} ]
+  }
+
+  # step 9
+  # - remove shortened lines (that match the beginning of other lines)
+
+  for my $commit (@$commits) {
+    next unless $commit->{formatted};
+
+    # return 1 if some other commit line starts with function arg
+    my $is_substr = sub {
+      my $str = $_[0];
+      $str =~ s/\s*…$//;       # github likes to shorten lines with ' …'
+      my $str_len = length $str;
+      for (@{$commit->{formatted}}) {
+        return 1 if $str_len < length($_) && $str eq substr($_, 0, $str_len);
+      }
+
+      return 0;
+    };
+
+    $commit->{formatted} = [ grep { ! $is_substr->($_) } 
@{$commit->{formatted}} ]
+  }
+
+  # step 10
+  # - add line breaks
 
-  return $b;
+  for my $commit (@$commits) {
+    next unless $commit->{formatted};
+    for (@{$commit->{formatted}}) {
+      $_ = add_line_breaks $_;
+    }
+  }
+
+  # step 11
+  # - 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;
 
-  # print "parent = $p\n";
+  my $next_len;
+  my $word_len;
 
-  return $p || "master";
+  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 "- " . $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