diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index e4df4a46ec..1ceed112f2 100644 --- a/Documentation/git-am.txt +++ b/Documentation/git-am.txt @@ -8,8 +8,8 @@ git-am - Apply a series of patches in a mailbox SYNOPSIS -------- -'git-am' [--signoff] [--dotest=] [--utf8] [--3way] ... -'git-am' [--skip] +'git-am' [--signoff] [--dotest=] [--utf8] [--binary] [--3way] ... +'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, diff --git a/Makefile b/Makefile index e5cf5ef473..819c48ce8b 100644 --- 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 129edb1889..50be8f3e22 100644 --- 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 73c063b9ab..d2a67ccf07 100644 --- 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; diff --git a/git-am.sh b/git-am.sh index 98a390ab45..8f073c90f6 100755 --- 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=] [--utf8] [--3way] " + echo >&2 "usage: $0 [--signoff] [--dotest=] [--utf8] [--binary] [--3way] " 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) diff --git a/git-archimport.perl b/git-archimport.perl index 23becb7962..c3bed08086 100755 --- a/git-archimport.perl +++ b/git-archimport.perl @@ -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 <{branch} = branchname($ps->{id}); +# old versions of git-archimport just use the 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 = ; 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 = ; 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; diff --git a/git-repack.sh b/git-repack.sh index f34720701b..e58fdd6d87 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -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) diff --git a/pack-redundant.c b/pack-redundant.c index fcb36ff901..51d7341b0b 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -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;