Merge branch 'master'
authorJunio C Hamano <junkio@cox.net>
Fri, 18 Nov 2005 08:11:28 +0000 (00:11 -0800)
committerJunio C Hamano <junkio@cox.net>
Fri, 18 Nov 2005 08:11:28 +0000 (00:11 -0800)
Documentation/git-am.txt
Makefile
apply.c
date.c
git-am.sh
git-archimport.perl
git-repack.sh
pack-redundant.c

index e4df4a4..1ceed11 100644 (file)
@@ -8,8 +8,8 @@ git-am - Apply a series of patches in a mailbox
 
 SYNOPSIS
 --------
-'git-am' [--signoff] [--dotest=<dir>] [--utf8] [--3way] <mbox>...
-'git-am' [--skip]
+'git-am' [--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way] <mbox>...
+'git-am' [--skip | --resolved]
 
 DESCRIPTION
 -----------
@@ -31,6 +31,10 @@ OPTIONS
        Pass `--utf8` and `--keep` flags to `git-mailinfo` (see
        gitlink:git-mailinfo[1]).
 
+--binary::
+       Pass `--allow-binary-replacement` flag to `git-apply`
+       (see gitlink:git-apply[1]).
+
 --3way::
        When the patch does not apply cleanly, fall back on
        3-way merge, if the patch records the identity of blobs
@@ -44,6 +48,13 @@ OPTIONS
 --interactive::
        Run interactively, just like git-applymbox.
 
+--resolved::
+       After a patch failure (e.g. attempting to apply
+       conflicting patch), the user has applied it by hand and
+       the index file stores the result of the application.
+       Make a commit using the authorship and commit log
+       extracted from the e-mail message and the current index
+       file, and continue.
 
 DISCUSSION
 ----------
@@ -56,12 +67,9 @@ recover from this in one of two ways:
 . skip the current one by re-running the command with '--skip'
   option.
 
-. hand resolve the conflict in the working directory, run 'git
-  diff HEAD' to extract the merge result into a patch form and
-  replacing the patch in .dotest/patch file.  After doing this,
-  run `git-reset --hard HEAD` to bring the working tree to the
-  state before half-applying the patch, then re-run the command
-  without any options.
+. hand resolve the conflict in the working directory, and update
+  the index file to bring it in a state that the patch should
+  have produced.  Then run the command with '--resume' option.
 
 The command refuses to process new mailboxes while `.dotest`
 directory exists, so if you decide to start over from scratch,
index e5cf5ef..819c48c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -466,7 +466,7 @@ deb: dist
 ### Cleaning rules
 
 clean:
