implement group namespaces
diff --git a/README.md b/README.md
index 0e31cb1..7daf91c 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@
 * Supports same option multiple times (can store in slice or last option counts)
 * Supports maps
 * Supports function callbacks
+* Supports namespaces for (nested) option groups
 
 The flags package uses structs, reflection and struct field tags
 to allow users to specify command line options. This results in very simple
diff --git a/command_private.go b/command_private.go
index bd97cc7..2560324 100644
--- a/command_private.go
+++ b/command_private.go
@@ -120,7 +120,7 @@
 			}
 
 			if len(option.LongName) > 0 {
-				ret.longNames[option.LongName] = option
+				ret.longNames[option.LongNameWithNamespace()] = option
 			}
 		}
 	})
diff --git a/flags.go b/flags.go
index 34adf1a..73da309 100644
--- a/flags.go
+++ b/flags.go
@@ -22,6 +22,7 @@
 //     Supports same option multiple times (can store in slice or last option counts)
 //     Supports maps
 //     Supports function callbacks
+//     Supports namespaces for (nested) option groups
 //
 // Additional features specific to Windows:
 //     Options with short names (/v)
@@ -94,6 +95,10 @@
 //
 //     group:                when specified on a struct field, makes the struct
 //                           field a separate group with the given name (optional)
+//     namespace:            when specified on a group struct field, the namespace
+//                           gets prepended to every option's long name and
+//                           subgroup's namespace of this group, separated by
+//                           the global namespace delimiter (optional)
 //     command:              when specified on a struct field, makes the struct
 //                           field a (sub)command with the given name (optional)
 //     subcommands-optional: when specified on a command struct field, makes
@@ -109,10 +114,10 @@
 //
 // Option groups:
 //
-// Option groups are a simple way to semantically separate your options. The
-// only real difference is in how your options will appear in the built-in
-// generated help. All options in a particular group are shown together in the
-// help under the name of the group.
+// Option groups are a simple way to semantically separate your options. All
+// options in a particular group are shown together in the help under the name
+// of the group. Namespaces can be used to specify option long names more
+// precisely and emphasize the options affiliation to their group.
 //
 // There are currently three ways to specify option groups.
 //
diff --git a/group.go b/group.go
index b424f60..49f6621 100644
--- a/group.go
+++ b/group.go
@@ -29,6 +29,9 @@
 	// (Command embeds Group) in the built-in generated help and man pages.
 	LongDescription string
 
+	// The namespace of the group
+	Namespace string
+
 	// The parent of the group or nil if it has no parent
 	parent *Group
 
diff --git a/group_private.go b/group_private.go
index bbfef49..4ea7340 100644
--- a/group_private.go
+++ b/group_private.go
@@ -32,7 +32,7 @@
 			prio = 3
 		}
 
