diff options
author | Junio C Hamano <junkio@cox.net> | 2005-11-18 00:11:28 -0800 |
---|---|---|
committer | Junio C Hamano <junkio@cox.net> | 2005-11-18 00:11:28 -0800 |
commit | 6eb668df76a5b46bd24c25d227bfdba12542b05a (patch) | |
tree | efbfbad73fbbb0a5a363bdaf30a2e3d3dfe0abc7 | |
parent | 1e9eb2e93747d2ee3fb44a5a2c8a48e7d19819b9 (diff) | |
parent | 087b6742fcab74f7a53626b98969bed27d288e2b (diff) | |
download | git-6eb668df76a5b46bd24c25d227bfdba12542b05a.tar.gz git-6eb668df76a5b46bd24c25d227bfdba12542b05a.tar.xz |
Merge branch 'master'
-rw-r--r-- | Documentation/git-am.txt | 24 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | apply.c | 24 | ||||
-rw-r--r-- | date.c | 18 | ||||
-rwxr-xr-x | git-am.sh | 20 | ||||
-rwxr-xr-x | git-archimport.perl | 145 | ||||
-rwxr-xr-x | git-repack.sh | 6 | ||||
-rw-r--r-- | pack-redundant.c | 60 |
8 files changed, 220 insertions, 79 deletions
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index e4df4a46e..1ceed112f 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=<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, @@ -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) @@ -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 @@ -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; @@ -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) diff --git a/git-archimport.perl b/git-archimport.perl index 23becb796..c3bed0808 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 <<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; diff --git a/git-repack.sh b/git-repack.sh index f34720701..e58fdd6d8 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 fcb36ff90..51d7341b0 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; |