aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/git-am.txt24
-rw-r--r--Makefile2
-rw-r--r--apply.c24
-rw-r--r--date.c18
-rwxr-xr-xgit-am.sh20
-rwxr-xr-xgit-archimport.perl145
-rwxr-xr-xgit-repack.sh6
-rw-r--r--pack-redundant.c60
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,
diff --git a/Makefile b/Makefile
index e5cf5ef47..819c48ce8 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 129edb188..50be8f3e2 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 73c063b9a..d2a67ccf0 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 98a390ab4..8f073c90f 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=<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;