-		if name == opt.LongName && prio < 2 {
+		if name == opt.LongNameWithNamespace() && prio < 2 {
 			retopt = opt
 			prio = 2
 		}
@@ -191,11 +191,13 @@
 	g.eachGroup(func(g *Group) {
 		for _, option := range g.options {
 			if option.LongName != "" {
-				if otherOption, ok := longNames[option.LongName]; ok {
+				longName := option.LongNameWithNamespace()
+
+				if otherOption, ok := longNames[longName]; ok {
 					duplicateError = newErrorf(ErrDuplicatedFlag, "option `%s' uses the same long name as option `%s'", option, otherOption)
 					return
 				}
-				longNames[option.LongName] = option
+				longNames[longName] = option
 			}
 			if option.ShortName != 0 {
 				if otherOption, ok := shortNames[option.ShortName]; ok {
@@ -223,10 +225,13 @@
 		ptrval := reflect.NewAt(realval.Type(), unsafe.Pointer(realval.UnsafeAddr()))
 		description := mtag.Get("description")
 
-		if _, err := g.AddGroup(subgroup, description, ptrval.Interface()); err != nil {
+		group, err := g.AddGroup(subgroup, description, ptrval.Interface())
+		if err != nil {
 			return true, err
 		}
 
+		group.Namespace = mtag.Get("namespace")
+
 		return true, nil
 	}
 
diff --git a/group_test.go b/group_test.go
index 35d0767..b5ed9d4 100644
--- a/group_test.go
+++ b/group_test.go
@@ -113,6 +113,33 @@
 	}
 }
 
+func TestGroupNestedInlineNamespace(t *testing.T) {
+	var opts = struct {
+		Opt string `long:"opt"`
+
+		Group struct {
+			Opt   string `long:"opt"`
+			Group struct {
+				Opt string `long:"opt"`
+			} `group:"Subsubgroup" namespace:"sap"`
+		} `group:"Subgroup" namespace:"sip"`
+	}{}
+
+	p, ret := assertParserSuccess(t, &opts, "--opt", "a", "--sip.opt", "b", "--sip.sap.opt", "c", "rest")
+
+	assertStringArray(t, ret, []string{"rest"})
+
+	assertString(t, opts.Opt, "a")
+	assertString(t, opts.Group.Opt, "b")
+	assertString(t, opts.Group.Group.Opt, "c")
+
+	for _, name := range []string{"Subgroup", "Subsubgroup"} {
+		if p.Command.Group.Find(name) == nil {
+			t.Errorf("Expected to find group '%s'", name)
+		}
+	}
+}
+
 func TestDuplicateShortFlags(t *testing.T) {
 	var opts struct {
 		Verbose   []bool   `short:"v" long:"verbose" description:"Show verbose debug information"`
diff --git a/help.go b/help.go
index 4ded286..8283223 100644
--- a/help.go
+++ b/help.go
@@ -50,7 +50,7 @@
 				ret.hasValueName = true
 			}
 
-			l := utf8.RuneCountInString(info.LongName) + lv
+			l := utf8.RuneCountInString(info.LongNameWithNamespace()) + lv
 
 			if c != p.Command {
 				// for indenting
@@ -109,7 +109,7 @@
 		}
 
 		line.WriteString(defaultLongOptDelimiter)
-		line.WriteString(option.LongName)
+		line.WriteString(option.LongNameWithNamespace())
 	}
 
 	if option.canArgument() {
diff --git a/help_test.go b/help_test.go
index e3e84d8..0522ce7 100644
--- a/help_test.go
+++ b/help_test.go
@@ -57,6 +57,14 @@
 		IntMap      map[string]int `long:"intmap" default:"a:1" description:"A map from string to int" ini-name:"int-map"`
 	} `group:"Other Options"`
 
+	Group struct {
+		Opt string `long:"opt" description:"This is a subgroup option"`
+
+		Group struct {
+			Opt string `long:"opt" description:"This is a subsubgroup option"`
+		} `group:"Subsubgroup" namespace:"sap"`
+	} `group:"Subgroup" namespace:"sip"`
+
 	Command struct {
 		ExtraVerbose []bool `long:"extra-verbose" description:"Use for extra verbosity"`
 	} `command:"command" alias:"cm" alias:"cmd" description:"A command"`
@@ -97,6 +105,12 @@
   -s=                      A slice of strings (some, value)
       --intmap=            A map from string to int (a:1)
 
+Subgroup:
+      --sip.opt=           This is a subgroup option
+
+Subsubgroup:
+      --sip.sap.opt=       This is a subsubgroup option
+
 Help Options:
   -h, --help               Show this help message
 
@@ -168,6 +182,12 @@
 .TP
 \fB--intmap\fP
 A map from string to int
+.TP
+\fB--sip.opt\fP
+This is a subgroup option
+.TP
+\fB--sip.sap.opt\fP
+This is a subsubgroup option
 .SH COMMANDS
 .SS command
 A command
diff --git a/ini_test.go b/ini_test.go
index 976c962..d25ac52 100644
--- a/ini_test.go
+++ b/ini_test.go
@@ -63,6 +63,14 @@
 int-map = a:2
 int-map = b:3
 
+[Subgroup]
+; This is a subgroup option
+Opt =
+
+[Subsubgroup]
+; This is a subsubgroup option
+Opt =
+
 [command.A command]
 ; Use for extra verbosity
 ; ExtraVerbose =
@@ -103,6 +111,14 @@
 ; A map from string to int
 ; int-map = a:1
 
+[Subgroup]
+; This is a subgroup option
+; Opt =
+
+[Subsubgroup]
+; This is a subsubgroup option
+; Opt =
+
 [command.A command]
 ; Use for extra verbosity
 ; ExtraVerbose =
@@ -141,6 +157,14 @@
 ; A map from string to int
 ; int-map = a:1
 
+[Subgroup]
+; This is a subgroup option
+; Opt =
+
+[Subsubgroup]
+; This is a subsubgroup option
+; Opt =
+
 [command.A command]
 ; Use for extra verbosity
 ; ExtraVerbose =
diff --git a/man.go b/man.go
index 9e1f036..3097156 100644
--- a/man.go
+++ b/man.go
@@ -50,7 +50,7 @@
 					fmt.Fprintf(wr, ", ")
 				}
 
-				fmt.Fprintf(wr, "--%s", opt.LongName)
+				fmt.Fprintf(wr, "--%s", opt.LongNameWithNamespace())
 			}
 
 			fmt.Fprintln(wr, "\\fP")
diff --git a/option.go b/option.go
index 17548a7..cb7ba75 100644
--- a/option.go
+++ b/option.go
@@ -67,6 +67,29 @@
 	tag          multiTag
 }
 
+// LongNameWithNamespace returns the option's long name with the group namespaces
+// prepended by walking up the option's group tree. Namespaces and the long name
+// itself are separated by the global namespace delimiter. If the long name is
+// empty an empty string is returned.
+func (option *Option) LongNameWithNamespace() string {
+	if len(option.LongName) == 0 {
+		return ""
+	}
+
+	longName := option.LongName
+	g := option.group
+
+	for g != nil {
+		if g.Namespace != "" {
+			longName = g.Namespace + NamespaceDelimiter + longName
+		}
+
+		g = g.parent
+	}
+
+	return longName
+}
+
 // String converts an option to a human friendly readable string describing the
 // option.
 func (option *Option) String() string {
@@ -81,12 +104,12 @@
 		if len(option.LongName) != 0 {
 			s = fmt.Sprintf("%s%s, %s%s",
 				string(defaultShortOptDelimiter), short,
-				defaultLongOptDelimiter, option.LongName)
+				defaultLongOptDelimiter, option.LongNameWithNamespace())
 		} else {
 			s = fmt.Sprintf("%s%s", string(defaultShortOptDelimiter), short)
 		}
 	} else if len(option.LongName) != 0 {
-		s = fmt.Sprintf("%s%s", defaultLongOptDelimiter, option.LongName)
+		s = fmt.Sprintf("%s%s", defaultLongOptDelimiter, option.LongNameWithNamespace())
 	}
 
 	return s
diff --git a/optstyle.go b/optstyle.go
new file mode 100644
index 0000000..0eedda9
--- /dev/null
+++ b/optstyle.go
@@ -0,0 +1,6 @@
+package flags
+
+var (
+	// NamespaceDelimiter separates group namespaces and option long names
+	NamespaceDelimiter = "."
+)