-       rm -f *.o mozilla-sha1/*.o ppc/*.o compat/*.o $(PROGRAMS) $(LIB_FILE)
+       rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o git $(PROGRAMS) $(LIB_FILE)
        rm -f $(filter-out gitk,$(SCRIPTS))
        rm -f *.spec *.pyc *.pyo
        rm -rf $(GIT_TARNAME)
diff --git a/apply.c b/apply.c
index 129edb1..50be8f3 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -893,12 +893,24 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
        patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
 
        if (!patchsize) {
-               static const char binhdr[] = "Binary files ";
-
-               if (sizeof(binhdr) - 1 < size - offset - hdrsize &&
-                   !memcmp(binhdr, buffer + hdrsize + offset,
-                           sizeof(binhdr)-1))
-                       patch->is_binary = 1;
+               static const char *binhdr[] = {
+                       "Binary files ",
+                       "Files ",
+                       NULL,
+               };
+               int i;
+               int hd = hdrsize + offset;
+               unsigned long llen = linelen(buffer + hd, size - hd);
+
+               if (!memcmp(" differ\n", buffer + hd + llen - 8, 8))
+                       for (i = 0; binhdr[i]; i++) {
+                               int len = strlen(binhdr[i]);
+                               if (len < size - hd &&
+                                   !memcmp(binhdr[i], buffer + hd, len)) {
+                                       patch->is_binary = 1;
+                                       break;
+                               }
+                       }
 
                /* Empty patch cannot be applied if:
                 * - it is a binary patch and we do not do binary_replace, or
diff --git a/date.c b/date.c
index 73c063b..d2a67cc 100644 (file)
--- a/date.c
+++ b/date.c
@@ -34,7 +34,7 @@ static const char *month_names[] = {
 };
 
 static const char *weekday_names[] = {
-       "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
+       "Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
 };
 
 /*
@@ -531,6 +531,22 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
                tl++;
        }
 
+       for (i = 0; i < 7; i++) {
+               int match = match_string(date, weekday_names[i]);
+               if (match >= 3) {
+                       int diff, n = *num -1;
+                       *num = 0;
+
+                       diff = tm->tm_wday - i;
+                       if (diff <= 0)
+                               n++;
+                       diff += 7*n;
+
+                       update_tm(tm, diff * 24 * 60 * 60);
+                       return end;
+               }
+       }
+
        if (match_string(date, "months") >= 5) {
                int n = tm->tm_mon - *num;
                *num = 0;
index 98a390a..8f073c9 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -4,7 +4,7 @@
 . git-sh-setup || die "Not a git archive"
 
 usage () {
-    echo >&2 "usage: $0 [--signoff] [--dotest=<dir>] [--utf8] [--3way] <mbox>"
+    echo >&2 "usage: $0 [--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way] <mbox>"
     echo >&2 " or, when resuming"
     echo >&2 " $0 [--skip | --resolved]"
     exit 1;
@@ -40,7 +40,7 @@ fall_back_3way () {
            cd "$dotest/patch-merge-tmp-dir" &&
            GIT_INDEX_FILE="../patch-merge-tmp-index" \
            GIT_OBJECT_DIRECTORY="$O_OBJECT" \
-           git-apply --index <../patch
+           git-apply $binary --index <../patch
         )
     then
        echo Using index info to reconstruct a base tree...
@@ -71,7 +71,7 @@ fall_back_3way () {
                GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
                export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
                git-read-tree "$base" &&
-               git-apply --index &&
+               git-apply $binary --index &&
                mv ../patch-merge-tmp-index ../patch-merge-index &&
                echo "$base" >../patch-merge-base
            ) <"$dotest/patch"  2>/dev/null && break
@@ -98,7 +98,7 @@ fall_back_3way () {
 }
 
 prec=4
-dotest=.dotest sign= utf8= keep= skip= interactive= resolved=
+dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary=
 
 while case "$#" in 0) break;; esac
 do
@@ -113,6 +113,9 @@ do
        --interacti|--interactiv|--interactive)
        interactive=t; shift ;;
 
+       -b|--b|--bi|--bin|--bina|--binar|--binary)
+       binary=t; shift ;;
+
        -3|--3|--3w|--3wa|--3way)
        threeway=t; shift ;;
        -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
@@ -169,9 +172,10 @@ else
                exit 1
        }
 
-       # -s, -u and -k flags are kept for the resuming session after
+       # -b, -s, -u and -k flags are kept for the resuming session after
        # a patch failure.
        # -3 and -i can and must be given when resuming.
+       echo "$binary" >"$dotest/binary"
        echo "$sign" >"$dotest/sign"
        echo "$utf8" >"$dotest/utf8"
        echo "$keep" >"$dotest/keep"
@@ -187,6 +191,10 @@ case "$resolved" in
        fi
 esac
 
+if test "$(cat "$dotest/binary")" = t
+then
+       binary=--allow-binary-replacement
+fi
 if test "$(cat "$dotest/utf8")" = t
 then
        utf8=-u
@@ -339,7 +347,7 @@ do
 
        case "$resolved" in
        '')
-               git-apply --index "$dotest/patch"
+               git-apply $binary --index "$dotest/patch"
                apply_status=$?
                ;;
        t)
index 23becb7..c3bed08 100755 (executable)
@@ -30,6 +30,24 @@ See man (1) git-archimport for more details.
 
 Add print in front of the shell commands invoked via backticks. 
 
+=head1 Devel Notes
+
+There are several places where Arch and git terminology are intermixed
+and potentially confused.
+
+The notion of a "branch" in git is approximately equivalent to
+a "archive/category--branch--version" in Arch.  Also, it should be noted
+that the "--branch" portion of "archive/category--branch--version" is really
+optional in Arch although not many people (nor tools!) seem to know this.
+This means that "archive/category--version" is also a valid "branch"
+in git terms.
+
+We always refer to Arch names by their fully qualified variant (which
+means the "archive" name is prefixed.
+
+For people unfamiliar with Arch, an "archive" is the term for "repository",
+and can contain multiple, unrelated branches.
+
 =cut
 
 use strict;
@@ -52,14 +70,14 @@ $ENV{'TZ'}="UTC";
 
 my $git_dir = $ENV{"GIT_DIR"} || ".git";
 $ENV{"GIT_DIR"} = $git_dir;
+my $ptag_dir = "$git_dir/archimport/tags";
 
-our($opt_h,$opt_v, $opt_T,
-    $opt_C,$opt_t);
+our($opt_h,$opt_v, $opt_T,$opt_t,$opt_o);
 
 sub usage() {
     print STDERR <<END;
 Usage: ${\basename $0}     # fetch/update GIT from Arch
-       [ -h ] [ -v ] [ -T ] [ -t tempdir ] 
+       [ -o ] [ -h ] [ -v ] [ -T ] [ -t tempdir ] 
        repository/arch-branch [ repository/arch-branch] ...
 END
     exit(1);
@@ -195,25 +213,68 @@ unless (-d $git_dir) { # initial import
     opendir(DIR, "$git_dir/archimport/tags")
        || die "can't opendir: $!";
     while (my $file = readdir(DIR)) {
-       # skip non-interesting-files
-       next unless -f "$git_dir/archimport/tags/$file";
-       next if     $file =~ m/--base-0$/; # don't care for base-0
+        # skip non-interesting-files
+        next unless -f "$ptag_dir/$file";
+   
+        # convert first '--' to '/' from old git-archimport to use
+        # as an archivename/c--b--v private tag
+        if ($file !~ m!,!) {
+            my $oldfile = $file;
+            $file =~ s!--!,!;
+            print STDERR "converting old tag $oldfile to $file\n";
+            rename("$ptag_dir/$oldfile", "$ptag_dir/$file") or die $!;
+        }
        my $sha = ptag($file);
        chomp $sha;
-       # reconvert the 3rd '--' sequence from the end
-       # into a slash
-       # $file = reverse $file;
-       # $file =~ s!^(.+?--.+?--.+?--.+?)--(.+)$!$1/$2!;
-       # $file = reverse $file;
        $rptags{$sha} = $file;
     }
     closedir DIR;
 }
 
 # process patchsets
-foreach my $ps (@psets) {
+# extract the Arch repository name (Arch "archive" in Arch-speak)
+sub extract_reponame {
+    my $fq_cvbr = shift; # archivename/[[[[category]branch]version]revision]
+    return (split(/\//, $fq_cvbr))[0];
+}
+sub extract_versionname {
+    my $name = shift;
+    $name =~ s/--(?:patch|version(?:fix)?|base)-\d+$//;
+    return $name;
+}
+
+# convert a fully-qualified revision or version to a unique dirname:
+#   normalperson@yhbt.net-05/mpd--uclinux--1--patch-2 
+# becomes: normalperson@yhbt.net-05,mpd--uclinux--1
+#
+# the git notion of a branch is closer to
+# archive/category--branch--version than archive/category--branch, so we
+# use this to convert to git branch names.
+# Also, keep archive names but replace '/' with ',' since it won't require
+# subdirectories, and is safer than swapping '--' which could confuse
+# reverse-mapping when dealing with bastard branches that
+# are just archive/category--version  (no --branch)
+sub tree_dirname {
+    my $revision = shift;
+    my $name = extract_versionname($revision);
+    $name =~ s#/#,#;
+    return $name;
+}
 
-    $ps->{branch} =  branchname($ps->{id});
+# old versions of git-archimport just use the <category--branch> part:
+sub old_style_branchname {
+    my $id = shift;
+    my $ret = safe_pipe_capture($TLA,'parse-package-name','-p',$id);
+    chomp $ret;
+    return $ret;
+}
+
+*git_branchname = $opt_o ? *old_style_branchname : *tree_dirname;
+
+# process patchsets
+foreach my $ps (@psets) {
+    $ps->{branch} = git_branchname($ps->{id});
 
     #
     # ensure we have a clean state 
@@ -424,16 +485,9 @@ foreach my $ps (@psets) {
     $opt_v && print "   + parents:  $par \n";
 }
 
-sub branchname {
-    my $id = shift;
-    $id =~ s#^.+?/##;
-    my @parts = split(m/--/, $id);
-    return join('--', @parts[0..1]);
-}
-
 sub apply_import {
     my $ps = shift;
-    my $bname = branchname($ps->{id});
+    my $bname = git_branchname($ps->{id});
 
     `mkdir -p $tmp`;
 
@@ -581,19 +635,24 @@ sub parselog {
 # write/read a tag
 sub tag {
     my ($tag, $commit) = @_;
-    $tag =~ s|/|--|g; 
-    $tag = shell_quote($tag);
+    if ($opt_o) {
+        $tag =~ s|/|--|g;
+    } else {
+        # don't use subdirs for tags yet, it could screw up other porcelains
+        $tag =~ s|/|,|g;
+    }
     
     if ($commit) {
-        open(C,">$git_dir/refs/tags/$tag")
+        open(C,">","$git_dir/refs/tags/$tag")
             or die "Cannot create tag $tag: $!\n";
         print C "$commit\n"
             or die "Cannot write tag $tag: $!\n";
         close(C)
             or die "Cannot write tag $tag: $!\n";
-        print " * Created tag ' $tag' on '$commit'\n" if $opt_v;
+        print " * Created tag '$tag' on '$commit'\n" if $opt_v;
     } else {                    # read
-        open(C,"<$git_dir/refs/tags/$tag")
+        open(C,"<","$git_dir/refs/tags/$tag")
             or die "Cannot read tag $tag: $!\n";
         $commit = <C>;
         chomp $commit;
@@ -608,15 +667,16 @@ sub tag {
 # reads fail softly if the tag isn't there
 sub ptag {
     my ($tag, $commit) = @_;
-    $tag =~ s|/|--|g; 
-    $tag = shell_quote($tag);
+
+    # don't use subdirs for tags yet, it could screw up other porcelains
+    $tag =~ s|/|,|g; 
     
-    unless (-d "$git_dir/archimport/tags") {
-        mkpath("$git_dir/archimport/tags");
-    }
+    my $tag_file = "$ptag_dir/$tag";
+    my $tag_branch_dir = dirname($tag_file);
+    mkpath($tag_branch_dir) unless (-d $tag_branch_dir);
 
     if ($commit) {              # write
-        open(C,">$git_dir/archimport/tags/$tag")
+        open(C,">",$tag_file)
             or die "Cannot create tag $tag: $!\n";
         print C "$commit\n"
             or die "Cannot write tag $tag: $!\n";
@@ -626,10 +686,10 @@ sub ptag {
            unless $tag =~ m/--base-0$/;
     } else {                    # read
         # if the tag isn't there, return 0
-        unless ( -s "$git_dir/archimport/tags/$tag") {
+        unless ( -s $tag_file) {
             return 0;
         }
-        open(C,"<$git_dir/archimport/tags/$tag")
+        open(C,"<",$tag_file)
             or die "Cannot read tag $tag: $!\n";
         $commit = <C>;
         chomp $commit;
@@ -662,7 +722,7 @@ sub find_parents {
     # simple loop to split the merges
     # per branch
     foreach my $merge (@{$ps->{merges}}) {
-       my $branch = branchname($merge);
+       my $branch = git_branchname($merge);
        unless (defined $branches{$branch} ){
            $branches{$branch} = [];
        }
@@ -686,7 +746,13 @@ sub find_parents {
        next unless -e "$git_dir/refs/heads/$branch";
 
        my $mergebase = `git-merge-base $branch $ps->{branch}`;
-       die "Cannot find merge base for $branch and $ps->{branch}" if $?;
+       if ($?) { 
+           # Don't die here, Arch supports one-way cherry-picking
+           # between branches with no common base (or any relationship
+           # at all beforehand)
+           warn "Cannot find merge base for $branch and $ps->{branch}";
+           next;
+       }
        chomp $mergebase;
 
        # now walk up to the mergepoint collecting what patches we have
@@ -779,12 +845,7 @@ sub commitid2pset {
     chomp $commitid;
     my $name = $rptags{$commitid} 
        || die "Cannot find reverse tag mapping for $commitid";
-    # the keys in %rptag  are slightly munged; unmunge
-    # reconvert the 3rd '--' sequence from the end
-    # into a slash
-    $name = reverse $name;
-    $name =~ s!^(.+?--.+?--.+?--.+?)--(.+)$!$1/$2!;
-    $name = reverse $name;
+    $name =~ s|,|/|;
     my $ps   = $psets{$name} 
        || (print Dumper(sort keys %psets)) && die "Cannot find patchset for $name";
     return $ps;
index f347207..e58fdd6 100755 (executable)
@@ -11,7 +11,7 @@ do
        case "$1" in
        -n)     no_update_info=t ;;
        -a)     all_into_one=t ;;
-       -d)     remove_redandant=t ;;
+       -d)     remove_redundant=t ;;
        -l)     local=t ;;
        *)      break ;;
        esac
@@ -42,7 +42,7 @@ name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) |
        exit 1
 if [ -z "$name" ]; then
        echo Nothing new to pack.
-       if test "$remove_redandant" = t ; then
+       if test "$remove_redundant" = t ; then
                echo "Removing redundant packs."
                sync
                redundant=$(git-pack-redundant --all)
@@ -60,7 +60,7 @@ mv .tmp-pack-$name.pack "$PACKDIR/pack-$name.pack" &&
 mv .tmp-pack-$name.idx  "$PACKDIR/pack-$name.idx" ||
 exit
 
-if test "$remove_redandant" = t
+if test "$remove_redundant" = t
 then
        sync
        redundant=$(git-pack-redundant --all)
index fcb36ff..51d7341 100644 (file)
@@ -33,6 +33,7 @@ struct pack_list {
 struct pll {
        struct pll *next;
        struct pack_list *pl;
+       size_t pl_size;
 };
 
 inline void llist_free(struct llist *list)
@@ -249,18 +250,45 @@ void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
        }
 }
 
+void pll_insert(struct pll **pll, struct pll **hint_table)
+{
+       struct pll *prev;
+       int i = (*pll)->pl_size - 1;
+
+       if (hint_table[i] == NULL) {
+               hint_table[i--] = *pll;
+               for (; i >= 0; --i) {
+                       if (hint_table[i] != NULL)
+                               break;
+               }
+               if (hint_table[i] == NULL) /* no elements in list */
+                       die("Why did this happen?");
+       }
+
+       prev = hint_table[i];
+       while (prev->next && prev->next->pl_size < (*pll)->pl_size)
+               prev = prev->next;
+
+       (*pll)->next = prev->next;
+       prev->next = *pll;
+}
+
 /* all the permutations have to be free()d at the same time,
  * since they refer to each other
  */
 struct pll * get_all_permutations(struct pack_list *list)
 {
        struct pll *subset, *pll, *new_pll = NULL; /*silence warning*/
-
+       static struct pll **hint = NULL;
+       if (hint == NULL)
+               hint = xcalloc(pack_list_size(list), sizeof(struct pll *));
+               
        if (list == NULL)
                return NULL;
 
        if (list->next == NULL) {
                new_pll = xmalloc(sizeof(struct pll));
+               hint[0] = new_pll;
                new_pll->next = NULL;
                new_pll->pl = list;
                return new_pll;
@@ -268,24 +296,30 @@ struct pll * get_all_permutations(struct pack_list *list)
 
        pll = subset = get_all_permutations(list->next);
        while (pll) {
+               if (pll->pl->pack == list->pack) {
+                       pll = pll->next;
+                       continue;
+               }
                new_pll = xmalloc(sizeof(struct pll));
-               new_pll->next = pll->next;
-               pll->next = new_pll;
 
                new_pll->pl = xmalloc(sizeof(struct pack_list));
                memcpy(new_pll->pl, list, sizeof(struct pack_list));
                new_pll->pl->next = pll->pl;
+               new_pll->pl_size = pll->pl_size + 1;
+               
+               pll_insert(&new_pll, hint);
 
-               pll = new_pll->next;
+               pll = pll->next;
        }
-       /* add ourself to the end */
-       new_pll->next = xmalloc(sizeof(struct pll));
-       new_pll->next->pl = xmalloc(sizeof(struct pack_list));
-       new_pll->next->next = NULL;
-       memcpy(new_pll->next->pl, list, sizeof(struct pack_list));
-       new_pll->next->pl->next = NULL;
-
-       return subset;
+       /* add ourself */
+       new_pll = xmalloc(sizeof(struct pll));
+       new_pll->pl = xmalloc(sizeof(struct pack_list));
+       memcpy(new_pll->pl, list, sizeof(struct pack_list));
+       new_pll->pl->next = NULL;
+       new_pll->pl_size = 1;
+       pll_insert(&new_pll, hint);
+
+       return hint[0];
 }
 
 int is_superset(struct pack_list *pl, struct llist *list)
@@ -401,6 +435,8 @@ void minimize(struct pack_list **min)
        /* find the permutations which contain all missing objects */
        perm_all = perm = get_all_permutations(non_unique);
        while (perm) {
+               if (perm_ok && perm->pl_size > perm_ok->pl_size)
+                       break; /* ignore all larger permutations */
                if (is_superset(perm->pl, missing)) {
                        new_perm = xmalloc(sizeof(struct pll));
                        new_perm->pl = perm->pl;