diff --git a/src/lib/Hydra/Controller/Jobset.pm b/src/lib/Hydra/Controller/Jobset.pm
index 5f3cdb74..0dfa824b 100644
--- a/src/lib/Hydra/Controller/Jobset.pm
+++ b/src/lib/Hydra/Controller/Jobset.pm
@@ -149,6 +149,7 @@ sub edit : Chained('jobsetChain') PathPart Args(0) {
 
     $c->stash->{template} = 'edit-jobset.tt';
     $c->stash->{edit} = 1;
+    $c->stash->{clone} = defined $c->stash->{params}->{clone};
     $c->stash->{totalShares} = getTotalShares($c->model('DB')->schema);
 }
 
@@ -209,102 +210,41 @@ sub updateJobset {
         , schedulingshares => int($c->stash->{params}->{schedulingshares})
         });
 
-    # Process the inputs of this jobset.
-    unless (defined $c->stash->{params}->{inputs}) {
-        $c->stash->{params}->{inputs} = {};
-        foreach my $param (keys %{$c->stash->{params}}) {
-            next unless $param =~ /^input-(\w+)-name$/;
-            my $baseName = $1;
-            next if $baseName eq "template";
-            $c->stash->{params}->{inputs}->{$c->stash->{params}->{$param}} = { type => $c->stash->{params}->{"input-$baseName-type"}, values => $c->stash->{params}->{"input-$baseName-values"} };
-            unless ($baseName =~ /^\d+$/) { # non-numeric base name is an existing entry
-                $c->stash->{params}->{inputs}->{$c->stash->{params}->{$param}}->{oldName} = $baseName;
-            }
-        }
-    }
+    # Set the inputs of this jobset.
+    $jobset->jobsetinputs->delete;
 
