]> git.notmuchmail.org Git - notmuch/blobdiff - devel/nmbug/nmbug
nmbug: Add 'clone' and replace FETCH_HEAD with @{upstream}
[notmuch] / devel / nmbug / nmbug
index fe103b3b7c5ac2794733e3cff3f2e2ffbb4901f4..d6f5213a08cf391ef3f43b3274dc87fce9eb905d 100755 (executable)
@@ -13,10 +13,7 @@ my $NMBGIT = $ENV{NMBGIT} || $ENV{HOME}.'/.nmbug';
 
 $NMBGIT .= '/.git' if (-d $NMBGIT.'/.git');
 
-my $TAGPREFIX = $ENV{NMBPREFIX} || 'notmuch::';
-
-# magic hash for git
-my $EMPTYBLOB = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391';
+my $TAGPREFIX = defined($ENV{NMBPREFIX}) ? $ENV{NMBPREFIX} : 'notmuch::';
 
 # for encoding
 
@@ -29,6 +26,7 @@ my $ESCAPED_RX =      qr{$ESCAPE_CHAR([A-Fa-f0-9]{2})};
 my %command = (
             archive    => \&do_archive,
             checkout   => \&do_checkout,
+            clone      => \&do_clone,
             commit     => \&do_commit,
             fetch      => \&do_fetch,
             help       => \&do_help,
@@ -39,12 +37,20 @@ my %command = (
             status     => \&do_status,
             );
 
+# Convert prefix into form suitable for literal matching against
+# notmuch dump --format=batch-tag output.
+my $ENCPREFIX = encode_for_fs ($TAGPREFIX);
+$ENCPREFIX =~ s/:/%3a/g;
+
 my $subcommand = shift || usage ();
 
 if (!exists $command{$subcommand}) {
   usage ();
 }
 
+# magic hash for git
+my $EMPTYBLOB = git (qw{hash-object -t blob /dev/null});
+
 &{$command{$subcommand}}(@ARGV);
 
 sub git_pipe {
@@ -120,6 +126,15 @@ sub do_archive {
   system ('git', "--git-dir=$NMBGIT", 'archive', 'HEAD');
 }
 
+sub do_clone {
+  my $repository = shift;
+
+  my $tempwork = tempdir ('/tmp/nmbug-clone.XXXXXX', CLEANUP => 1);
+  system ('git', 'clone', '--no-checkout', '--separate-git-dir', $NMBGIT,
+          $repository, $tempwork) == 0
+    or die "'git clone' exited with nonzero value\n";
+  git ('config', '--unset', 'core.worktree');
+}
 
 sub is_committed {
   my $status = shift;
@@ -203,9 +218,9 @@ sub index_tags {
 
   my $index = $NMBGIT.'/nmbug.index';
 
-  my $query = join ' ', map ("tag:$_", get_tags ($TAGPREFIX));
+  my $query = join ' ', map ("tag:\"$_\"", get_tags ($TAGPREFIX));
 
-  my $fh = spawn ('-|', qw/notmuch dump --/, $query)
+  my $fh = spawn ('-|', qw/notmuch dump --format=batch-tag --/, $query)
     or die "notmuch dump: $!";
 
   git ('read-tree', '--empty');
@@ -214,22 +229,30 @@ sub index_tags {
     or die 'git update-index';
 
   while (<$fh>) {
-    m/ ( [^ ]* ) \s+ \( ([^\)]* ) \) /x || die 'syntax error in dump';
-    my ($id,$rest) = ($1,$2);
 
-    #strip prefixes before writing
-    my @tags = grep { s/^$TAGPREFIX//; } split (' ', $rest);
+    chomp();
+    my ($rest,$id) = split(/ -- id:/);
+
+    if ($id =~ s/^"(.*)"\s*$/$1/) {
+      # xapian quoted string, dequote.
+      $id =~ s/""/"/g;
+    }
+
+    #strip prefixes from tags before writing
+    my @tags = grep { s/^[+]$ENCPREFIX//; } split (' ', $rest);
     index_tags_for_msg ($git,$id, 'A', @tags);
   }
   unless (close $git) {
     die "'git update-index --index-info' exited with nonzero value\n";
   }
   unless (close $fh) {
-    die "'notmuch dump -- $query' exited with nonzero value\n";
+    die "'notmuch dump --format=batch-tag -- $query' exited with nonzero value\n";
   }
   return $index;
 }
 
+# update the git index to either create or delete an empty file.
+# Neither argument should be encoded/escaped.
 sub index_tags_for_msg {
   my $fh = shift;
   my $msgid = shift;
@@ -254,6 +277,20 @@ sub do_checkout {
   do_sync (action => 'checkout');
 }
 
+sub quote_for_xapian {
+  my $str = shift;
+  $str =~ s/"/""/g;
+  return '"' . $str . '"';
+}
+
+sub pair_to_batch_line {
+  my ($action, $pair) = @_;
+
+  # the tag should already be suitably encoded
+
+  return $action . $ENCPREFIX . $pair->{tag} .
+    ' -- id:' . quote_for_xapian ($pair->{id})."\n";
+}
 
 sub do_sync {
 
@@ -270,17 +307,20 @@ sub do_sync {
     $D_action = '-';
   }
 
-  foreach my $pair (@{$status->{added}}) {
+  my $notmuch = spawn ({}, '|-', qw/notmuch tag --batch/)
+    or die 'notmuch tag --batch';
 
-    notmuch ('tag', $A_action.$TAGPREFIX.$pair->{tag},
-            'id:'.$pair->{id});
+  foreach my $pair (@{$status->{added}}) {
+    print $notmuch pair_to_batch_line ($A_action, $pair);
   }
 
   foreach my $pair (@{$status->{deleted}}) {
-    notmuch ('tag', $D_action.$TAGPREFIX.$pair->{tag},
-            'id:'.$pair->{id});
+    print $notmuch pair_to_batch_line ($D_action, $pair);
   }
 
+  unless (close $notmuch) {
+    die "'notmuch tag --batch' exited with nonzero value\n";
+  }
 }
 
 
@@ -302,21 +342,24 @@ To discard your changes,  run 'nmbug checkout'
 
 sub do_pull {
   my $remote = shift || 'origin';
+  my $branch = shift || 'master';
 
   git ( 'fetch', $remote);
 
-  do_merge ();
+  do_merge ("$remote/$branch");
 }
 
 
 sub do_merge {
+  my $commit = shift || '@{upstream}';
+
   insist_committed ();
 
   my $tempwork = tempdir ('/tmp/nmbug-merge.XXXXXX', CLEANUP => 1);
 
   git ( { GIT_WORK_TREE => $tempwork }, 'checkout', '-f', 'HEAD');
 
-  git ( { GIT_WORK_TREE => $tempwork }, 'merge', 'FETCH_HEAD');
+  git ( { GIT_WORK_TREE => $tempwork }, 'merge', $commit);
 
   do_checkout ();
 }
@@ -377,11 +420,10 @@ sub do_status {
 
 
 sub is_unmerged {
+  my $commit = shift || '@{upstream}';
 
-  return 0 if (! -f $NMBGIT.'/FETCH_HEAD');
-
-  my $fetch_head = git ('rev-parse', 'FETCH_HEAD');
-  my $base = git ( 'merge-base', 'HEAD', 'FETCH_HEAD');
+  my $fetch_head = git ('rev-parse', $commit);
+  my $base = git ( 'merge-base', 'HEAD', $commit);
 
   return ($base ne $fetch_head);
 
@@ -443,7 +485,7 @@ sub diff_index {
 sub diff_refs {
   my $filter = shift;
   my $ref1 = shift || 'HEAD';
-  my $ref2 = shift || 'FETCH_HEAD';
+  my $ref2 = shift || '@{upstream}';
 
   my $fh= git_pipe ( 'diff', "--diff-filter=$filter", '--name-only',
                 $ref1, $ref2);
@@ -531,10 +573,11 @@ git. Any extra arguments are used (one per line) as a commit message.
 
 push local nmbug git state to remote repo
 
-=item  B<pull> [remote]
+=item  B<pull> [remote] [branch]
 
 pull (merge) remote repo changes to notmuch. B<pull> is equivalent to
-B<fetch> followed by B<merge>.
+B<fetch> followed by B<merge>.  The default remote is C<origin>, and
+the default branch is C<master>.
 
 =back
 
@@ -542,6 +585,12 @@ B<fetch> followed by B<merge>.
 
 =over 8
 
+=item B<clone> repository
+
+Create a local nmbug repository from a remote source.  This wraps
+C<git clone>, adding some options to avoid creating a working tree
+while preserving remote-tracking branches and upstreams.
+
 =item B<checkout>
 
 Update the notmuch database from git. This is mainly useful to discard
@@ -559,12 +608,12 @@ print help [for subcommand]
 =item B<log> [parameters]
 
 A simple wrapper for git log. After running C<nmbug fetch>, you can
-inspect the changes with C<nmbug log HEAD..FETCH_HEAD>
+inspect the changes with C<nmbug log HEAD..@{upstream}>
 
-=item B<merge>
+=item B<merge> [commit]
 
-Merge changes from FETCH_HEAD into HEAD, and load the result into
-notmuch.
+Merge changes from C<commit> into HEAD, and load the result into
+notmuch.  The default commit is C<@{upstream}>.
 
 =item  B<status>