* Re: Retroactively change email signature?
2008-01-02 21:24 ` Linus Torvalds
@ 2008-01-02 22:06 ` Stephen Sinclair
2008-01-03 0:53 ` David Brown
2008-01-03 5:52 ` Retroactively change email signature? [resend] Sam Vilain
1 sibling, 1 reply; 9+ messages in thread
From: Stephen Sinclair @ 2008-01-02 22:06 UTC (permalink / raw)
To: git
Thanks, I was able to use git-filter-branch to accomplish what I wanted.
I should have mentioned in the original email that it is not a widely
distributed repo, so I'm not too worried about breaking things at this
point. I guess for a larger project it might be a problem, so I
probably just wouldn't do it.
> BUT! See also the big bold-face warning: using "filter-branch" will not
> change old commits, it will create a series of *new* commits that bear a
> striking resemblance to the old ones, but are not the same. So you'll have
> a totally new history.
Got it, thanks.
I'll just have to remember to git-config properly next time I do this.
One thing that's a bit odd for me is that I use a different email
address for this particular project..
Conveniently I'm able to use git-config to set an email address
locally for this repo, however I have to remember to do this every
time I clone it somewhere. Not a problem, necessarily, but perhaps
just a matter of training myself.
For example, I have a clone of this repo on a Linux, Windows, and OS X
machine, and also another clone for each on my laptop.. git is
actually turning out to be great for doing all this swapping between
machines, but it does take a little training to remember to set the
configuration, since there's no central repo to ask me for my username
and password before I can commit.
cheers,
Steve
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Retroactively change email signature? [resend]
2008-01-02 21:24 ` Linus Torvalds
2008-01-02 22:06 ` Stephen Sinclair
@ 2008-01-03 5:52 ` Sam Vilain
2008-01-04 18:34 ` Linus Torvalds
1 sibling, 1 reply; 9+ messages in thread
From: Sam Vilain @ 2008-01-03 5:52 UTC (permalink / raw)
To: Junio Hamano; +Cc: git
Linus Torvalds wrote:
> (I don't think git-filter-branch even exposes any easy way to remap the
> "revert commit xyz" messages in the message format. It's not technically
> hard to use a --msg-filter for it, but it seems a big bother).
Assuming, of course, that the commit you refer to in a message like that
is in your history. If it's referring to another commit in a different
branch, which may be being re-written, you've got two problems:
1. rewriting the commit
2. making sure that git-filter-branch processes the two commits in the
right order.
The first is easily fixable; the second requires a hook in
git-filter-branch to customize the tournament.
Subject: [PATCH] git-filter-branch: allow a custom tournament for rewrite ordering
The topological sort by git rev-list may be inconvenient for history
modification where commit IDs are used in comments to refer to other
changes. So, allow for a program that can take a list of commits, and
return that list in the order that the rewriter must follow. Borrow
"Tournament" term from Graph Theory.
Signed-off-by: Sam Vilain <sam.vilain@catalyst.net.nz>
---
Documentation/git-filter-branch.txt | 18 ++++
contrib/filter-branch/msg-links.perl | 50 +++++++++
contrib/filter-branch/tournament-links.perl | 148 +++++++++++++++++++++++++++
git-filter-branch.sh | 11 ++
t/t7003-filter-branch.sh | 21 ++++
5 files changed, 248 insertions(+), 0 deletions(-)
create mode 100755 contrib/filter-branch/msg-links.perl
create mode 100755 contrib/filter-branch/tournament-links.perl
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index 895d750..ed3a839 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -118,6 +118,24 @@ convenience functions, too. For example, calling 'skip_commit "$@"'
will leave out the current commit (but not its changes! If you want
that, use gitlink:git-rebase[1] instead).
+--tournament <command>::
+ This command is responsible for re-ordering the commit history
+ after it is retrieved from the input list. It is passed on
+ standard input the deduced list based upon a regular
+ topological search, and the command is expected to print to
+ standard output a list in the same form.
++
+Again, this can't be used to re-order patches like
+gitlink:git-rebase[1] does, as the tree IDs of the original positions
+will remain untouched, so the differences between revisions will be
+scrambled.
++
+However, it is required when you need to refer to a commit by ID in a
+commit message, where the commit you are referring to is on an
+unrelated branch. See the contributed filters in the Git distribution
+for an example script that can let you preserve such referential
+integrity during a rewrite.
+
--tag-name-filter <command>::
This is the filter for rewriting tag names. When passed,
it will be called for every tag ref that points to a rewritten
diff --git a/contrib/filter-branch/msg-links.perl b/contrib/filter-branch/msg-links.perl
new file mode 100755
index 0000000..1ff780c
--- /dev/null
+++ b/contrib/filter-branch/msg-links.perl
@@ -0,0 +1,50 @@
+#!/usr/bin/perl -nl
+#
+# msg-links.perl
+#
+# This is the companion script to tournament-links.perl, it is
+# configured in the same way. If you want this script, you will
+# probably also want the other one.
+#
+# What it does is rewrites all commit IDs of at least 12 digits long
+# to the post-rewritten version. Note that this works only for
+# commits seen by the current git-filter-branch run! If you need to
+# correct "dead" links from a previous run, then supply the rewrite
+# file.
+#
+# You don't need the tournament filter if all of the commits IDs you
+# are changing to are "ancient" history, or if you are rewriting
+# history that is a straight line.
+
+no strict;
+no warnings;
+
+use constant REV_MIN_LENGTH => $ENV{MSG_LINK_MIN} || 12;
+
+BEGIN {
+ $mlr = "$ENV{GITDIR}/msg-link-rewrite";
+ if ( -e $mlr ) {
+ open FH, "<", $mlr or die $!;
+ local($/);
+ %remap = m{([0-9a-f]{40})}g while <FH>;
+ }
+ ( -d "../map" ) or die "can't see the git-filter-branch map dir";
+}
+
+my $old = $_;
+s{([0-9a-f]{${\(REV_MIN_LENGTH)},40})}{substr(($remap{$1}?do {
+ print STDERR " Remapped: $1 => $remap{$1} (rule)\n";
+ $remap{$1}; }
+ : $1), 0, length($1))}eg;
+
+s{([0-9a-f]{${\(REV_MIN_LENGTH)},40})}{substr((-e "../map/$1" ? do {
+ $x = `cat ../map/$1`; chomp($x);
+ print STDERR " Remapped: $1 => $x (ripple)\n";
+ $x}
+: $1),0,length($1))}eg;
+
+if ( $old ne $_ ) {
+ print STDERR "*** filtered: pre: \n$old\n post:\n$_\n";
+}
+
+print;
diff --git a/contrib/filter-branch/tournament-links.perl b/contrib/filter-branch/tournament-links.perl
new file mode 100755
index 0000000..479a28f
--- /dev/null
+++ b/contrib/filter-branch/tournament-links.perl
@@ -0,0 +1,148 @@
+#!/usr/bin/perl
+#
+# tournament-links.perl: a tournament that allows simple re-ordering.
+#
+# This provides the requirement of getting the commits in the right
+# order if there are any (full length) commitids in the commit
+# messages. It also provides for some remapping along the way (if,
+# say, a referenced commit ID was wrong). To do that, put it in a
+# file in GIT_DIR called "msg-link-rewrite".
+#
+# The format of msg-link-rewrite is very simple - any full-length
+# SHA1s that are seen in the file are put into a single hash. So,
+# every other item is a key, starting with the first. Everything
+# else in the file is ignored. This is combined with the remapping
+# that the current git-filter-branch run has performed.
+#
+# This script is only called once, at the beginning of the run, and
+# can be relatively expensive on this basis. This one is practically
+# guaranteed to feed quicksort it's worst nightmare every time.
+#
+# Example:
+# $ cat > .git/msg-link-rewrite <<EOF
+# Rewrite the commit ID 123456789012 to
+# '92712632981923'
+# EOF
+# $ path=$ENV{HOME}/src/git/contrib/filter-branch
+# $ git-filter-branch --tournament $path/tournament.perl \
+# --msg-filter $path/msg-links.perl
+#
+
+no strict;
+no warnings;
+# use sort '_mergesort'; # Perl 5.10+ - use a mergesort instead
+
+use constant WATCH_BATTLE => $ENV{WATCH_BATTLE};
+use constant TOURNAMENT_RESULTS => $ENV{TOURNAMENT_RESULTS};
+use constant REV_MIN_LENGTH => $ENV{MSG_LINK_MIN} || 12;
+
+BEGIN {
+ $mlr = "$ENV{GITDIR}/msg-link-rewrite";
+ if ( -e $mlr ) {
+ open FH, "<", $mlr or die $!;
+ local($/);
+ %remap = m{([0-9a-f]{${\(REV_MIN_LENGTH)},40})}g while <FH>;
+ }
+ ( -d "../map" ) or die "can't see the git-filter-branch map dir";
+}
+
+@commits = <>;
+%refers;
+
+# shorten a commit ID to something human-readable for display to
+# STDERR only
+sub n{
+ my $commitid = shift;
+ substr($commitid, 0, 12);
+}
+
+print STDERR "tournament: read ".@commits." commit IDs\n"
+ if TOURNAMENT_RESULTS;
+
+for my $line ( @commits ) {
+ chomp($line);
+ my ($commit, @parents) = split " ", $line;
+ my @refers_commitids;
+ open COMMIT, "-|", qw(git cat-file commit), $commit;
+ while ( <COMMIT> ) {
+ push @refers_commitids, $1 if m{^parent (.*)$};
+ last if m{^\s*$};
+ }
+
+ my $msg = join "", <COMMIT>;
+
+ my @refers_commitids = map {
+ my ($match) = $remap{$_} ? ($remap{$_})
+ : map { $remap{$_} } grep m{^$_}, keys %remap;
+ if ( $match ) {
+ print STDERR "msg-link in ".n($commit).": "
+ .n($_)." treated as ".n($match)."\n"
+ if WATCH_BATTLE;
+ $match;
+ }
+ elsif ( do { $match = `git rev-parse $_`; !$? } ) {
+ chomp($match);
+ print STDERR "msg-link seen in ".n($commit)
+ ." to ".n($match)."\n"
+ if WATCH_BATTLE;
+ $match;
+ }
+ else {
+ print STDERR "dead msg-link seen in ".n($commit)
+ ." to ".n($_)."\n";
+ $_;
+ }
+ } ($msg =~ m{([0-9a-f]{${\(REV_MIN_LENGTH)},40})}g);
+
+ warn("$commit is not an extant revision\n"), next if $?;
+
+ $refers{$commit} = { map { $_ => 1 } @refers_commitids, @parents };
+}
+print STDERR "tournament: read ".@commits." commits\n"
+ if TOURNAMENT_RESULTS;
+
+my $O;
+
+# returns 1 if source refers to target, or target is a parent of
+# anything referred to by source.
+my $refers = sub {
+ my $source = shift;
+ my $target = shift;
+ my @todo = keys %{ $refers{$source}||{} };
+ my %seen = map { $_ => 1 } @todo;
+ while ( my $commit = shift @todo ) {
+ $O++;
+ do {
+ print STDERR n($source)." refers to ".n($target)."\n"
+ if WATCH_BATTLE;
+ return 1;
+ } if $commit eq $target;
+ push @todo, grep { $O++; !$seen{$_}++ }
+ keys %{ $refers{$commit} || {} };
+ }
+ print STDERR n($source)." does not refer to ".n($target)."\n"
+ if WATCH_BATTLE;
+ return 0;
+};
+
+# It's tournament time!
+print "$_\n" for sort {
+ (my $_a = $a) =~ s{ .*}{};
+ (my $_b = $b) =~ s{ .*}{};
+ my $cmp_val = 0;
+ if ( $refers->($_a, $_b) ) {
+ ++$cmp_val;
+ }
+ elsif ( $refers->($_b, $_a) ) {
+ --$cmp_val;
+ }
+ print STDERR n($a)." ".
+ (($cmp_val == 0 ? "EQUIVALENT TO" :
+ $cmp_val == -1 ? "COMES BEFORE" :
+ $cmp_val == 1 ? "COMES AFTER" : "TSNH")
+ ." ".n($b)."\n")
+ if WATCH_BATTLE;
+ $cmp_val;
+} @commits;
+
+print STDERR "tournament-links.perl: O(".@commits.") = ".$O."\n";
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index ae29f47..6c099ea 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -110,6 +110,7 @@ filter_msg=cat
filter_commit='git commit-tree "$@"'
filter_tag_name=
filter_subdir=
+tournament=
orig_namespace=refs/original/
force=
while :
@@ -166,6 +167,9 @@ do
--subdirectory-filter)
filter_subdir="$OPTARG"
;;
+ --tournament)
+ tournament="$OPTARG"
+ ;;
--original)
orig_namespace=$(expr "$OPTARG/" : '\(.*[^/]\)/*$')/
;;
@@ -254,6 +258,13 @@ commits=$(wc -l <../revs | tr -d " ")
test $commits -eq 0 && die "Found nothing to rewrite"
+if [ -n "$tournament" ]
+then
+ cat ../revs | (eval "$tournament") > ../revs.sorted
+ mv ../revs ../revs.pre-sorted
+ mv ../revs.sorted ../revs
+fi
+
# Rewrite the commits
i=0
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 5f60b22..8d5679d 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -165,4 +165,25 @@ test_expect_success '"map" works in commit filter' '
git rev-parse --verify master
'
+test_expect_success "Specifying a custom tournament" '
+ git checkout -b custom-tournament &&
+ echo "Hello">&2
+ echo j > j &&
+ git add j &&
+ echo "There">&2
+ git commit -m j j &&
+ echo z > z &&
+ git add z &&
+ echo "Dude">&2
+ git commit -m z &&
+ echo x > x &&
+ git add x &&
+ echo "Dude">&2
+ git commit -m x &&
+ git-filter-branch -f --parent-filter cat --tournament "cat > foo; fp=\$(head -1 foo | awk \"{print \\\$2}\"); ( head -2 foo | tac; tail -1 foo ) | awk \"{print \\\$1}\" | while read x y; do echo \$x \$fp; fp=\$x; done" master..HEAD &&
+ order=$(git-log --pretty=format:"%s" master...custom-tournament | xargs) &&
+ echo "order is <$order>" &&
+ test "$order" = "x j z"
+'
+
test_done
--
1.5.3.5
^ permalink raw reply related [flat|nested] 9+ messages in thread