-    foreach my $inputName (keys %{$c->stash->{params}->{inputs}}) {
-        my $inputData = $c->stash->{params}->{inputs}->{$inputName};
-        error($c, "Invalid input name ‘$inputName’.") unless $inputName =~ /^[[:alpha:]][\w-]*$/;
+    foreach my $param (keys %{$c->stash->{params}}) {
+        next unless $param =~ /^input-(\w+)-name$/;
+        my $baseName = $1;
+        next if $baseName eq "template";
+        my $name = $c->stash->{params}->{$param};
+        my $type = $c->stash->{params}->{"input-$baseName-type"};
+        my $values = $c->stash->{params}->{"input-$baseName-values"};
 
-        my $inputType = $inputData->{type};
+        error($c, "Invalid input name ‘$name’.") unless $name =~ /^[[:alpha:]][\w-]*$/;
+        error($c, "Invalid input type ‘$type’.") unless defined $c->stash->{inputTypes}->{$type};
 
-        error($c, "Invalid input type ‘$inputType’.") unless defined $c->stash->{inputTypes}->{$inputType};
+        my $input = $jobset->jobsetinputs->create({ name => $name, type => $type });
 
-        my $input;
-        unless (defined $inputData->{oldName}) {
-            $input = $jobset->jobsetinputs->update_or_create(
-                { name => $inputName
-                , type => $inputType
-                });
-        } else { # it's an existing input
-            $input = ($jobset->jobsetinputs->search({name => $inputData->{oldName}}))[0];
-            die unless defined $input;
-            $input->update({name => $inputName, type => $inputType});
-        }
-
-        # Update the values for this input.  Just delete all the
-        # current ones, then create the new values.
-        $input->jobsetinputalts->delete_all;
-        my $values = $inputData->{values};
-        $values = [] unless defined $values;
-        $values = [$values] unless ref($values) eq 'ARRAY';
+        # Set the values for this input.
+        my @values = ref($values) eq 'ARRAY' ? @{$values} : ($values);
         my $altnr = 0;
-        foreach my $value (@{$values}) {
-            $value = checkInputValue($c, $inputType, $value);
+        foreach my $value (@values) {
+            $value = checkInputValue($c, $type, $value);
             $input->jobsetinputalts->create({altnr => $altnr++, value => $value});
         }
     }
-
-    # Get rid of deleted inputs.
-    my @inputs = $jobset->jobsetinputs->all;
-    foreach my $input (@inputs) {
-        $input->delete unless defined $c->stash->{params}->{inputs}->{$input->name};
-    }
 }
 
 
 sub clone : Chained('jobsetChain') PathPart('clone') Args(0) {
     my ($self, $c) = @_;
 
-    my $jobset = $c->stash->{jobset};
-    requireProjectOwner($c, $jobset->project);
+    requireProjectOwner($c, $c->stash->{project});
 
-    $c->stash->{template} = 'clone-jobset.tt';
-}
-
-
-sub clone_submit : Chained('jobsetChain') PathPart('clone/submit') Args(0) {
-    my ($self, $c) = @_;
-
-    my $jobset = $c->stash->{jobset};
-    requireProjectOwner($c, $jobset->project);
-    requirePost($c);
-
-    my $newJobsetName = trim $c->stash->{params}->{"newjobset"};
-    error($c, "Invalid jobset name: $newJobsetName") if $newJobsetName !~ /^$jobsetNameRE$/;
-
-    my $newJobset;
-    txn_do($c->model('DB')->schema, sub {
-        $newJobset = $jobset->project->jobsets->create(
-            { name => $newJobsetName
-            , description => $jobset->description
-            , nixexprpath => $jobset->nixexprpath
-            , nixexprinput => $jobset->nixexprinput
-            , enabled => 0
-            , enableemail => $jobset->enableemail
-            , emailoverride => $jobset->emailoverride || ""
-            });
-
-        foreach my $input ($jobset->jobsetinputs) {
-            my $newinput = $newJobset->jobsetinputs->create({name => $input->name, type => $input->type});
-            foreach my $inputalt ($input->jobsetinputalts) {
-                $newinput->jobsetinputalts->create({altnr => $inputalt->altnr, value => $inputalt->value});
-            }
-        }
-    });
-
-    $c->res->redirect($c->uri_for($c->controller('Jobset')->action_for("edit"), [$jobset->project->name, $newJobsetName]));
+    $c->stash->{template} = 'edit-jobset.tt';
+    $c->stash->{clone} = 1;
+    $c->stash->{totalShares} = getTotalShares($c->model('DB')->schema);
 }
 
 
diff --git a/src/root/clone-jobset.tt b/src/root/clone-jobset.tt
deleted file mode 100644
index 386f06ed..00000000
--- a/src/root/clone-jobset.tt
+++ /dev/null
@@ -1,24 +0,0 @@
-[% WRAPPER layout.tt title="Clone jobset $jobset.project.name:$jobset.name" %]
-[% PROCESS common.tt %]
-[% USE HTML %]
-[% edit=1 %]
-
-<form class="form-horizontal" action="[% c.uri_for('/jobset' jobset.project.name jobset.name 'clone' 'submit') %]" method="post">
-
-  <fieldset>
-    <div class="control-group">
-      <label class="control-label">New name</label>
-      <div class="controls">
-        <input type="text" class="span3" name="newjobset" value=""/>
-      </div>
-    </div>
-
-    <div class="form-actions">
-      <input type="submit" value="Submit" class="btn btn-primary" />
-    </div>
-
-  </fieldset>
-
-</form>
-
-[% END %]
diff --git a/src/root/edit-jobset.tt b/src/root/edit-jobset.tt
index 16076692..0cc48c89 100644
--- a/src/root/edit-jobset.tt
+++ b/src/root/edit-jobset.tt
@@ -1,4 +1,4 @@
-[% WRAPPER layout.tt title=(create ? "Create jobset in project $project.name" : "Editing jobset $project.name:$jobset.name") %]
+[% WRAPPER layout.tt title=(create ? "Create jobset in project $project.name" : clone ? "Cloning jobset $project.name:$jobset.name" : "Editing jobset $project.name:$jobset.name") %]
 [% PROCESS common.tt %]
 [% USE format %]
 
@@ -64,7 +64,7 @@
     <div class="control-group">
       <label class="control-label">Identifier</label>
       <div class="controls">
-        <input type="text" class="span3" name="name" [% HTML.attributes(value => jobset.name) %]/>
+        <input type="text" class="span3" name="name" [% HTML.attributes(value => clone ? "" : jobset.name) %]/>
       </div>
     </div>
 
@@ -132,7 +132,7 @@
     [% INCLUDE renderJobsetInputs %]
 
     <div class="form-actions">
-      <button id="submit-jobset" type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i> [%IF create %]Create[% ELSE %]Apply changes[% END %]</button>
+      <button id="submit-jobset" type="submit" class="btn btn-primary"><i class="icon-ok icon-white"></i> [%IF create || clone %]Create jobset[% ELSE %]Apply changes[% END %]</button>
     </div>
 
   </fieldset>
@@ -168,7 +168,7 @@
 
   $("#submit-jobset").click(function() {
     requestJSON({
-      [% IF create %]
+      [% IF create || clone %]
         url: "[% c.uri_for('/jobset' project.name '.new') %]",
       [% ELSE %]
         url: "[% c.uri_for('/jobset' project.name jobset.name) %]",
diff --git a/src/root/edit-project.tt b/src/root/edit-project.tt
index 233229ab..58fcfecf 100644
--- a/src/root/edit-project.tt
+++ b/src/root/edit-project.tt
@@ -56,7 +56,7 @@
     <div class="form-actions">
       <button id="submit-project" type="submit" class="btn btn-primary">
         <i class="icon-ok icon-white"></i>
-        [%IF create %]Create[% ELSE %]Apply changes[% END %]
+        [%IF create %]Create project[% ELSE %]Apply changes[% END %]
       </button>
     </div>
 
diff --git a/src/root/jobset.tt b/src/root/jobset.tt
index 54dbfb1e..8bffd227 100644
--- a/src/root/jobset.tt
+++ b/src/root/jobset.tt
@@ -51,7 +51,7 @@
       <ul class="dropdown-menu">
         [% INCLUDE menuItem title="Edit configuration" icon="icon-edit" uri=c.uri_for(c.controller('Jobset').action_for('edit'), [project.name, jobset.name]) %]
         [% INCLUDE menuItem title="Delete this jobset" icon="icon-trash" uri="javascript:deleteJobset()" %]
-        [% INCLUDE menuItem title="Clone this jobset" uri=c.uri_for('/jobset' project.name jobset.name 'clone') %]
+        [% INCLUDE menuItem title="Clone this jobset" uri=c.uri_for(c.controller('Jobset').action_for('edit'), [project.name, jobset.name], { clone => 1 }) %]
         [% INCLUDE menuItem title="Evaluate this jobset" uri="javascript:confirmEvaluateJobset()" %]
       </ul>
     </li>