From 905f8b7dfc2c520b91f418ab0f2aecb1c371fbe4 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 16 Feb 2007 03:22:40 -0800 Subject: [PATCH] git-svn: add a 'rebase' command This works similarly to 'svn update' or 'git pull' except that it preserves linear history with 'git rebase' instead of 'git merge' for ease of dcommit-ing with git-svn. While we're at it, put the working_head_info() logic into its own function and allow --fetch-all/--all for dcommit and rebase (which will fetch all refs in the current [svn-remote] instead of just the working one). Note that the '-a' switch (short for --fetch-all/--all) has been removed as it conflicts with the non-svn 'git fetch' Signed-off-by: Eric Wong --- git-svn.perl | 91 +++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 28 deletions(-) diff --git a/git-svn.perl b/git-svn.perl index 7ffbf64139..eca08bdd3b 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -56,7 +56,7 @@ my ($_stdin, $_help, $_edit, $_template, $_shared, $_version, $_fetch_all, $_merge, $_strategy, $_dry_run, - $_prefix, $_no_checkout); + $_prefix, $_no_checkout, $_verbose); $Git::SVN::_follow_parent = 1; my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, 'config-dir=s' => \$Git::SVN::Ra::config_dir, @@ -88,7 +88,7 @@ my %cmt_opts = ( 'edit|e' => \$_edit, my %cmd = ( fetch => [ \&cmd_fetch, "Download new revisions from SVN", { 'revision|r=s' => \$_revision, - 'all|a' => \$_fetch_all, + 'fetch-all|all' => \$_fetch_all, %fc_opts } ], init => [ \&cmd_init, "Initialize a repo for tracking" . " (requires URL argument)", @@ -101,7 +101,9 @@ my %cmd = ( 'Commit several diffs to merge with upstream', { 'merge|m|M' => \$_merge, 'strategy|s=s' => \$_strategy, + 'verbose|v' => \$_verbose, 'dry-run|n' => \$_dry_run, + 'fetch-all|all' => \$_fetch_all, %cmt_opts, %fc_opts } ], 'set-tree' => [ \&cmd_set_tree, "Set an SVN repository to a git tree-ish", @@ -129,6 +131,12 @@ my %cmd = ( 'color' => \$Git::SVN::Log::color, 'pager=s' => \$Git::SVN::Log::pager, } ], + 'rebase' => [ \&cmd_rebase, "Fetch and rebase your working directory", + { 'merge|m|M' => \$_merge, + 'verbose|v' => \$_verbose, + 'strategy|s=s' => \$_strategy, + 'fetch-all|all' => \$_fetch_all, + %fc_opts } ], 'commit-diff' => [ \&cmd_commit_diff, 'Commit a diff between two trees', { 'message|m=s' => \$_message, @@ -248,7 +256,7 @@ sub cmd_fetch { } my ($remote) = @_; if (@_ > 1) { - die "Usage: $0 fetch [--all|-a] [svn-remote]\n"; + die "Usage: $0 fetch [--all] [svn-remote]\n"; } $remote ||= $Git::SVN::default_repo_id; if ($_fetch_all) { @@ -296,21 +304,12 @@ sub cmd_set_tree { sub cmd_dcommit { my $head = shift; $head ||= 'HEAD'; - my ($url, $rev, $uuid); - my ($fh, $ctx) = command_output_pipe('rev-list', $head); my @refs; - my $c; - while (<$fh>) { - $c = $_; - chomp $c; - ($url, $rev, $uuid) = cmt_metadata($c); - last if (defined $url && defined $rev && defined $uuid); - unshift @refs, $c; - } - close $fh; # most likely breaking the pipe + my ($url, $rev, $uuid) = working_head_info($head, \@refs); + my $c = $refs[-1]; unless (defined $url && defined $rev && defined $uuid) { die "Unable to determine upstream SVN information from ", - "$head history:\n $ctx\n"; + "$head history\n"; } my $gs = Git::SVN->find_by_url($url); my $last_rev; @@ -354,15 +353,13 @@ sub cmd_dcommit { "now resync your SVN::Mirror repository.\n"; return; } - $gs->fetch; + $_fetch_all ? $gs->fetch_all : $gs->fetch; # we always want to rebase against the current HEAD, not any # head that was passed to us my @diff = command('diff-tree', 'HEAD', $gs->refname, '--'); my @finish; if (@diff) { - @finish = qw/rebase/; - push @finish, qw/--merge/ if $_merge; - push @finish, "--strategy=$_strategy" if $_strategy; + @finish = rebase_cmd(); print STDERR "W: HEAD and ", $gs->refname, " differ, ", "using @finish:\n", "@diff"; } else { @@ -374,6 +371,24 @@ sub cmd_dcommit { command_noisy(@finish, $gs->refname); } +sub cmd_rebase { + command_noisy(qw/update-index --refresh/); + my $url = (working_head_info('HEAD'))[0]; + if (!defined $url) { + die "Unable to determine upstream SVN information from ", + "working tree history\n"; + } + + my $gs = Git::SVN->find_by_url($url); + if (command(qw/diff-index HEAD --/)) { + print STDERR "Cannot rebase with uncommited changes:\n"; + command_noisy('status'); + exit 1; + } + $_fetch_all ? $gs->fetch_all : $gs->fetch; + command_noisy(rebase_cmd(), $gs->refname); +} + sub cmd_show_ignore { my $gs = Git::SVN->new; my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum); @@ -468,6 +483,14 @@ sub cmd_commit_diff { ########################### utility functions ######################### +sub rebase_cmd { + my @cmd = qw/rebase/; + push @cmd, '-v' if $_verbose; + push @cmd, qw/--merge/ if $_merge; + push @cmd, "--strategy=$_strategy" if $_strategy; + @cmd; +} + sub post_fetch_checkout { return if $_no_checkout; my $gs = $Git::SVN::_head or return; @@ -687,6 +710,20 @@ sub cmt_metadata { command(qw/cat-file commit/, shift)))[-1]); } +sub working_head_info { + my ($head, $refs) = @_; + my ($url, $rev, $uuid); + my ($fh, $ctx) = command_output_pipe('rev-list', $head); + while (<$fh>) { + chomp; + ($url, $rev, $uuid) = cmt_metadata($_); + last if (defined $url && defined $rev && defined $uuid); + unshift @$refs, $_ if $refs; + } + close $fh; # break the pipe + ($url, $rev, $uuid); +} + package Git::SVN; use strict; use warnings; @@ -783,6 +820,12 @@ sub parse_revision_argument { sub fetch_all { my ($repo_id, $remotes) = @_; + if (ref $repo_id) { + my $gs = $repo_id; + $repo_id = undef; + $repo_id = $gs->{repo_id}; + } + $remotes ||= read_all_remotes(); my $remote = $remotes->{$repo_id} or die "[svn-remote \"$repo_id\"] unknown\n"; my $fetch = $remote->{fetch}; @@ -3085,15 +3128,7 @@ sub git_svn_log_cmd { last; } - my $url; - my ($fh, $ctx) = command_output_pipe('rev-list', $head); - while (<$fh>) { - chomp; - $url = (::cmt_metadata($_))[0]; - last if defined $url; - } - close $fh; # break the pipe - + my $url = (::working_head_info($head))[0]; my $gs = Git::SVN->find_by_url($url) || Git::SVN->_new; my @cmd = (qw/log --abbrev-commit --pretty=raw --default/, $gs->refname); -- 2.11.4.GIT