From 9de83169d34825d617b7fe92b95c966d9910848d Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 19 Mar 2006 10:05:22 +0100 Subject: git.el: More robust handling of subprocess errors when returning strings. Make sure that functions that call a git process and return a string always return nil when the subprocess failed. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 5135e361be..cf650da3e5 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -148,6 +148,12 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add (append (git-get-env-strings env) (list "git") args)) (apply #'call-process "git" nil buffer nil args))) +(defun git-call-process-env-string (env &rest args) + "Wrapper for call-process that sets environment strings, and returns the process output as a string." + (with-temp-buffer + (and (eq 0 (apply #' git-call-process-env t env args)) + (buffer-string)))) + (defun git-run-process-region (buffer start end program args) "Run a git process with a buffer region as input." (let ((output-buffer (current-buffer)) @@ -189,8 +195,9 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add (defun git-get-string-sha1 (string) "Read a SHA1 from the specified string." - (let ((pos (string-match "[0-9a-f]\\{40\\}" string))) - (and pos (substring string pos (match-end 0))))) + (and string + (string-match "[0-9a-f]\\{40\\}" string) + (match-string 0 string))) (defun git-get-committer-name () "Return the name to use as GIT_COMMITTER_NAME." @@ -259,18 +266,12 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add (defun git-rev-parse (rev) "Parse a revision name and return its SHA1." (git-get-string-sha1 - (with-output-to-string - (with-current-buffer standard-output - (git-call-process-env t nil "rev-parse" rev))))) + (git-call-process-env-string nil "rev-parse" rev))) (defun git-symbolic-ref (ref) "Wrapper for the git-symbolic-ref command." - (car - (split-string - (with-output-to-string - (with-current-buffer standard-output - (git-call-process-env t nil "symbolic-ref" ref))) - "\n"))) + (let ((str (git-call-process-env-string nil "symbolic-ref" ref))) + (and str (car (split-string str "\n"))))) (defun git-update-ref (ref val &optional oldval) "Update a reference by calling git-update-ref." @@ -285,11 +286,7 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add (defun git-write-tree (&optional index-file) "Call git-write-tree and return the resulting tree SHA1 as a string." (git-get-string-sha1 - (with-output-to-string - (with-current-buffer standard-output - (git-call-process-env t - (if index-file `(("GIT_INDEX_FILE" . ,index-file)) nil) - "write-tree"))))) + (git-call-process-env-string (and index-file `(("GIT_INDEX_FILE" . ,index-file))) "write-tree"))) (defun git-commit-tree (buffer tree head) "Call git-commit-tree with buffer as input and return the resulting commit SHA1." -- cgit v1.2.3 From 75a8180d4b6939aca03b2c6320f128469abd5879 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 19 Mar 2006 10:05:48 +0100 Subject: git.el: Get the default user name and email from the repository config. If user name or email are not set explicitly, get them from the user.name and user.email configuration values before falling back to the Emacs defaults. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index cf650da3e5..5496a1bb5e 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -59,14 +59,14 @@ (defcustom git-committer-name nil "User name to use for commits. -The default is to fall back to `add-log-full-name' and then `user-full-name'." +The default is to fall back to the repository config, then to `add-log-full-name' and then to `user-full-name'." :group 'git :type '(choice (const :tag "Default" nil) (string :tag "Name"))) (defcustom git-committer-email nil "Email address to use for commits. -The default is to fall back to `add-log-mailing-address' and then `user-mail-address'." +The default is to fall back to the git repository config, then to `add-log-mailing-address' and then to `user-mail-address'." :group 'git :type '(choice (const :tag "Default" nil) (string :tag "Email"))) @@ -203,6 +203,7 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add "Return the name to use as GIT_COMMITTER_NAME." ; copied from log-edit (or git-committer-name + (git-repo-config "user.name") (and (boundp 'add-log-full-name) add-log-full-name) (and (fboundp 'user-full-name) (user-full-name)) (and (boundp 'user-full-name) user-full-name))) @@ -211,6 +212,7 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add "Return the email address to use as GIT_COMMITTER_EMAIL." ; copied from log-edit (or git-committer-email + (git-repo-config "user.email") (and (boundp 'add-log-mailing-address) add-log-mailing-address) (and (fboundp 'user-mail-address) (user-mail-address)) (and (boundp 'user-mail-address) user-mail-address))) @@ -268,6 +270,11 @@ The default is to fall back to `add-log-mailing-address' and then `user-mail-add (git-get-string-sha1 (git-call-process-env-string nil "rev-parse" rev))) +(defun git-repo-config (key) + "Retrieve the value associated to KEY in the git repository config file." + (let ((str (git-call-process-env-string nil "repo-config" key))) + (and str (car (split-string str "\n"))))) + (defun git-symbolic-ref (ref) "Wrapper for the git-symbolic-ref command." (let ((str (git-call-process-env-string nil "symbolic-ref" ref))) -- cgit v1.2.3 From 2b1c0ef2e5d103efacb535f32fb142114490dc33 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 19 Mar 2006 10:06:10 +0100 Subject: git.el: Added a function to diff against the other heads in a merge. git-diff-file-merge-head generates a diff against the first merge head, or with a prefix argument against the nth head. Bound to `d h' by default. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 5496a1bb5e..ebd00ef9c4 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -767,6 +767,16 @@ The default is to fall back to the git repository config, then to `add-log-maili (git-setup-diff-buffer (apply #'git-run-command-buffer "*git-diff*" "diff-index" "-p" "-M" "HEAD" "--" (git-get-filenames files))))) +(defun git-diff-file-merge-head (arg) + "Diff the marked file(s) against the first merge head (or the nth one with a numeric prefix)." + (interactive "p") + (let ((files (git-marked-files)) + (merge-heads (git-get-merge-heads))) + (unless merge-heads (error "No merge in progress")) + (git-setup-diff-buffer + (apply #'git-run-command-buffer "*git-diff*" "diff-index" "-p" "-M" + (or (nth (1- arg) merge-heads) "HEAD") "--" (git-get-filenames files))))) + (defun git-diff-unmerged-file (stage) "Diff the marked unmerged file(s) against the specified stage." (let ((files (git-marked-files))) @@ -959,6 +969,7 @@ The default is to fall back to the git repository config, then to `add-log-maili (define-key diff-map "=" 'git-diff-file) (define-key diff-map "e" 'git-diff-file-idiff) (define-key diff-map "E" 'git-find-file-imerge) + (define-key diff-map "h" 'git-diff-file-merge-head) (define-key diff-map "m" 'git-diff-file-mine) (define-key diff-map "o" 'git-diff-file-other) (setq git-status-mode-map map))) -- cgit v1.2.3 From ac7490506418e3ec495775e432b7040a17449fa9 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 20 Mar 2006 20:51:16 -0800 Subject: contrib/git-svn: allow rebuild to work on non-linear remote heads Because committing back to an SVN repository from different machines can result in different lineages, two different repositories running git-svn can result in different commit SHA1s (but of the same tree). Sometimes trees that are tracked independently are merged together (usually via children), resulting in non-unique git-svn-id: lines in rev-list. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index cf233ef6ed..f3fc3ec1a9 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -850,11 +850,23 @@ sub assert_revision_unknown { } } +sub trees_eq { + my ($x, $y) = @_; + my @x = safe_qx('git-cat-file','commit',$x); + my @y = safe_qx('git-cat-file','commit',$y); + if (($y[0] ne $x[0]) || $x[0] !~ /^tree $sha1\n$/ + || $y[0] !~ /^tree $sha1\n$/) { + print STDERR "Trees not equal: $y[0] != $x[0]\n"; + return 0 + } + return 1; +} + sub assert_revision_eq_or_unknown { my ($revno, $commit) = @_; if (-f "$REV_DIR/$revno") { my $current = file_to_s("$REV_DIR/$revno"); - if ($commit ne $current) { + if (($commit ne $current) && !trees_eq($commit, $current)) { croak "$REV_DIR/$revno already exists!\n", "current: $current\nexpected: $commit\n"; } -- cgit v1.2.3 From 03823184247215fba1dd9c9c39659d08dea3bad7 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 25 Mar 2006 18:52:31 -0800 Subject: contrib/git-svn: stabilize memory usage for big fetches We should be safely able to import histories with thousands of revisions without hogging up lots of memory. With this, we lose the ability to autocorrect mistakes when people specify revisions in reverse, but it's probably no longer a problem since we only have one method of log parsing nowadays. I've added an extra check to ensure that revision numbers do increment. Also, increment the version number to 0.11.0. I really should just call it 1.0 soon... Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 109 +++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 46 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index f3fc3ec1a9..3e5733eed9 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -8,7 +8,7 @@ use vars qw/ $AUTHOR $VERSION $GIT_SVN_INDEX $GIT_SVN $GIT_DIR $REV_DIR/; $AUTHOR = 'Eric Wong '; -$VERSION = '0.10.0'; +$VERSION = '0.11.0'; $GIT_DIR = $ENV{GIT_DIR} || "$ENV{PWD}/.git"; # make sure the svn binary gives consistent output between locales and TZs: $ENV{TZ} = 'UTC'; @@ -217,9 +217,8 @@ sub fetch { push @log_args, '--stop-on-copy' unless $_no_stop_copy; my $svn_log = svn_log_raw(@log_args); - @$svn_log = sort { $a->{revision} <=> $b->{revision} } @$svn_log; - my $base = shift @$svn_log or croak "No base revision!\n"; + my $base = next_log_entry($svn_log) or croak "No base revision!\n"; my $last_commit = undef; unless (-d $SVN_WC) { svn_cmd_checkout($SVN_URL,$base->{revision},$SVN_WC); @@ -234,18 +233,22 @@ sub fetch { } my @svn_up = qw(svn up); push @svn_up, '--ignore-externals' unless $_no_ignore_ext; - my $last_rev = $base->{revision}; - foreach my $log_msg (@$svn_log) { - assert_svn_wc_clean($last_rev, $last_commit); - $last_rev = $log_msg->{revision}; - sys(@svn_up,"-r$last_rev"); + my $last = $base; + while (my $log_msg = next_log_entry($svn_log)) { + assert_svn_wc_clean($last->{revision}, $last_commit); + if ($last->{revision} >= $log_msg->{revision}) { + croak "Out of order: last >= current: ", + "$last->{revision} >= $log_msg->{revision}\n"; + } + sys(@svn_up,"-r$log_msg->{revision}"); $last_commit = git_commit($log_msg, $last_commit, @parents); + $last = $log_msg; } - assert_svn_wc_clean($last_rev, $last_commit); + assert_svn_wc_clean($last->{revision}, $last_commit); unless (-e "$GIT_DIR/refs/heads/master") { sys(qw(git-update-ref refs/heads/master),$last_commit); } - return pop @$svn_log; + return $last; } sub commit { @@ -708,49 +711,61 @@ sub svn_commit_tree { return fetch("$rev_committed=$commit")->{revision}; } +# read the entire log into a temporary file (which is removed ASAP) +# and store the file handle + parser state sub svn_log_raw { my (@log_args) = @_; - my $pid = open my $log_fh,'-|'; + my $log_fh = IO::File->new_tmpfile or croak $!; + my $pid = fork; defined $pid or croak $!; - - if ($pid == 0) { + if (!$pid) { + open STDOUT, '>&', $log_fh or croak $!; exec (qw(svn log), @log_args) or croak $! } + waitpid $pid, 0; + croak if $?; + seek $log_fh, 0, 0 or croak $!; + return { state => 'sep', fh => $log_fh }; +} + +sub next_log_entry { + my $log = shift; # retval of svn_log_raw() + my $ret = undef; + my $fh = $log->{fh}; - my @svn_log; - my $state = 'sep'; - while (<$log_fh>) { + while (<$fh>) { chomp; if (/^\-{72}$/) { - if ($state eq 'msg') { - if ($svn_log[$#svn_log]->{lines}) { - $svn_log[$#svn_log]->{msg} .= $_."\n"; - unless(--$svn_log[$#svn_log]->{lines}) { - $state = 'sep'; + if ($log->{state} eq 'msg') { + if ($ret->{lines}) { + $ret->{msg} .= $_."\n"; + unless(--$ret->{lines}) { + $log->{state} = 'sep'; } } else { croak "Log parse error at: $_\n", - $svn_log[$#svn_log]->{revision}, + $ret->{revision}, "\n"; } next; } - if ($state ne 'sep') { + if ($log->{state} ne 'sep') { croak "Log parse error at: $_\n", - "state: $state\n", - $svn_log[$#svn_log]->{revision}, + "state: $log->{state}\n", + $ret->{revision}, "\n"; } - $state = 'rev'; + $log->{state} = 'rev'; # if we have an empty log message, put something there: - if (@svn_log) { - $svn_log[$#svn_log]->{msg} ||= "\n"; - delete $svn_log[$#svn_log]->{lines}; + if ($ret) { + $ret->{msg} ||= "\n"; + delete $ret->{lines}; + return $ret; } next; } - if ($state eq 'rev' && s/^r(\d+)\s*\|\s*//) { + if ($log->{state} eq 'rev' && s/^r(\d+)\s*\|\s*//) { my $rev = $1; my ($author, $date, $lines) = split(/\s*\|\s*/, $_, 3); ($lines) = ($lines =~ /(\d+)/); @@ -758,36 +773,34 @@ sub svn_log_raw { /(\d{4})\-(\d\d)\-(\d\d)\s (\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x) or croak "Failed to parse date: $date\n"; - my %log_msg = ( revision => $rev, + $ret = { revision => $rev, date => "$tz $Y-$m-$d $H:$M:$S", author => $author, lines => $lines, - msg => '' ); + msg => '' }; if (defined $_authors && ! defined $users{$author}) { die "Author: $author not defined in ", "$_authors file\n"; } - push @svn_log, \%log_msg; - $state = 'msg_start'; + $log->{state} = 'msg_start'; next; } # skip the first blank line of the message: - if ($state eq 'msg_start' && /^$/) { - $state = 'msg'; - } elsif ($state eq 'msg') { - if ($svn_log[$#svn_log]->{lines}) { - $svn_log[$#svn_log]->{msg} .= $_."\n"; - unless (--$svn_log[$#svn_log]->{lines}) { - $state = 'sep'; + if ($log->{state} eq 'msg_start' && /^$/) { + $log->{state} = 'msg'; + } elsif ($log->{state} eq 'msg') { + if ($ret->{lines}) { + $ret->{msg} .= $_."\n"; + unless (--$ret->{lines}) { + $log->{state} = 'sep'; } } else { croak "Log parse error at: $_\n", - $svn_log[$#svn_log]->{revision},"\n"; + $ret->{revision},"\n"; } } } - close $log_fh or croak $?; - return \@svn_log; + return $ret; } sub svn_info { @@ -1114,9 +1127,13 @@ __END__ Data structures: -@svn_log = array of log_msg hashes +$svn_log hashref (as returned by svn_log_raw) +{ + fh => file handle of the log file, + state => state of the log file parser (sep/msg/rev/msg_start...) +} -$log_msg hash +$log_msg hashref as returned by next_log_entry($svn_log) { msg => 'whitespace-formatted log entry ', # trailing newline is preserved -- cgit v1.2.3 From 13ccd6d4f2086ace8f70dc8a60a40ac5836dc881 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 29 Mar 2006 22:37:18 -0800 Subject: contrib/git-svn: force GIT_DIR to an absolute path We chdir internally, so we need a consistent GIT_DIR variable. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 3e5733eed9..59dd504047 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -9,7 +9,11 @@ use vars qw/ $AUTHOR $VERSION $GIT_DIR $REV_DIR/; $AUTHOR = 'Eric Wong '; $VERSION = '0.11.0'; -$GIT_DIR = $ENV{GIT_DIR} || "$ENV{PWD}/.git"; + +use Cwd qw/abs_path/; +$GIT_DIR = abs_path($ENV{GIT_DIR} || '.git'); +$ENV{GIT_DIR} = $GIT_DIR; + # make sure the svn binary gives consistent output between locales and TZs: $ENV{TZ} = 'UTC'; $ENV{LC_ALL} = 'C'; @@ -69,7 +73,6 @@ GetOptions(%opts, 'help|H|h' => \$_help, $GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn'; $GIT_SVN_INDEX = "$GIT_DIR/$GIT_SVN/index"; -$ENV{GIT_DIR} ||= $GIT_DIR; $SVN_URL = undef; $REV_DIR = "$GIT_DIR/$GIT_SVN/revs"; $SVN_WC = "$GIT_DIR/$GIT_SVN/tree"; -- cgit v1.2.3 From 53909056da869eb79c2a20699794b63c8c87e7fd Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 1 Apr 2006 18:25:02 -0800 Subject: contrib/git-svn: accept configuration via repo-config repo-config keys are any of the long option names minus the '-' characters Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 59dd504047..edfb19c39e 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -67,6 +67,23 @@ for (my $i = 0; $i < @ARGV; $i++) { my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd); +# convert GetOpt::Long specs for use by git-repo-config +foreach my $o (keys %opts) { + my $v = $opts{$o}; + my ($key) = ($o =~ /^([a-z\-]+)/); + $key =~ s/-//g; + my $arg = 'git-repo-config'; + $arg .= ' --int' if ($o =~ /=i$/); + $arg .= ' --bool' if ($o !~ /=[sfi]$/); + $arg .= " svn.$key"; # $key only matches [a-z\-], always shell-safe + if (ref $v eq 'ARRAY') { + chomp(@$v = `$arg`); + } else { + chomp($$v = `$arg`); + $$v = 0 if $$v eq 'false'; + } +} + GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version, 'id|i=s' => \$GIT_SVN) or exit 1; -- cgit v1.2.3 From 20b1d700c94c8b5b2b8b6f1b4982858d03cd9453 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 1 Apr 2006 18:25:03 -0800 Subject: contrib/git-svn: documentation updates contrib/git-svn/git-svn.txt: added git-repo-config key names for options fixed quoting of "git-svn-HEAD" in the manpage use preformatted text for examples contrib/git-svn/Makefile: add target to generate HTML: http://git-svn.yhbt.net/git-svn.html Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/Makefile | 3 +++ contrib/git-svn/git-svn.txt | 41 ++++++++++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile index a330c617d2..d7f1643bf7 100644 --- a/contrib/git-svn/Makefile +++ b/contrib/git-svn/Makefile @@ -25,6 +25,9 @@ git-svn.1 : git-svn.xml git-svn.xml : git-svn.txt asciidoc -b docbook -d manpage \ -f ../../Documentation/asciidoc.conf $< +git-svn.html : git-svn.txt + asciidoc -b xhtml11 -d manpage \ + -f ../../Documentation/asciidoc.conf $< test: cd t && $(SHELL) ./t0000-contrib-git-svn.sh diff --git a/contrib/git-svn/git-svn.txt b/contrib/git-svn/git-svn.txt index 7a6e0c4a4a..e18fcaf4fb 100644 --- a/contrib/git-svn/git-svn.txt +++ b/contrib/git-svn/git-svn.txt @@ -101,6 +101,8 @@ OPTIONS cannot version empty directories. Enabling this flag will make the commit to SVN act like git. + repo-config key: svn.rmdir + -e:: --edit:: Only used with the 'commit' command. @@ -109,6 +111,8 @@ OPTIONS default for objects that are commits, and forced on when committing tree objects. + repo-config key: svn.edit + -l:: --find-copies-harder:: Both of these are only used with the 'commit' command. @@ -116,6 +120,9 @@ OPTIONS They are both passed directly to git-diff-tree see git-diff-tree(1) for more information. + repo-config key: svn.l + repo-config key: svn.findcopiesharder + ADVANCED OPTIONS ---------------- -b:: @@ -133,6 +140,8 @@ ADVANCED OPTIONS This option may be specified multiple times, once for each branch. + repo-config key: svn.branch + -i:: --id :: This sets GIT_SVN_ID (instead of using the environment). See @@ -145,7 +154,7 @@ COMPATIBILITY OPTIONS Only used with the 'rebuild' command. Run this if you used an old version of git-svn that used - 'git-svn-HEAD' instead of 'remotes/git-svn' as the branch + "git-svn-HEAD" instead of "remotes/git-svn" as the branch for tracking the remote. --no-ignore-externals:: @@ -162,25 +171,29 @@ COMPATIBILITY OPTIONS Otherwise, do not enable this flag unless you know what you're doing. + repo-config key: svn.noignoreexternals + Basic Examples ~~~~~~~~~~~~~~ Tracking and contributing to an Subversion managed-project: -# Initialize a tree (like git init-db):: +------------------------------------------------------------------------ +# Initialize a tree (like git init-db): git-svn init http://svn.foo.org/project/trunk -# Fetch remote revisions:: +# Fetch remote revisions: git-svn fetch -# Create your own branch to hack on:: +# Create your own branch to hack on: git checkout -b my-branch remotes/git-svn -# Commit only the git commits you want to SVN:: +# Commit only the git commits you want to SVN: git-svn commit [ ...] -# Commit all the git commits from my-branch that don't exist in SVN:: +# Commit all the git commits from my-branch that don't exist in SVN: git-svn commit remotes/git-svn..my-branch -# Something is committed to SVN, pull the latest into your branch:: +# Something is committed to SVN, pull the latest into your branch: git-svn fetch && git pull . remotes/git-svn -# Append svn:ignore settings to the default git exclude file:: +# Append svn:ignore settings to the default git exclude file: git-svn show-ignore >> .git/info/exclude +------------------------------------------------------------------------ DESIGN PHILOSOPHY ----------------- @@ -219,7 +232,7 @@ git commits with the following syntax: This allows you to tie unfetched SVN revision 375 to your current HEAD:: - git-svn fetch 375=$(git-rev-parse HEAD) + `git-svn fetch 375=$(git-rev-parse HEAD)` Advanced Example: Tracking a Reorganized Repository ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -232,22 +245,24 @@ This is how Yann Dirson tracked the trunk of the ufoai directory when the /trunk directory of his repository was moved to /ufoai/trunk and he needed to continue tracking /ufoai/trunk where /trunk left off. - # This log message shows when the repository was reorganized:: +------------------------------------------------------------------------ + # This log message shows when the repository was reorganized: r166 | ydirson | 2006-03-02 01:36:55 +0100 (Thu, 02 Mar 2006) | 1 line Changed paths: D /trunk A /ufoai/trunk (from /trunk:165) - # First we start tracking the old revisions:: + # First we start tracking the old revisions: GIT_SVN_ID=git-oldsvn git-svn init \ - https://svn.sourceforge.net/svnroot/ufoai/trunk + https://svn.sourceforge.net/svnroot/ufoai/trunk GIT_SVN_ID=git-oldsvn git-svn fetch -r1:165 - # And now, we continue tracking the new revisions:: + # And now, we continue tracking the new revisions: GIT_SVN_ID=git-newsvn git-svn init \ https://svn.sourceforge.net/svnroot/ufoai/ufoai/trunk GIT_SVN_ID=git-newsvn git-svn fetch \ 166=`git-rev-parse refs/remotes/git-oldsvn` +------------------------------------------------------------------------ BUGS ---- -- cgit v1.2.3 From 5941a9e9d882b12dcc2a80e55acb25c180475529 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 3 Apr 2006 15:18:48 -0700 Subject: contrib/git-svn: ensure repo-config returns a value before using it fetching from repos without an authors-file defined was broken. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index edfb19c39e..e7fff46d22 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -77,10 +77,13 @@ foreach my $o (keys %opts) { $arg .= ' --bool' if ($o !~ /=[sfi]$/); $arg .= " svn.$key"; # $key only matches [a-z\-], always shell-safe if (ref $v eq 'ARRAY') { - chomp(@$v = `$arg`); + chomp(my @tmp = `$arg`); + @$v = @tmp if @tmp; } else { - chomp($$v = `$arg`); - $$v = 0 if $$v eq 'false'; + chomp(my $tmp = `$arg`); + if ($tmp && !($arg =~ / --bool / && $tmp eq 'false')) { + $$v = $tmp; + } } } -- cgit v1.2.3 From 5f2f4240022418e9a75505f11298db54a5da12d2 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 3 Apr 2006 15:18:49 -0700 Subject: contrib/git-svn: make sure our git-svn is up-to-date for test Bugs like the last one could've been avoided if it weren't for this... Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile index d7f1643bf7..acedf7305e 100644 --- a/contrib/git-svn/Makefile +++ b/contrib/git-svn/Makefile @@ -28,7 +28,7 @@ git-svn.xml : git-svn.txt git-svn.html : git-svn.txt asciidoc -b xhtml11 -d manpage \ -f ../../Documentation/asciidoc.conf $< -test: +test: git-svn cd t && $(SHELL) ./t0000-contrib-git-svn.sh clean: -- cgit v1.2.3 From fc9957b0052df6a8248420395bc9febd66194252 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 3 Apr 2006 17:41:44 -0700 Subject: contrib/git-svn: handle array values correctly Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index e7fff46d22..7c44450d72 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -75,12 +75,11 @@ foreach my $o (keys %opts) { my $arg = 'git-repo-config'; $arg .= ' --int' if ($o =~ /=i$/); $arg .= ' --bool' if ($o !~ /=[sfi]$/); - $arg .= " svn.$key"; # $key only matches [a-z\-], always shell-safe if (ref $v eq 'ARRAY') { - chomp(my @tmp = `$arg`); + chomp(my @tmp = `$arg --get-all svn.$key`); @$v = @tmp if @tmp; } else { - chomp(my $tmp = `$arg`); + chomp(my $tmp = `$arg --get svn.$key`); if ($tmp && !($arg =~ / --bool / && $tmp eq 'false')) { $$v = $tmp; } -- cgit v1.2.3 From 96afa0764e6b58a38b21befded2c8fd0543fab2a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 11 Apr 2006 23:05:14 -0700 Subject: Add colordiff for git to contrib/colordiff. I hacked it up to teach it the git extended diff headers, made it not to read the whole patch in the array. Also, the original program, when arguments are given, ran "diff" with the given arguments and showed the output from it. Of course, I changed it to run "git diff" ;-). Signed-off-by: Junio C Hamano --- contrib/colordiff/README | 2 + contrib/colordiff/colordiff.perl | 196 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 contrib/colordiff/README create mode 100755 contrib/colordiff/colordiff.perl (limited to 'contrib') diff --git a/contrib/colordiff/README b/contrib/colordiff/README new file mode 100644 index 0000000000..2678fdf9c2 --- /dev/null +++ b/contrib/colordiff/README @@ -0,0 +1,2 @@ +This is "colordiff" (http://colordiff.sourceforge.net/) by Dave +Ewart , modified specifically for git. diff --git a/contrib/colordiff/colordiff.perl b/contrib/colordiff/colordiff.perl new file mode 100755 index 0000000000..5789cfb265 --- /dev/null +++ b/contrib/colordiff/colordiff.perl @@ -0,0 +1,196 @@ +#!/usr/bin/perl -w +# +# $Id: colordiff.pl,v 1.4.2.10 2004/01/04 15:02:59 daveewart Exp $ + +######################################################################## +# # +# ColorDiff - a wrapper/replacment for 'diff' producing # +# colourful output # +# # +# Copyright (C)2002-2004 Dave Ewart (davee@sungate.co.uk) # +# # +######################################################################## +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # +# # +######################################################################## + +use strict; +use Getopt::Long qw(:config pass_through); +use IPC::Open2; + +my $app_name = 'colordiff'; +my $version = '1.0.4'; +my $author = 'Dave Ewart'; +my $author_email = 'davee@sungate.co.uk'; +my $app_www = 'http://colordiff.sourceforge.net/'; +my $copyright = '(C)2002-2004'; +my $show_banner = 1; + +# ANSI sequences for colours +my %colour; +$colour{white} = "\033[1;37m"; +$colour{yellow} = "\033[1;33m"; +$colour{green} = "\033[1;32m"; +$colour{blue} = "\033[1;34m"; +$colour{cyan} = "\033[1;36m"; +$colour{red} = "\033[1;31m"; +$colour{magenta} = "\033[1;35m"; +$colour{black} = "\033[1;30m"; +$colour{darkwhite} = "\033[0;37m"; +$colour{darkyellow} = "\033[0;33m"; +$colour{darkgreen} = "\033[0;32m"; +$colour{darkblue} = "\033[0;34m"; +$colour{darkcyan} = "\033[0;36m"; +$colour{darkred} = "\033[0;31m"; +$colour{darkmagenta} = "\033[0;35m"; +$colour{darkblack} = "\033[0;30m"; +$colour{OFF} = "\033[0;0m"; + +# Default colours if /etc/colordiffrc or ~/.colordiffrc do not exist +my $plain_text = $colour{OFF}; +my $file_old = $colour{red}; +my $file_new = $colour{blue}; +my $diff_stuff = $colour{magenta}; + +# Locations for personal and system-wide colour configurations +my $HOME = $ENV{HOME}; +my $etcdir = '/etc'; + +my ($setting, $value); +my @config_files = ("$etcdir/colordiffrc", "$HOME/.colordiffrc"); +my $config_file; + +foreach $config_file (@config_files) { + if (open(COLORDIFFRC, "<$config_file")) { + while () { + chop; + next if (/^#/ || /^$/); + s/\s+//g; + ($setting, $value) = split ('='); + if ($setting eq 'banner') { + if ($value eq 'no') { + $show_banner = 0; + } + next; + } + if (!defined $colour{$value}) { + print "Invalid colour specification ($value) in $config_file\n"; + next; + } + if ($setting eq 'plain') { + $plain_text = $colour{$value}; + } + elsif ($setting eq 'oldtext') { + $file_old = $colour{$value}; + } + elsif ($setting eq 'newtext') { + $file_new = $colour{$value}; + } + elsif ($setting eq 'diffstuff') { + $diff_stuff = $colour{$value}; + } + else { + print "Unknown option in $etcdir/colordiffrc: $setting\n"; + } + } + close COLORDIFFRC; + } +} + +# colordiff specfic options here. Need to pre-declare if using variables +GetOptions( + "no-banner" => sub { $show_banner = 0 }, + "plain-text=s" => \&set_color, + "file-old=s" => \&set_color, + "file-new=s" => \&set_color, + "diff-stuff=s" => \&set_color +); + +if ($show_banner == 1) { + print STDERR "$app_name $version ($app_www)\n"; + print STDERR "$copyright $author, $author_email\n\n"; +} + +if (defined $ARGV[0]) { + # More reliable way of pulling in arguments + open2(\*INPUTSTREAM, undef, "git", "diff", @ARGV); +} +else { + *INPUTSTREAM = \*STDIN; +} + +my $record; +my $nrecs = 0; +my $inside_file_old = 1; +my $nparents = undef; + +while () { + $nrecs++; + if (/^(\@\@+) -[-+0-9, ]+ \1/) { + print "$diff_stuff"; + $nparents = length($1) - 1; + } + elsif (/^diff -/ || /^index / || + /^old mode / || /^new mode / || + /^deleted file mode / || /^new file mode / || + /^similarity index / || /^dissimilarity index / || + /^copy from / || /^copy to / || + /^rename from / || /^rename to /) { + $nparents = undef; + print "$diff_stuff"; + } + elsif (defined $nparents) { + if ($nparents == 1) { + if (/^\+/) { + print $file_new; + } + elsif (/^-/) { + print $file_old; + } + else { + print $plain_text; + } + } + elsif (/^ {$nparents}/) { + print "$plain_text"; + } + elsif (/^[+ ]{$nparents}/) { + print "$file_new"; + } + elsif (/^[- ]{$nparents}/) { + print "$file_old"; + } + else { + print $plain_text; + } + } + elsif (/^--- / || /^\+\+\+ /) { + print $diff_stuff; + } + else { + print "$plain_text"; + } + s/$/$colour{OFF}/; + print "$_"; +} +close INPUTSTREAM; + +sub set_color { + my ($type, $color) = @_; + + $type =~ s/-/_/; + eval "\$$type = \$colour{$color}"; +} -- cgit v1.2.3 From a4a6e4ab32648631204398691f4066719dea1029 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 3 May 2006 15:27:26 +0200 Subject: Add a conversion tool to migrate remote information into the config Use this tool to rewrite the .git/remotes/* files into the config. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/remotes2config.sh | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 contrib/remotes2config.sh (limited to 'contrib') diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh new file mode 100644 index 0000000000..25901e2b3b --- /dev/null +++ b/contrib/remotes2config.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +# Use this tool to rewrite your .git/remotes/ files into the config. + +. git-sh-setup + +if [ -d "$GIT_DIR"/remotes ]; then + echo "Rewriting $GIT_DIR/remotes" >&2 + error=0 + # rewrite into config + { + cd "$GIT_DIR"/remotes + ls | while read f; do + name=$(echo -n "$f" | tr -c "A-Za-z0-9" ".") + sed -n \ + -e "s/^URL: \(.*\)$/remote.$name.url \1 ./p" \ + -e "s/^Pull: \(.*\)$/remote.$name.fetch \1 ^$ /p" \ + -e "s/^Push: \(.*\)$/remote.$name.push \1 ^$ /p" \ + < "$f" + done + echo done + } | while read key value regex; do + case $key in + done) + if [ $error = 0 ]; then + mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old + fi ;; + *) + echo "git-repo-config $key "$value" $regex" + git-repo-config $key "$value" $regex || error=1 ;; + esac + done +fi + + -- cgit v1.2.3 From 81c5a0e6e5135384a7316b9689a5b0baaf1a2752 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 5 May 2006 12:35:39 -0700 Subject: git-svn: documentation updates * Clarify that 'init' requires an argument * Remove instances of 'SVN_URL' in the manpage, it's not an environment variable. * Refer to 'Additional Fetch Arguments' when documenting 'fetch' * document --authors-file / -A option Thanks to Pavel Roskin and Seth Falcon for bringing these issues to my attention. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 6 ++++-- contrib/git-svn/git-svn.txt | 45 +++++++++++++++++++++++++++++++++----------- 2 files changed, 38 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 7c44450d72..e003501be6 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -42,7 +42,8 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, my %cmd = ( fetch => [ \&fetch, "Download new revisions from SVN", { 'revision|r=s' => \$_revision, %fc_opts } ], - init => [ \&init, "Initialize and fetch (import)", { } ], + init => [ \&init, "Initialize a repo for tracking" . + " (requires URL argument)", { } ], commit => [ \&commit, "Commit git revisions to SVN", { 'stdin|' => \$_stdin, 'edit|e' => \$_edit, @@ -220,7 +221,8 @@ when you have upgraded your tools and habits to use refs/remotes/$GIT_SVN } sub init { - $SVN_URL = shift or croak "SVN repository location required\n"; + $SVN_URL = shift or die "SVN repository location required " . + "as a command-line argument\n"; unless (-d $GIT_DIR) { sys('git-init-db'); } diff --git a/contrib/git-svn/git-svn.txt b/contrib/git-svn/git-svn.txt index e18fcaf4fb..f7d3de48f0 100644 --- a/contrib/git-svn/git-svn.txt +++ b/contrib/git-svn/git-svn.txt @@ -36,17 +36,22 @@ COMMANDS -------- init:: Creates an empty git repository with additional metadata - directories for git-svn. The SVN_URL must be specified - at this point. + directories for git-svn. The Subversion URL must be specified + as a command-line argument. fetch:: - Fetch unfetched revisions from the SVN_URL we are tracking. - refs/heads/remotes/git-svn will be updated to the latest revision. + Fetch unfetched revisions from the Subversion URL we are + tracking. refs/remotes/git-svn will be updated to the + latest revision. - Note: You should never attempt to modify the remotes/git-svn branch - outside of git-svn. Instead, create a branch from remotes/git-svn - and work on that branch. Use the 'commit' command (see below) - to write git commits back to remotes/git-svn. + Note: You should never attempt to modify the remotes/git-svn + branch outside of git-svn. Instead, create a branch from + remotes/git-svn and work on that branch. Use the 'commit' + command (see below) to write git commits back to + remotes/git-svn. + + See 'Additional Fetch Arguments' if you are interested in + manually joining branches on commit. commit:: Commit specified commit or tree objects to SVN. This relies on @@ -62,9 +67,9 @@ rebuild:: tracked with git-svn. Unfortunately, git-clone does not clone git-svn metadata and the svn working tree that git-svn uses for its operations. This rebuilds the metadata so git-svn can - resume fetch operations. SVN_URL may be optionally specified if - the directory/repository you're tracking has moved or changed - protocols. + resume fetch operations. A Subversion URL may be optionally + specified at the command-line if the directory/repository you're + tracking has moved or changed protocols. show-ignore:: Recursively finds and lists the svn:ignore property on @@ -123,6 +128,24 @@ OPTIONS repo-config key: svn.l repo-config key: svn.findcopiesharder +-A:: +--authors-file=:: + + Syntax is compatible with the files used by git-svnimport and + git-cvsimport: + +------------------------------------------------------------------------ +loginname = Joe User +------------------------------------------------------------------------ + + If this option is specified and git-svn encounters an SVN + committer name that does not exist in the authors-file, git-svn + will abort operation. The user will then have to add the + appropriate entry. Re-running the previous git-svn command + after the authors-file is modified should continue operation. + + repo-config key: svn.authors-file + ADVANCED OPTIONS ---------------- -b:: -- cgit v1.2.3 From 88521450fc4cca1f96d9179ee4f2cbe51da3359c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 5 May 2006 12:35:40 -0700 Subject: git-svn 1.0.0 Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index e003501be6..de13a96b8a 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -8,7 +8,7 @@ use vars qw/ $AUTHOR $VERSION $GIT_SVN_INDEX $GIT_SVN $GIT_DIR $REV_DIR/; $AUTHOR = 'Eric Wong '; -$VERSION = '0.11.0'; +$VERSION = '1.0.0'; use Cwd qw/abs_path/; $GIT_DIR = abs_path($ENV{GIT_DIR} || '.git'); -- cgit v1.2.3 From 304dac15486b6d86d31aea1ca736c2c61f17e902 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 23 May 2006 19:23:40 -0700 Subject: git-svn: starting a 1.1.0-pre development version Some not-very-well-tested changes coming... Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index de13a96b8a..ea7bfc22e4 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -8,7 +8,7 @@ use vars qw/ $AUTHOR $VERSION $GIT_SVN_INDEX $GIT_SVN $GIT_DIR $REV_DIR/; $AUTHOR = 'Eric Wong '; -$VERSION = '1.0.0'; +$VERSION = '1.1.0-pre'; use Cwd qw/abs_path/; $GIT_DIR = abs_path($ENV{GIT_DIR} || '.git'); -- cgit v1.2.3 From 36f5b1f0c800a23b9755a0214c1c3250f34f2486 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 23 May 2006 19:23:41 -0700 Subject: git-svn: ignore expansion of svn:keywords Unlike my earlier test patch, this also checks svn:eol-style and makes sure it's applied to working copy updates. This is definitely more correct than my original attempt at killing keyword expansions, but I still haven't tested it enough to know. Feedback would be much appreciated. Also changed assert_svn_wc_clean() to only work on the svn working copy. This requires a separate call to assert_tree() to check wc integrity against git in preparation for another change I'm planning. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/Makefile | 1 + contrib/git-svn/git-svn.perl | 106 +++++++++++++++---- contrib/git-svn/t/lib-git-svn.sh | 39 +++++++ contrib/git-svn/t/t0000-contrib-git-svn.sh | 43 +------- contrib/git-svn/t/t0001-contrib-git-svn-props.sh | 125 +++++++++++++++++++++++ 5 files changed, 254 insertions(+), 60 deletions(-) create mode 100644 contrib/git-svn/t/lib-git-svn.sh create mode 100644 contrib/git-svn/t/t0001-contrib-git-svn-props.sh (limited to 'contrib') diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile index acedf7305e..48f60b3a0d 100644 --- a/contrib/git-svn/Makefile +++ b/contrib/git-svn/Makefile @@ -30,6 +30,7 @@ git-svn.html : git-svn.txt -f ../../Documentation/asciidoc.conf $< test: git-svn cd t && $(SHELL) ./t0000-contrib-git-svn.sh + cd t && $(SHELL) ./t0001-contrib-git-svn-props.sh clean: rm -f git-svn *.xml *.html *.1 diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index ea7bfc22e4..b3e0684c44 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -39,6 +39,10 @@ my $_svn_co_url_revs; my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, 'branch|b=s' => \@_branch_from, 'authors-file|A=s' => \$_authors ); + +# yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome: +my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" ); + my %cmd = ( fetch => [ \&fetch, "Download new revisions from SVN", { 'revision|r=s' => \$_revision, %fc_opts } ], @@ -207,7 +211,7 @@ sub rebuild { push @svn_up, '--ignore-externals' unless $_no_ignore_ext; sys(@svn_up,"-r$newest_rev"); $ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX; - git_addremove(); + index_changes(); exec('git-write-tree'); } waitpid $pid, 0; @@ -249,7 +253,7 @@ sub fetch { chdir $SVN_WC or croak $!; read_uuid(); $last_commit = git_commit($base, @parents); - assert_svn_wc_clean($base->{revision}, $last_commit); + assert_tree($last_commit); } else { chdir $SVN_WC or croak $!; read_uuid(); @@ -259,16 +263,20 @@ sub fetch { push @svn_up, '--ignore-externals' unless $_no_ignore_ext; my $last = $base; while (my $log_msg = next_log_entry($svn_log)) { - assert_svn_wc_clean($last->{revision}, $last_commit); + assert_tree($last_commit); if ($last->{revision} >= $log_msg->{revision}) { croak "Out of order: last >= current: ", "$last->{revision} >= $log_msg->{revision}\n"; } + # Revert is needed for cases like: + # https://svn.musicpd.org/Jamming/trunk (r166:167), but + # I can't seem to reproduce something like that on a test... + sys(qw/svn revert -R ./); + assert_svn_wc_clean($last->{revision}); sys(@svn_up,"-r$log_msg->{revision}"); $last_commit = git_commit($log_msg, $last_commit, @parents); $last = $log_msg; } - assert_svn_wc_clean($last->{revision}, $last_commit); unless (-e "$GIT_DIR/refs/heads/master") { sys(qw(git-update-ref refs/heads/master),$last_commit); } @@ -314,7 +322,6 @@ sub commit { $svn_current_rev = svn_commit_tree($svn_current_rev, $c); } print "Done committing ",scalar @revs," revisions to SVN\n"; - } sub show_ignore { @@ -367,13 +374,11 @@ sub setup_git_svn { } sub assert_svn_wc_clean { - my ($svn_rev, $treeish) = @_; + my ($svn_rev) = @_; croak "$svn_rev is not an integer!\n" unless ($svn_rev =~ /^\d+$/); - croak "$treeish is not a sha1!\n" unless ($treeish =~ /^$sha1$/o); my $lcr = svn_info('.')->{'Last Changed Rev'}; if ($svn_rev != $lcr) { print STDERR "Checking for copy-tree ... "; - # use my @diff = grep(/^Index: /,(safe_qx(qw(svn diff), "-r$lcr:$svn_rev"))); if (@diff) { @@ -389,7 +394,6 @@ sub assert_svn_wc_clean { print STDERR $_ foreach @status; croak; } - assert_tree($treeish); } sub assert_tree { @@ -416,7 +420,7 @@ sub assert_tree { unlink $tmpindex or croak $!; } $ENV{GIT_INDEX_FILE} = $tmpindex; - git_addremove(); + index_changes(1); chomp(my $tree = `git-write-tree`); if ($old_index) { $ENV{GIT_INDEX_FILE} = $old_index; @@ -426,6 +430,7 @@ sub assert_tree { if ($tree ne $expected) { croak "Tree mismatch, Got: $tree, Expected: $expected\n"; } + unlink $tmpindex; } sub parse_diff_tree { @@ -562,7 +567,8 @@ sub precommit_check { sub svn_checkout_tree { my ($svn_rev, $treeish) = @_; my $from = file_to_s("$REV_DIR/$svn_rev"); - assert_svn_wc_clean($svn_rev,$from); + assert_svn_wc_clean($svn_rev); + assert_tree($from); print "diff-tree $from $treeish\n"; my $pid = open my $diff_fh, '-|'; defined $pid or croak $!; @@ -852,13 +858,75 @@ sub svn_info { sub sys { system(@_) == 0 or croak $? } -sub git_addremove { - system( "git-diff-files --name-only -z ". - " | git-update-index --remove -z --stdin && ". - "git-ls-files -z --others ". - "'--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude'". - " | git-update-index --add -z --stdin" - ) == 0 or croak $? +sub eol_cp { + my ($from, $to) = @_; + my $es = safe_qx(qw/svn propget svn:eol-style/, $to); + open my $rfd, '<', $from or croak $!; + binmode $rfd or croak $!; + open my $wfd, '>', $to or croak $!; + binmode $wfd or croak $!; + + my $eol = $EOL{$es} or undef; + if ($eol) { + print "$eol: $from => $to\n"; + } + my $buf; + while (1) { + my ($r, $w, $t); + defined($r = sysread($rfd, $buf, 4096)) or croak $!; + return unless $r; + $buf =~ s/(?:\015|\012|\015\012)/$eol/gs if $eol; + for ($w = 0; $w < $r; $w += $t) { + $t = syswrite($wfd, $buf, $r - $w, $w) or croak $!; + } + } +} + +sub do_update_index { + my ($z_cmd, $cmd, $no_text_base) = @_; + + my $z = open my $p, '-|'; + defined $z or croak $!; + unless ($z) { exec @$z_cmd or croak $! } + + my $pid = open my $ui, '|-'; + defined $pid or croak $!; + unless ($pid) { + exec('git-update-index',"--$cmd",'-z','--stdin') or croak $!; + } + local $/ = "\0"; + while (my $x = <$p>) { + chomp $x; + if (!$no_text_base && lstat $x && ! -l _ && + safe_qx(qw/svn propget svn:keywords/,$x)) { + my $mode = -x _ ? 0755 : 0644; + my ($v,$d,$f) = File::Spec->splitpath($x); + my $tb = File::Spec->catfile($d, '.svn', 'tmp', + 'text-base',"$f.svn-base"); + $tb =~ s#^/##; + unless (-f $tb) { + $tb = File::Spec->catfile($d, '.svn', + 'text-base',"$f.svn-base"); + $tb =~ s#^/##; + } + unlink $x or croak $!; + eol_cp($tb, $x); + chmod(($mode &~ umask), $x) or croak $!; + } + print $ui $x,"\0"; + } + close $ui or croak $!; +} + +sub index_changes { + my $no_text_base = shift; + do_update_index([qw/git-diff-files --name-only -z/], + 'remove', + $no_text_base); + do_update_index([qw/git-ls-files -z --others/, + "--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude"], + 'add', + $no_text_base); } sub s_to_file { @@ -936,7 +1004,7 @@ sub git_commit { defined $pid or croak $!; if ($pid == 0) { $ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX; - git_addremove(); + index_changes(); chomp(my $tree = `git-write-tree`); croak if $?; if (exists $tree_map{$tree}) { diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh new file mode 100644 index 0000000000..a98e9d164d --- /dev/null +++ b/contrib/git-svn/t/lib-git-svn.sh @@ -0,0 +1,39 @@ +PATH=$PWD/../:$PATH +if test -d ../../../t +then + cd ../../../t +else + echo "Must be run in contrib/git-svn/t" >&2 + exit 1 +fi + +. ./test-lib.sh + +GIT_DIR=$PWD/.git +GIT_SVN_DIR=$GIT_DIR/git-svn +SVN_TREE=$GIT_SVN_DIR/tree + +svnadmin >/dev/null 2>&1 +if test $? != 1 +then + test_expect_success 'skipping contrib/git-svn test' : + test_done + exit +fi + +svn >/dev/null 2>&1 +if test $? != 1 +then + test_expect_success 'skipping contrib/git-svn test' : + test_done + exit +fi + +svnrepo=$PWD/svnrepo + +set -e + +svnadmin create $svnrepo +svnrepo="file://$svnrepo/test-git-svn" + + diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh index 80ad3573db..8b3a0d9029 100644 --- a/contrib/git-svn/t/t0000-contrib-git-svn.sh +++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh @@ -3,48 +3,10 @@ # Copyright (c) 2006 Eric Wong # - -PATH=$PWD/../:$PATH test_description='git-svn tests' -if test -d ../../../t -then - cd ../../../t -else - echo "Must be run in contrib/git-svn/t" >&2 - exit 1 -fi - -. ./test-lib.sh - -GIT_DIR=$PWD/.git -GIT_SVN_DIR=$GIT_DIR/git-svn -SVN_TREE=$GIT_SVN_DIR/tree - -svnadmin >/dev/null 2>&1 -if test $? != 1 -then - test_expect_success 'skipping contrib/git-svn test' : - test_done - exit -fi - -svn >/dev/null 2>&1 -if test $? != 1 -then - test_expect_success 'skipping contrib/git-svn test' : - test_done - exit -fi - -svnrepo=$PWD/svnrepo - -set -e - -svnadmin create $svnrepo -svnrepo="file://$svnrepo/test-git-svn" +. ./lib-git-svn.sh mkdir import - cd import echo foo > foo @@ -55,10 +17,9 @@ mkdir -p bar echo 'zzz' > bar/zzz echo '#!/bin/sh' > exec.sh chmod +x exec.sh -svn import -m 'import for git-svn' . $svnrepo >/dev/null +svn import -m 'import for git-svn' . "$svnrepo" >/dev/null cd .. - rm -rf import test_expect_success \ diff --git a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh new file mode 100644 index 0000000000..6fa7889e9a --- /dev/null +++ b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh @@ -0,0 +1,125 @@ +#!/bin/sh +# +# Copyright (c) 2006 Eric Wong +# + +test_description='git-svn property tests' +. ./lib-git-svn.sh + +mkdir import + +a_crlf= +a_lf= +a_cr= +a_ne_crlf= +a_ne_lf= +a_ne_cr= +a_empty= +a_empty_lf= +a_empty_cr= +a_empty_crlf= + +cd import + cat >> kw.c <<'' +/* Make it look like somebody copied a file from CVS into SVN: */ +/* $Id: kw.c,v 1.1.1.1 1994/03/06 00:00:00 eric Exp $ */ + + printf "Hello\r\nWorld\r\n" > crlf + a_crlf=`git-hash-object -w crlf` + printf "Hello\rWorld\r" > cr + a_cr=`git-hash-object -w cr` + printf "Hello\nWorld\n" > lf + a_lf=`git-hash-object -w lf` + + printf "Hello\r\nWorld" > ne_crlf + a_ne_crlf=`git-hash-object -w ne_crlf` + printf "Hello\nWorld" > ne_lf + a_ne_lf=`git-hash-object -w ne_lf` + printf "Hello\rWorld" > ne_cr + a_ne_cr=`git-hash-object -w ne_cr` + + touch empty + a_empty=`git-hash-object -w empty` + printf "\n" > empty_lf + a_empty_lf=`git-hash-object -w empty_lf` + printf "\r" > empty_cr + a_empty_cr=`git-hash-object -w empty_cr` + printf "\r\n" > empty_crlf + a_empty_crlf=`git-hash-object -w empty_crlf` + + svn import -m 'import for git-svn' . "$svnrepo" >/dev/null +cd .. + +rm -rf import +svn co "$svnrepo" test_wc + +cd test_wc + echo 'Greetings' >> kw.c + svn commit -m 'Not yet an $Id$' + svn up + + echo 'Hello world' >> kw.c + svn commit -m 'Modified file, but still not yet an $Id$' + svn up + + svn propset svn:keywords Id kw.c + svn commit -m 'Propset $Id$' + svn up +cd .. + +git-svn init "$svnrepo" +git-svn fetch + +git checkout -b mybranch remotes/git-svn +echo 'Hi again' >> kw.c +name='test svn:keywords ignoring' + +git commit -a -m "$name" +git-svn commit remotes/git-svn..mybranch +git pull . remotes/git-svn + +expect='/* $Id$ */' +got="`sed -ne 2p kw.c`" +test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'" + +cd test_wc + svn propset svn:eol-style CR empty + svn propset svn:eol-style CR crlf + svn propset svn:eol-style CR ne_crlf + svn commit -m 'propset CR on crlf files' + svn up +cd .. + +git-svn fetch +git pull . remotes/git-svn + +svn co "$svnrepo" new_wc +for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf +do + test_expect_success "Comparing $i" "cmp $i new_wc/$i" +done + + +cd test_wc + printf '$Id$\rHello\rWorld\r' > cr + printf '$Id$\rHello\rWorld' > ne_cr + a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git-hash-object --stdin` + a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git-hash-object --stdin` + svn propset svn:eol-style CRLF cr + svn propset svn:eol-style CRLF ne_cr + svn propset svn:keywords Id cr + svn propset svn:keywords Id ne_cr + svn commit -m 'propset CRLF on cr files' + svn up +cd .. + +git-svn fetch +git pull . remotes/git-svn + +b_cr="`git-hash-object cr`" +b_ne_cr="`git-hash-object ne_cr`" + +test_expect_success 'CRLF + $Id$' "test '$a_cr' = '$b_cr'" +test_expect_success 'CRLF + $Id$ (no newline)' "test '$a_ne_cr' = '$b_ne_cr'" + +test_done -- cgit v1.2.3 From 034016391c475e98c38a9b715cd670b8b2d0c619 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Sat, 27 May 2006 15:54:14 +0530 Subject: gitview: Add key binding for F5. Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 17 +++++++++++++---- contrib/gitview/gitview.txt | 6 ++++-- 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 781badbc5b..c708534286 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -425,7 +425,7 @@ class DiffWindow: class GitView: """ This is the main class """ - version = "0.7" + version = "0.8" def __init__(self, with_diff=0): self.with_diff = with_diff @@ -449,8 +449,17 @@ class GitView: self.accel_group = gtk.AccelGroup() self.window.add_accel_group(self.accel_group) + self.accel_group.connect_group(0xffc2, 0, gtk.ACCEL_LOCKED, self.refresh); - self.construct() + self.window.add(self.construct()) + + def refresh(self, widget, event=None, *arguments, **keywords): + self.get_encoding() + self.get_bt_sha1() + Commit.children_sha1 = {} + self.set_branch(sys.argv[without_diff:]) + self.window.show() + return True def get_bt_sha1(self): """ Update the bt_sha1 dictionary with the @@ -500,9 +509,9 @@ class GitView: menu_bar.show() vbox.pack_start(menu_bar, expand=False, fill=True) vbox.pack_start(paned, expand=True, fill=True) - self.window.add(vbox) paned.show() vbox.show() + return vbox def construct_top(self): @@ -987,8 +996,8 @@ class GitView: window.set_diff(commit_sha1, parent_sha1, encoding) self.treeview.grab_focus() +without_diff = 0 if __name__ == "__main__": - without_diff = 0 if (len(sys.argv) > 1 ): if (sys.argv[1] == "--without-diff"): diff --git a/contrib/gitview/gitview.txt b/contrib/gitview/gitview.txt index fcf759c307..e3bc4f46c2 100644 --- a/contrib/gitview/gitview.txt +++ b/contrib/gitview/gitview.txt @@ -25,6 +25,9 @@ OPTIONS All the valid option for git-rev-list(1) + Key Bindings: + F5: + To reread references. EXAMPLES ------ @@ -33,6 +36,5 @@ EXAMPLES or drivers/scsi subdirectories gitview --since=2.weeks.ago - Show the changes during the last two weeks + Show the changes during the last two weeks - -- cgit v1.2.3 From 756944350d675bd3c08130df337b776f189ed752 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Sat, 27 May 2006 15:55:32 +0530 Subject: gitview: Move the console error messages to message dialog Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index c708534286..b836047cf3 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -983,10 +983,15 @@ class GitView: try: self.treeview.set_cursor(self.index[revid]) except KeyError: - print "Revision %s not present in the list" % revid + dialog = gtk.MessageDialog(parent=None, flags=0, + type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE, + message_format=None) + dialog.set_markup("Revision %s not present in the list" % revid) # revid == 0 is the parent of the first commit if (revid != 0 ): - print "Try running gitview without any options" + dialog.format_secondary_text("Try running gitview without any options") + dialog.run() + dialog.destroy() self.treeview.grab_focus() -- cgit v1.2.3 From 3c4c7351c030a18fa94076ffe62f534f890e9602 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 29 May 2006 19:03:45 -0700 Subject: git-svn: t0001: workaround a heredoc bug in old versions of dash The dash installed on my Debian Sarge boxes don't seem to like <<'' as a heredoc starter. Recent versions of dash do not need this fix. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/t/t0001-contrib-git-svn-props.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh index 6fa7889e9a..23a5a2a223 100644 --- a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh +++ b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh @@ -20,9 +20,10 @@ a_empty_cr= a_empty_crlf= cd import - cat >> kw.c <<'' + cat >> kw.c <<\EOF /* Make it look like somebody copied a file from CVS into SVN: */ /* $Id: kw.c,v 1.1.1.1 1994/03/06 00:00:00 eric Exp $ */ +EOF printf "Hello\r\nWorld\r\n" > crlf a_crlf=`git-hash-object -w crlf` -- cgit v1.2.3 From 037b048eceafa129903d6327d3565c543f5895bb Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 29 May 2006 19:03:46 -0700 Subject: git-svn: remove assertion that broke with older versions of svn svn < 1.3.x would display changes to keywords lines as modified if they aren't expanded in the working copy. We already check for changes against the git tree here, so checking against the svn one is probably excessive. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index b3e0684c44..aac877974d 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -567,7 +567,6 @@ sub precommit_check { sub svn_checkout_tree { my ($svn_rev, $treeish) = @_; my $from = file_to_s("$REV_DIR/$svn_rev"); - assert_svn_wc_clean($svn_rev); assert_tree($from); print "diff-tree $from $treeish\n"; my $pid = open my $diff_fh, '-|'; -- cgit v1.2.3 From 4e2e5647f23e2babcb188fd05eececbb261e2f17 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Sun, 4 Jun 2006 23:37:48 +0530 Subject: gitview: Add some useful keybindings. Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 15 +++++++++++++++ contrib/gitview/gitview.txt | 6 ++++++ 2 files changed, 21 insertions(+) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index b836047cf3..3b6bdceeeb 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -450,6 +450,9 @@ class GitView: self.accel_group = gtk.AccelGroup() self.window.add_accel_group(self.accel_group) self.accel_group.connect_group(0xffc2, 0, gtk.ACCEL_LOCKED, self.refresh); + self.accel_group.connect_group(0xffc1, 0, gtk.ACCEL_LOCKED, self.maximize); + self.accel_group.connect_group(0xffc8, 0, gtk.ACCEL_LOCKED, self.fullscreen); + self.accel_group.connect_group(0xffc9, 0, gtk.ACCEL_LOCKED, self.unfullscreen); self.window.add(self.construct()) @@ -461,6 +464,18 @@ class GitView: self.window.show() return True + def maximize(self, widget, event=None, *arguments, **keywords): + self.window.maximize() + return True + + def fullscreen(self, widget, event=None, *arguments, **keywords): + self.window.fullscreen() + return True + + def unfullscreen(self, widget, event=None, *arguments, **keywords): + self.window.unfullscreen() + return True + def get_bt_sha1(self): """ Update the bt_sha1 dictionary with the respective sha1 details """ diff --git a/contrib/gitview/gitview.txt b/contrib/gitview/gitview.txt index e3bc4f46c2..6924df286e 100644 --- a/contrib/gitview/gitview.txt +++ b/contrib/gitview/gitview.txt @@ -26,8 +26,14 @@ OPTIONS All the valid option for git-rev-list(1) Key Bindings: + F4: + To maximize the window F5: To reread references. + F11: + Full screen + F12: + Leave full screen EXAMPLES ------ -- cgit v1.2.3 From c7162c1db6fef6527eafb8829d60cf26b02b0108 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 23 May 2006 18:34:24 -0700 Subject: git-svn: t0000: add -f flag to checkout Some changes to the latest git.git made this test croak. So we'll always just force everything when using a new branch. Signed-off-by: Eric Wong --- contrib/git-svn/t/t0000-contrib-git-svn.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh index 8b3a0d9029..a07fbad68b 100644 --- a/contrib/git-svn/t/t0000-contrib-git-svn.sh +++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh @@ -32,7 +32,7 @@ test_expect_success \ name='try a deep --rmdir with a commit' -git checkout -b mybranch remotes/git-svn +git checkout -f -b mybranch remotes/git-svn mv dir/a/b/c/d/e/file dir/file cp dir/file file git update-index --add --remove dir/a/b/c/d/e/file dir/file file @@ -58,7 +58,7 @@ test_expect_code 1 "$name" \ name='detect node change from directory to file #1' rm -rf dir $GIT_DIR/index -git checkout -b mybranch2 remotes/git-svn +git checkout -f -b mybranch2 remotes/git-svn mv bar/zzz zzz rm -rf bar mv zzz bar @@ -73,7 +73,7 @@ test_expect_code 1 "$name" \ name='detect node change from file to directory #2' rm -f $GIT_DIR/index -git checkout -b mybranch3 remotes/git-svn +git checkout -f -b mybranch3 remotes/git-svn rm bar/zzz git-update-index --remove bar/zzz mkdir bar/zzz @@ -88,7 +88,7 @@ test_expect_code 1 "$name" \ name='detect node change from directory to file #2' rm -f $GIT_DIR/index -git checkout -b mybranch4 remotes/git-svn +git checkout -f -b mybranch4 remotes/git-svn rm -rf dir git update-index --remove -- dir/file touch dir @@ -103,7 +103,7 @@ test_expect_code 1 "$name" \ name='remove executable bit from a file' rm -f $GIT_DIR/index -git checkout -b mybranch5 remotes/git-svn +git checkout -f -b mybranch5 remotes/git-svn chmod -x exec.sh git update-index exec.sh git commit -m "$name" -- cgit v1.2.3 From 8a97e368882afbc2bdc030214339bed54ed6545c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 28 May 2006 15:23:56 -0700 Subject: git-svn: fix handling of filenames with embedded '@' svn has trouble parsing files with embedded '@' characters. For example, svn propget svn:keywords foo@bar.c svn: Syntax error parsing revision 'bar.c' I asked about this on #svn and the workaround suggested was to append an explicit revision specifier: svn propget svn:keywords foo@bar.c@BASE This patch appends '@BASE' to the filename in all calls to 'svn propget'. Patch originally by Seth Falcon Seth: signoff? [ew: Made to work with older svn that don't support peg revisions] Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index aac877974d..7ed11ef0a5 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -34,7 +34,7 @@ my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_find_copies_harder, $_l, $_version, $_upgrade, $_authors); my (@_branch_from, %tree_map, %users); -my $_svn_co_url_revs; +my ($_svn_co_url_revs, $_svn_pg_peg_revs); my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, 'branch|b=s' => \@_branch_from, @@ -336,7 +336,7 @@ sub show_ignore { my %ign; File::Find::find({wanted=>sub{if(lstat $_ && -d _ && -d "$_/.svn"){ s#^\./##; - @{$ign{$_}} = safe_qx(qw(svn propget svn:ignore),$_); + @{$ign{$_}} = svn_propget_base('svn:ignore', $_); }}, no_chdir=>1},'.'); print "\n# /\n"; @@ -859,7 +859,7 @@ sub sys { system(@_) == 0 or croak $? } sub eol_cp { my ($from, $to) = @_; - my $es = safe_qx(qw/svn propget svn:eol-style/, $to); + my $es = svn_propget_base('svn:eol-style', $to); open my $rfd, '<', $from or croak $!; binmode $rfd or croak $!; open my $wfd, '>', $to or croak $!; @@ -897,7 +897,7 @@ sub do_update_index { while (my $x = <$p>) { chomp $x; if (!$no_text_base && lstat $x && ! -l _ && - safe_qx(qw/svn propget svn:keywords/,$x)) { + svn_propget_base('svn:keywords', $x)) { my $mode = -x _ ? 0755 : 0644; my ($v,$d,$f) = File::Spec->splitpath($x); my $tb = File::Spec->catfile($d, '.svn', 'tmp', @@ -1135,6 +1135,9 @@ sub svn_compat_check { if (grep /usage: checkout URL\[\@REV\]/,@co_help) { $_svn_co_url_revs = 1; } + if (grep /\[TARGET\[\@REV\]\.\.\.\]/, `svn propget -h`) { + $_svn_pg_peg_revs = 1; + } # I really, really hope nobody hits this... unless (grep /stop-on-copy/, (safe_qx(qw(svn log -h)))) { @@ -1214,6 +1217,12 @@ sub load_authors { close $authors or croak $!; } +sub svn_propget_base { + my ($p, $f) = @_; + $f .= '@BASE' if $_svn_pg_peg_revs; + return safe_qx(qw/svn propget/, $p, $f); +} + __END__ Data structures: -- cgit v1.2.3 From 4a393f2b53f0997f79d47793d4c774fa0072887c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 9 Jun 2006 23:27:01 -0700 Subject: git-svn: eol_cp corner-case fixes If we read the maximum size of our buffer into $buf, and the last character is '\015', there's a chance that the character is '\012', which means our regex won't work correctly. At the worst case, this could introduce an extra newline into the code. We'll now read an extra character if we see '\015' is the last character in $buf. We also forgot to recalculate the length of $buf after doing the newline substitution, causing some files to appeare truncated. We'll do that now and force byte semantics in length() for good measure. Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 7ed11ef0a5..8d2e7f74ea 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -866,19 +866,26 @@ sub eol_cp { binmode $wfd or croak $!; my $eol = $EOL{$es} or undef; - if ($eol) { - print "$eol: $from => $to\n"; - } my $buf; + use bytes; while (1) { my ($r, $w, $t); defined($r = sysread($rfd, $buf, 4096)) or croak $!; return unless $r; - $buf =~ s/(?:\015|\012|\015\012)/$eol/gs if $eol; + if ($eol) { + if ($buf =~ /\015$/) { + my $c; + defined($r = sysread($rfd,$c,1)) or croak $!; + $buf .= $c if $r > 0; + } + $buf =~ s/(?:\015\012|\015|\012)/$eol/gs; + $r = length($buf); + } for ($w = 0; $w < $r; $w += $t) { $t = syswrite($wfd, $buf, $r - $w, $w) or croak $!; } } + no bytes; } sub do_update_index { -- cgit v1.2.3 From ce475dfcb5021339d8545a020e8697dd35a1bbea Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 2 Jun 2006 15:16:41 -0700 Subject: git-svn: restore original LC_ALL setting (or unset) for commit svn forces UTF-8 for commit messages, and with LC_ALL set to 'C' it is unable to determine encoding of the git commit message. Now we'll just assume the user has set LC_* correctly for the commit message they're using. Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 8d2e7f74ea..8bc3d69fdb 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -14,6 +14,7 @@ use Cwd qw/abs_path/; $GIT_DIR = abs_path($ENV{GIT_DIR} || '.git'); $ENV{GIT_DIR} = $GIT_DIR; +my $LC_ALL = $ENV{LC_ALL}; # make sure the svn binary gives consistent output between locales and TZs: $ENV{TZ} = 'UTC'; $ENV{LC_ALL} = 'C'; @@ -704,23 +705,34 @@ sub svn_commit_tree { my ($oneline) = ($log_msg{msg} =~ /([^\n\r]+)/); print "Committing $commit: $oneline\n"; + if (defined $LC_ALL) { + $ENV{LC_ALL} = $LC_ALL; + } else { + delete $ENV{LC_ALL}; + } my @ci_output = safe_qx(qw(svn commit -F),$commit_msg); - my ($committed) = grep(/^Committed revision \d+\./,@ci_output); + $ENV{LC_ALL} = 'C'; unlink $commit_msg; - defined $committed or croak + my ($committed) = ($ci_output[$#ci_output] =~ /(\d+)/); + if (!defined $committed) { + my $out = join("\n",@ci_output); + print STDERR "W: Trouble parsing \`svn commit' output:\n\n", + $out, "\n\nAssuming English locale..."; + ($committed) = ($out =~ /^Committed revision \d+\./sm); + defined $committed or die " FAILED!\n", "Commit output failed to parse committed revision!\n", - join("\n",@ci_output),"\n"; - my ($rev_committed) = ($committed =~ /^Committed revision (\d+)\./); + print STDERR " OK\n"; + } my @svn_up = qw(svn up); push @svn_up, '--ignore-externals' unless $_no_ignore_ext; - if ($rev_committed == ($svn_rev + 1)) { - push @svn_up, "-r$rev_committed"; + if ($committed == ($svn_rev + 1)) { + push @svn_up, "-r$committed"; sys(@svn_up); my $info = svn_info('.'); my $date = $info->{'Last Changed Date'} or die "Missing date\n"; - if ($info->{'Last Changed Rev'} != $rev_committed) { - croak "$info->{'Last Changed Rev'} != $rev_committed\n" + if ($info->{'Last Changed Rev'} != $committed) { + croak "$info->{'Last Changed Rev'} != $committed\n" } my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~ /(\d{4})\-(\d\d)\-(\d\d)\s @@ -728,16 +740,16 @@ sub svn_commit_tree { or croak "Failed to parse date: $date\n"; $log_msg{date} = "$tz $Y-$m-$d $H:$M:$S"; $log_msg{author} = $info->{'Last Changed Author'}; - $log_msg{revision} = $rev_committed; + $log_msg{revision} = $committed; $log_msg{msg} .= "\n"; my $parent = file_to_s("$REV_DIR/$svn_rev"); git_commit(\%log_msg, $parent, $commit); - return $rev_committed; + return $committed; } # resync immediately push @svn_up, "-r$svn_rev"; sys(@svn_up); - return fetch("$rev_committed=$commit")->{revision}; + return fetch("$committed=$commit")->{revision}; } # read the entire log into a temporary file (which is removed ASAP) -- cgit v1.2.3 From b63af9b340dd831840c70103c9f609940a910031 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 3 Jun 2006 02:56:33 -0700 Subject: git-svn: don't allow commit if svn tree is not current If new revisions are fetched, that implies we haven't merged, acked, or nacked them yet, and attempting to write the tree we're committing means we'd silently clobber the newly fetched changes. Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 8bc3d69fdb..72129de6a3 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -309,9 +309,16 @@ sub commit { } chomp @revs; - fetch(); - chdir $SVN_WC or croak $!; + chdir $SVN_WC or croak "Unable to chdir $SVN_WC: $!\n"; my $info = svn_info('.'); + my $fetched = fetch(); + if ($info->{Revision} != $fetched->{revision}) { + print STDERR "There are new revisions that were fetched ", + "and need to be merged (or acknowledged) ", + "before committing.\n"; + exit 1; + } + $info = svn_info('.'); read_uuid($info); my $svn_current_rev = $info->{'Last Changed Rev'}; foreach my $c (@revs) { -- cgit v1.2.3 From 162f41292167a800432fc6bbacfcd9f93a90b0c8 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 14 May 2006 20:00:00 -0700 Subject: git-svn: support -C passing to git-diff-tree The repo-config key is 'svn.copysimilarity' Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 72129de6a3..089d597d25 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -33,7 +33,8 @@ use POSIX qw/strftime/; my $sha1 = qr/[a-f\d]{40}/; my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, - $_find_copies_harder, $_l, $_version, $_upgrade, $_authors); + $_find_copies_harder, $_l, $_cp_similarity, + $_version, $_upgrade, $_authors); my (@_branch_from, %tree_map, %users); my ($_svn_co_url_revs, $_svn_pg_peg_revs); @@ -55,6 +56,7 @@ my %cmd = ( 'rmdir' => \$_rmdir, 'find-copies-harder' => \$_find_copies_harder, 'l=i' => \$_l, + 'copy-similarity|C=i'=> \$_cp_similarity, %fc_opts, } ], 'show-ignore' => [ \&show_ignore, "Show svn:ignore listings", { } ], @@ -580,7 +582,12 @@ sub svn_checkout_tree { my $pid = open my $diff_fh, '-|'; defined $pid or croak $!; if ($pid == 0) { - my @diff_tree = qw(git-diff-tree -z -r -C); + my @diff_tree = qw(git-diff-tree -z -r); + if ($_cp_similarity) { + push @diff_tree, "-C$_cp_similarity"; + } else { + push @diff_tree, '-C'; + } push @diff_tree, '--find-copies-harder' if $_find_copies_harder; push @diff_tree, "-l$_l" if defined $_l; exec(@diff_tree, $from, $treeish) or croak $!; -- cgit v1.2.3 From bf78b1d89b29f8524ccfbd7042fa277277e316ff Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 28 Apr 2006 03:42:38 -0700 Subject: git-svn: --branch-all-refs / -B support This should make life easier for all those who type: `git-rev-parse --symbolic --all | xargs -n1 echo -b` every time they run git-svn fetch. Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 089d597d25..c91160d37f 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -34,12 +34,13 @@ my $sha1 = qr/[a-f\d]{40}/; my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_find_copies_harder, $_l, $_cp_similarity, - $_version, $_upgrade, $_authors); + $_version, $_upgrade, $_authors, $_branch_all_refs); my (@_branch_from, %tree_map, %users); my ($_svn_co_url_revs, $_svn_pg_peg_revs); my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, 'branch|b=s' => \@_branch_from, + 'branch-all-refs|B' => \$_branch_all_refs, 'authors-file|A=s' => \$_authors ); # yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome: @@ -108,6 +109,7 @@ usage(0) if $_help; version() if $_version; usage(1) unless defined $cmd; load_authors() if $_authors; +load_all_refs() if $_branch_all_refs; svn_compat_check(); $cmd{$cmd}->[0]->(@ARGV); exit 0; @@ -1238,6 +1240,17 @@ sub map_tree_joins { } } +sub load_all_refs { + if (@_branch_from) { + print STDERR '--branch|-b parameters are ignored when ', + "--branch-all-refs|-B is passed\n"; + } + + # don't worry about rev-list on non-commit objects/tags, + # it shouldn't blow up if a ref is a blob or tree... + chomp(@_branch_from = `git-rev-parse --symbolic --all`); +} + # ' = real-name ' mapping based on git-svnimport: sub load_authors { open my $authors, '<', $_authors or die "Can't open $_authors $!\n"; -- cgit v1.2.3 From 098749d9bee6694abc8a0991996ff94b607abc7f Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 28 Apr 2006 03:51:16 -0700 Subject: git-svn: optimize --branch and --branch-all-ref By breaking the pipe read once we've seen a commit twice. This should make -B/--branch-all-ref faster and usable on a frequent basis. We use topological order now for calling git-rev-list, and any commit we've seen before should imply that all parents have been seen (at least I hope that's the case for --topo-order). Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index c91160d37f..d4b9323694 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -1220,23 +1220,30 @@ sub check_upgrade_needed { # fills %tree_map with a reverse mapping of trees to commits. Useful # for finding parents to commit on. sub map_tree_joins { + my %seen; foreach my $br (@_branch_from) { my $pid = open my $pipe, '-|'; defined $pid or croak $!; if ($pid == 0) { - exec(qw(git-rev-list --pretty=raw), $br) or croak $?; + exec(qw(git-rev-list --topo-order --pretty=raw), $br) + or croak $?; } while (<$pipe>) { if (/^commit ($sha1)$/o) { my $commit = $1; + + # if we've seen a commit, + # we've seen its parents + last if $seen{$commit}; my ($tree) = (<$pipe> =~ /^tree ($sha1)$/o); unless (defined $tree) { die "Failed to parse commit $commit\n"; } push @{$tree_map{$tree}}, $commit; + $seen{$commit} = 1; } } - close $pipe or croak $?; + close $pipe; # we could be breaking the pipe early } } -- cgit v1.2.3 From 6dfbe5163e26e3e1126c9b08c3cb38195e92a82c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 3 May 2006 22:54:00 -0700 Subject: git-svn: support manually placed initial trees from fetch Sometimes I don't feel like downloading an entire tree again when I actually decide a branch is worth tracking, so some users can get around it more easily with this. Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index d4b9323694..54f3d6312e 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -262,7 +262,14 @@ sub fetch { } else { chdir $SVN_WC or croak $!; read_uuid(); - $last_commit = file_to_s("$REV_DIR/$base->{revision}"); + eval { $last_commit = file_to_s("$REV_DIR/$base->{revision}") }; + # looks like a user manually cp'd and svn switch'ed + unless ($last_commit) { + sys(qw/svn revert -R ./); + assert_svn_wc_clean($base->{revision}); + $last_commit = git_commit($base, @parents); + assert_tree($last_commit); + } } my @svn_up = qw(svn up); push @svn_up, '--ignore-externals' unless $_no_ignore_ext; -- cgit v1.2.3 From 883d0a78d2a28add079f809ffb8dd077f4a766d8 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 24 May 2006 01:22:07 -0700 Subject: git-svn: Move all git-svn-related paths into $GIT_DIR/svn Since GIT_SVN_ID usage is probably going to become more widespread , we won't run the chance of somebody having a GIT_SVN_ID name that conflicts with one of the default directories that already exist in $GIT_DIR (branches/tags). Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 99 ++++++++++++++++++++++++++++++++++------ contrib/git-svn/t/lib-git-svn.sh | 2 +- 2 files changed, 85 insertions(+), 16 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 54f3d6312e..2dce4e7b83 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -6,7 +6,7 @@ use strict; use vars qw/ $AUTHOR $VERSION $SVN_URL $SVN_INFO $SVN_WC $SVN_UUID $GIT_SVN_INDEX $GIT_SVN - $GIT_DIR $REV_DIR/; + $GIT_DIR $REV_DIR $GIT_SVN_DIR/; $AUTHOR = 'Eric Wong '; $VERSION = '1.1.0-pre'; @@ -37,6 +37,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_version, $_upgrade, $_authors, $_branch_all_refs); my (@_branch_from, %tree_map, %users); my ($_svn_co_url_revs, $_svn_pg_peg_revs); +my @repo_path_split_cache; my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, 'branch|b=s' => \@_branch_from, @@ -100,10 +101,11 @@ GetOptions(%opts, 'help|H|h' => \$_help, 'id|i=s' => \$GIT_SVN) or exit 1; $GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn'; -$GIT_SVN_INDEX = "$GIT_DIR/$GIT_SVN/index"; +$GIT_SVN_DIR = "$GIT_DIR/svn/$GIT_SVN"; +$GIT_SVN_INDEX = "$GIT_SVN_DIR/index"; $SVN_URL = undef; -$REV_DIR = "$GIT_DIR/$GIT_SVN/revs"; -$SVN_WC = "$GIT_DIR/$GIT_SVN/tree"; +$REV_DIR = "$GIT_SVN_DIR/revs"; +$SVN_WC = "$GIT_SVN_DIR/tree"; usage(0) if $_help; version() if $_version; @@ -111,6 +113,7 @@ usage(1) unless defined $cmd; load_authors() if $_authors; load_all_refs() if $_branch_all_refs; svn_compat_check(); +migration_check() unless $cmd eq 'init'; $cmd{$cmd}->[0]->(@ARGV); exit 0; @@ -200,7 +203,7 @@ sub rebuild { $latest = $rev; } assert_revision_eq_or_unknown($rev, $c); - sys('git-update-ref',"$GIT_SVN/revs/$rev",$c); + sys('git-update-ref',"svn/$GIT_SVN/revs/$rev",$c); $newest_rev = $rev if ($rev > $newest_rev); } close $rev_list or croak $?; @@ -241,7 +244,7 @@ sub init { sub fetch { my (@parents) = @_; check_upgrade_needed(); - $SVN_URL ||= file_to_s("$GIT_DIR/$GIT_SVN/info/url"); + $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url"); my @log_args = -d $SVN_WC ? ($SVN_WC) : ($SVN_URL); unless ($_revision) { $_revision = -d $SVN_WC ? 'BASE:HEAD' : '0:HEAD'; @@ -350,7 +353,7 @@ sub show_ignore { chomp(my @excludes = (<$fh>)); close $fh or croak $!; - $SVN_URL ||= file_to_s("$GIT_DIR/$GIT_SVN/info/url"); + $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url"); chdir $SVN_WC or croak $!; my %ign; File::Find::find({wanted=>sub{if(lstat $_ && -d _ && -d "$_/.svn"){ @@ -374,7 +377,44 @@ sub read_uuid { my $info = shift || svn_info('.'); $SVN_UUID = $info->{'Repository UUID'} or croak "Repository UUID unreadable\n"; - s_to_file($SVN_UUID,"$GIT_DIR/$GIT_SVN/info/uuid"); + s_to_file($SVN_UUID,"$GIT_SVN_DIR/info/uuid"); +} + +sub quiet_run { + my $pid = fork; + defined $pid or croak $!; + if (!$pid) { + open my $null, '>', '/dev/null' or croak $!; + open STDERR, '>&', $null or croak $!; + open STDOUT, '>&', $null or croak $!; + exec @_ or croak $!; + } + waitpid $pid, 0; + return $?; +} + +sub repo_path_split { + my $full_url = shift; + $full_url =~ s#/+$##; + + foreach (@repo_path_split_cache) { + if ($full_url =~ s#$_##) { + my $u = $1; + $full_url =~ s#^/+##; + return ($u, $full_url); + } + } + + my ($url, $path) = ($full_url =~ m!^([a-z\+]+://[^/]*)(.*)$!i); + $path =~ s#^/+##; + my @paths = split(m#/+#, $path); + + while (quiet_run(qw/svn ls --non-interactive/, $url)) { + my $n = shift @paths || last; + $url .= "/$n"; + } + push @repo_path_split_cache, qr/^(\Q$url\E)/; + return ($url, $path); } sub setup_git_svn { @@ -382,14 +422,17 @@ sub setup_git_svn { unless (-d $GIT_DIR) { croak "GIT_DIR=$GIT_DIR does not exist!\n"; } - mkpath(["$GIT_DIR/$GIT_SVN"]); - mkpath(["$GIT_DIR/$GIT_SVN/info"]); + mkpath([$GIT_SVN_DIR]); + mkpath(["$GIT_SVN_DIR/info"]); mkpath([$REV_DIR]); - s_to_file($SVN_URL,"$GIT_DIR/$GIT_SVN/info/url"); + s_to_file($SVN_URL,"$GIT_SVN_DIR/info/url"); - open my $fd, '>>', "$GIT_DIR/$GIT_SVN/info/exclude" or croak $!; + open my $fd, '>>', "$GIT_SVN_DIR/info/exclude" or croak $!; print $fd '.svn',"\n"; close $fd or croak $!; + my ($url, $path) = repo_path_split($SVN_URL); + s_to_file($url, "$GIT_SVN_DIR/info/repo_url"); + s_to_file($path, "$GIT_SVN_DIR/info/repo_path"); } sub assert_svn_wc_clean { @@ -688,7 +731,7 @@ sub handle_rmdir { sub svn_commit_tree { my ($svn_rev, $commit) = @_; - my $commit_msg = "$GIT_DIR/$GIT_SVN/.svn-commit.tmp.$$"; + my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$"; my %log_msg = ( msg => '' ); open my $msg, '>', $commit_msg or croak $!; @@ -965,7 +1008,7 @@ sub index_changes { 'remove', $no_text_base); do_update_index([qw/git-ls-files -z --others/, - "--exclude-from=$GIT_DIR/$GIT_SVN/info/exclude"], + "--exclude-from=$GIT_SVN_DIR/info/exclude"], 'add', $no_text_base); } @@ -1097,7 +1140,7 @@ sub git_commit { push @update_ref, $primary_parent unless $?; } sys(@update_ref); - sys('git-update-ref',"$GIT_SVN/revs/$log_msg->{revision}",$commit); + sys('git-update-ref',"svn/$GIT_SVN/revs/$log_msg->{revision}",$commit); print "r$log_msg->{revision} = $commit\n"; return $commit; } @@ -1283,6 +1326,32 @@ sub svn_propget_base { return safe_qx(qw/svn propget/, $p, $f); } +sub migration_check { + return if (-d "$GIT_DIR/svn" || !-d $GIT_DIR); + print "Upgrading repository...\n"; + unless (-d "$GIT_DIR/svn") { + mkdir "$GIT_DIR/svn" or croak $!; + } + print "Data from a previous version of git-svn exists, but\n\t", + "$GIT_SVN_DIR\n\t(required for this version ", + "($VERSION) of git-svn) does not.\n"; + + foreach my $x (`git-rev-parse --symbolic --all`) { + next unless $x =~ s#^refs/remotes/##; + chomp $x; + next unless -f "$GIT_DIR/$x/info/url"; + my $u = eval { file_to_s("$GIT_DIR/$x/info/url") }; + next unless $u; + my $dn = dirname("$GIT_DIR/svn/$x"); + mkpath([$dn]) unless -d $dn; + rename "$GIT_DIR/$x", "$GIT_DIR/svn/$x" or croak "$!: $x"; + my ($url, $path) = repo_path_split($u); + s_to_file($url, "$GIT_DIR/svn/$x/info/repo_url"); + s_to_file($path, "$GIT_DIR/svn/$x/info/repo_path"); + } + print "Done upgrading.\n"; +} + __END__ Data structures: diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh index a98e9d164d..58408a6c07 100644 --- a/contrib/git-svn/t/lib-git-svn.sh +++ b/contrib/git-svn/t/lib-git-svn.sh @@ -10,7 +10,7 @@ fi . ./test-lib.sh GIT_DIR=$PWD/.git -GIT_SVN_DIR=$GIT_DIR/git-svn +GIT_SVN_DIR=$GIT_DIR/svn/git-svn SVN_TREE=$GIT_SVN_DIR/tree svnadmin >/dev/null 2>&1 -- cgit v1.2.3 From b8c92caddac61e556254bf93c3a4bd744de94320 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 24 May 2006 01:40:37 -0700 Subject: git-svn: minor cleanups, extra error-checking While we're at it, read_repo_config has been added and expanded to handle case where command-line arguments are optional to Getopt::Long Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 82 +++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 36 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 2dce4e7b83..a24306072e 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -77,39 +77,15 @@ for (my $i = 0; $i < @ARGV; $i++) { my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd); -# convert GetOpt::Long specs for use by git-repo-config -foreach my $o (keys %opts) { - my $v = $opts{$o}; - my ($key) = ($o =~ /^([a-z\-]+)/); - $key =~ s/-//g; - my $arg = 'git-repo-config'; - $arg .= ' --int' if ($o =~ /=i$/); - $arg .= ' --bool' if ($o !~ /=[sfi]$/); - if (ref $v eq 'ARRAY') { - chomp(my @tmp = `$arg --get-all svn.$key`); - @$v = @tmp if @tmp; - } else { - chomp(my $tmp = `$arg --get svn.$key`); - if ($tmp && !($arg =~ / --bool / && $tmp eq 'false')) { - $$v = $tmp; - } - } -} - +read_repo_config(\%opts); GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version, 'id|i=s' => \$GIT_SVN) or exit 1; -$GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn'; -$GIT_SVN_DIR = "$GIT_DIR/svn/$GIT_SVN"; -$GIT_SVN_INDEX = "$GIT_SVN_DIR/index"; -$SVN_URL = undef; -$REV_DIR = "$GIT_SVN_DIR/revs"; -$SVN_WC = "$GIT_SVN_DIR/tree"; - usage(0) if $_help; version() if $_version; usage(1) unless defined $cmd; +init_vars(); load_authors() if $_authors; load_all_refs() if $_branch_all_refs; svn_compat_check(); @@ -132,7 +108,7 @@ Usage: $0 [options] [arguments]\n print $fd ' ',pack('A13',$_),$cmd{$_}->[1],"\n"; foreach (keys %{$cmd{$_}->[2]}) { # prints out arguments as they should be passed: - my $x = s#=s$## ? '' : s#=i$## ? '' : ''; + my $x = s#[:=]s$## ? '' : s#[:=]i$## ? '' : ''; print $fd ' ' x 17, join(', ', map { length $_ > 1 ? "--$_" : "-$_" } split /\|/,$_)," $x\n"; @@ -220,9 +196,10 @@ sub rebuild { sys(@svn_up,"-r$newest_rev"); $ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX; index_changes(); - exec('git-write-tree'); + exec('git-write-tree') or croak $!; } waitpid $pid, 0; + croak $? if $?; if ($_upgrade) { print STDERR <<""; @@ -295,6 +272,7 @@ sub fetch { unless (-e "$GIT_DIR/refs/heads/master") { sys(qw(git-update-ref refs/heads/master),$last_commit); } + close $svn_log->{fh}; return $last; } @@ -830,7 +808,7 @@ sub svn_log_raw { exec (qw(svn log), @log_args) or croak $! } waitpid $pid, 0; - croak if $?; + croak $? if $?; seek $log_fh, 0, 0 or croak $!; return { state => 'sep', fh => $log_fh }; } @@ -1090,7 +1068,7 @@ sub git_commit { $ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX; index_changes(); chomp(my $tree = `git-write-tree`); - croak if $?; + croak $? if $?; if (exists $tree_map{$tree}) { my %seen_parent = map { $_ => 1 } @exec_parents; foreach (@{$tree_map{$tree}}) { @@ -1118,7 +1096,7 @@ sub git_commit { exec @exec or croak $!; } waitpid($pid,0); - croak if $?; + croak $? if $?; $out_fh->flush == 0 or croak $!; seek $out_fh, 0, 0 or croak $!; @@ -1134,7 +1112,7 @@ sub git_commit { close STDERR; close STDOUT; exec 'git-rev-parse','--verify', - "refs/remotes/$GIT_SVN^0"; + "refs/remotes/$GIT_SVN^0" or croak $!; } waitpid $pid, 0; push @update_ref, $primary_parent unless $?; @@ -1190,7 +1168,7 @@ sub blob_to_file { if ($pid == 0) { open STDOUT, '>&', $blob_fh or croak $!; - exec('git-cat-file','blob',$blob); + exec('git-cat-file','blob',$blob) or croak $!; } waitpid $pid, 0; croak $? if $?; @@ -1202,7 +1180,7 @@ sub safe_qx { my $pid = open my $child, '-|'; defined $pid or croak $!; if ($pid == 0) { - exec(@_) or croak $?; + exec(@_) or croak $!; } my @ret = (<$child>); close $child or croak $?; @@ -1252,7 +1230,7 @@ sub check_upgrade_needed { defined $pid or croak $!; if ($pid == 0) { close STDERR; - exec('git-rev-parse',"$GIT_SVN-HEAD") or croak $?; + exec('git-rev-parse',"$GIT_SVN-HEAD") or croak $!; } my @ret = (<$child>); close $child or croak $?; @@ -1276,7 +1254,7 @@ sub map_tree_joins { defined $pid or croak $!; if ($pid == 0) { exec(qw(git-rev-list --topo-order --pretty=raw), $br) - or croak $?; + or croak $!; } while (<$pipe>) { if (/^commit ($sha1)$/o) { @@ -1352,6 +1330,38 @@ sub migration_check { print "Done upgrading.\n"; } +sub init_vars { + $GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn'; + $GIT_SVN_DIR = "$GIT_DIR/svn/$GIT_SVN"; + $GIT_SVN_INDEX = "$GIT_SVN_DIR/index"; + $SVN_URL = undef; + $REV_DIR = "$GIT_SVN_DIR/revs"; + $SVN_WC = "$GIT_SVN_DIR/tree"; +} + +# convert GetOpt::Long specs for use by git-repo-config +sub read_repo_config { + return unless -d $GIT_DIR; + my $opts = shift; + foreach my $o (keys %$opts) { + my $v = $opts->{$o}; + my ($key) = ($o =~ /^([a-z\-]+)/); + $key =~ s/-//g; + my $arg = 'git-repo-config'; + $arg .= ' --int' if ($o =~ /[:=]i$/); + $arg .= ' --bool' if ($o !~ /[:=][sfi]$/); + if (ref $v eq 'ARRAY') { + chomp(my @tmp = `$arg --get-all svn.$key`); + @$v = @tmp if @tmp; + } else { + chomp(my $tmp = `$arg --get svn.$key`); + if ($tmp && !($arg =~ / --bool / && $tmp eq 'false')) { + $$v = $tmp; + } + } + } +} + __END__ Data structures: -- cgit v1.2.3 From dc5869c00d9aafbddcc11b93b5a0a7fcdeb755ea Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 24 May 2006 02:07:32 -0700 Subject: git-svn: add --repack and --repack-flags= options This should help keep disk usage sane for large imports. --repack takes an optional argument for the interval, it defaults to 1000 if no argument is specified. Arguments to --repack-flags are passed directly to git-repack. No arguments are passed by default. Idea stolen from git-cvsimport :) Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index a24306072e..a04cf1d354 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -34,6 +34,7 @@ my $sha1 = qr/[a-f\d]{40}/; my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_find_copies_harder, $_l, $_cp_similarity, + $_repack, $_repack_nr, $_repack_flags, $_version, $_upgrade, $_authors, $_branch_all_refs); my (@_branch_from, %tree_map, %users); my ($_svn_co_url_revs, $_svn_pg_peg_revs); @@ -42,7 +43,9 @@ my @repo_path_split_cache; my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, 'branch|b=s' => \@_branch_from, 'branch-all-refs|B' => \$_branch_all_refs, - 'authors-file|A=s' => \$_authors ); + 'authors-file|A=s' => \$_authors, + 'repack:i' => \$_repack, + 'repack-flags|repack-args|repack-opts=s' => \$_repack_flags); # yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome: my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" ); @@ -82,6 +85,7 @@ GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version, 'id|i=s' => \$GIT_SVN) or exit 1; +set_default_vals(); usage(0) if $_help; version() if $_version; usage(1) unless defined $cmd; @@ -1120,6 +1124,10 @@ sub git_commit { sys(@update_ref); sys('git-update-ref',"svn/$GIT_SVN/revs/$log_msg->{revision}",$commit); print "r$log_msg->{revision} = $commit\n"; + if ($_repack && (--$_repack_nr == 0)) { + $_repack_nr = $_repack; + sys("git repack $_repack_flags"); + } return $commit; } @@ -1362,6 +1370,14 @@ sub read_repo_config { } } +sub set_default_vals { + if (defined $_repack) { + $_repack = 1000 if ($_repack <= 0); + $_repack_nr = $_repack; + $_repack_flags ||= ''; + } +} + __END__ Data structures: -- cgit v1.2.3 From f8ab6b732f0a2cead5089aea2ce0c3c3aa97cafe Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 31 May 2006 15:49:56 -0700 Subject: git-svn: add --shared and --template= options to pass to init-db Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index a04cf1d354..d8f103ed9a 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -35,6 +35,7 @@ my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_find_copies_harder, $_l, $_cp_similarity, $_repack, $_repack_nr, $_repack_flags, + $_template, $_shared, $_version, $_upgrade, $_authors, $_branch_all_refs); my (@_branch_from, %tree_map, %users); my ($_svn_co_url_revs, $_svn_pg_peg_revs); @@ -54,7 +55,9 @@ my %cmd = ( fetch => [ \&fetch, "Download new revisions from SVN", { 'revision|r=s' => \$_revision, %fc_opts } ], init => [ \&init, "Initialize a repo for tracking" . - " (requires URL argument)", { } ], + " (requires URL argument)", + { 'template=s' => \$_template, + 'shared' => \$_shared } ], commit => [ \&commit, "Commit git revisions to SVN", { 'stdin|' => \$_stdin, 'edit|e' => \$_edit, @@ -217,7 +220,10 @@ sub init { $SVN_URL = shift or die "SVN repository location required " . "as a command-line argument\n"; unless (-d $GIT_DIR) { - sys('git-init-db'); + my @init_db = ('git-init-db'); + push @init_db, "--template=$_template" if defined $_template; + push @init_db, "--shared" if defined $_shared; + sys(@init_db); } setup_git_svn(); } -- cgit v1.2.3 From 9d55b41aadd65b1ebfbbe1336db00168c2dd01c5 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 12 Jun 2006 15:53:13 -0700 Subject: git-svn: add some functionality to better support branches in svn New commands: graft-branches - The most interesting command of the bunch. It detects branches in SVN via various techniques (currently regexes and file copies). It can be later extended to handle svk and other properties people may use to track merges in svk. Basically, merge tracking is not standardized at all in the SVN world, and git grafts are perfect for dealing with this situation. Existing branch support (via tree matches) is only handled at fetch time. The following tow were originally implemented as shell scripts several months ago, but I just decided to streamline things a bit and added them to the main script. multi-init - supports git-svnimport-like command-line syntax for importing repositories that are layed out as recommended by the SVN folks. This is a bit more tolerant than the git-svnimport command-line syntax and doesn't require the user to figure out where the repository URL ends and where the repository path begins. multi-fetch - runs fetch on all known SVN branches we're tracking. This will NOT discover new branches (unlike git-svnimport), so multi-init will need to be re-run (it's idempotent). Consider these three to be auxilliary commands (like show-ignore, and rebuild) so their behavior won't receive as much testing or scrutiny as the core commands (fetch and commit). Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 429 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 424 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index d8f103ed9a..d5c7e47967 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -35,8 +35,8 @@ my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_find_copies_harder, $_l, $_cp_similarity, $_repack, $_repack_nr, $_repack_flags, - $_template, $_shared, - $_version, $_upgrade, $_authors, $_branch_all_refs); + $_template, $_shared, $_no_default_regex, $_no_graft_copy, + $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m); my (@_branch_from, %tree_map, %users); my ($_svn_co_url_revs, $_svn_pg_peg_revs); my @repo_path_split_cache; @@ -48,6 +48,12 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, 'repack:i' => \$_repack, 'repack-flags|repack-args|repack-opts=s' => \$_repack_flags); +my ($_trunk, $_tags, $_branches); +my %multi_opts = ( 'trunk|T=s' => \$_trunk, + 'tags|t=s' => \$_tags, + 'branches|b=s' => \$_branches ); +my %init_opts = ( 'template=s' => \$_template, 'shared' => \$_shared ); + # yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome: my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" ); @@ -56,8 +62,7 @@ my %cmd = ( { 'revision|r=s' => \$_revision, %fc_opts } ], init => [ \&init, "Initialize a repo for tracking" . " (requires URL argument)", - { 'template=s' => \$_template, - 'shared' => \$_shared } ], + \%init_opts ], commit => [ \&commit, "Commit git revisions to SVN", { 'stdin|' => \$_stdin, 'edit|e' => \$_edit, @@ -71,7 +76,19 @@ my %cmd = ( rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)", { 'no-ignore-externals' => \$_no_ignore_ext, 'upgrade' => \$_upgrade } ], + 'graft-branches' => [ \&graft_branches, + 'Detect merges/branches from already imported history', + { 'merge-rx|m' => \@_opt_m, + 'no-default-regex' => \$_no_default_regex, + 'no-graft-copy' => \$_no_graft_copy } ], + 'multi-init' => [ \&multi_init, + 'Initialize multiple trees (like git-svnimport)', + { %multi_opts, %fc_opts } ], + 'multi-fetch' => [ \&multi_fetch, + 'Fetch multiple trees (like git-svnimport)', + \%fc_opts ], ); + my $cmd; for (my $i = 0; $i < @ARGV; $i++) { if (defined $cmd{$ARGV[$i]}) { @@ -96,7 +113,7 @@ init_vars(); load_authors() if $_authors; load_all_refs() if $_branch_all_refs; svn_compat_check(); -migration_check() unless $cmd eq 'init'; +migration_check() unless $cmd =~ /^(?:init|multi-init)$/; $cmd{$cmd}->[0]->(@ARGV); exit 0; @@ -219,6 +236,7 @@ when you have upgraded your tools and habits to use refs/remotes/$GIT_SVN sub init { $SVN_URL = shift or die "SVN repository location required " . "as a command-line argument\n"; + $SVN_URL =~ s!/+$!!; # strip trailing slash unless (-d $GIT_DIR) { my @init_db = ('git-init-db'); push @init_db, "--template=$_template" if defined $_template; @@ -358,8 +376,283 @@ sub show_ignore { } } +sub graft_branches { + my $gr_file = "$GIT_DIR/info/grafts"; + my ($grafts, $comments) = read_grafts($gr_file); + my $gr_sha1; + + if (%$grafts) { + # temporarily disable our grafts file to make this idempotent + chomp($gr_sha1 = safe_qx(qw/git-hash-object -w/,$gr_file)); + rename $gr_file, "$gr_file~$gr_sha1" or croak $!; + } + + my $l_map = read_url_paths(); + my @re = map { qr/$_/is } @_opt_m if @_opt_m; + unless ($_no_default_regex) { + push @re, ( qr/\b(?:merge|merging|merged)\s+(\S.+)/is, + qr/\b(?:from|of)\s+(\S.+)/is ); + } + foreach my $u (keys %$l_map) { + if (@re) { + foreach my $p (keys %{$l_map->{$u}}) { + graft_merge_msg($grafts,$l_map,$u,$p); + } + } + graft_file_copy($grafts,$l_map,$u) unless $_no_graft_copy; + } + + write_grafts($grafts, $comments, $gr_file); + unlink "$gr_file~$gr_sha1" if $gr_sha1; +} + +sub multi_init { + my $url = shift; + $_trunk ||= 'trunk'; + $_trunk =~ s#/+$##; + $url =~ s#/+$## if $url; + if ($_trunk !~ m#^[a-z\+]+://#) { + $_trunk = '/' . $_trunk if ($_trunk !~ m#^/#); + unless ($url) { + print STDERR "E: '$_trunk' is not a complete URL ", + "and a separate URL is not specified\n"; + exit 1; + } + $_trunk = $url . $_trunk; + } + if ($GIT_SVN eq 'git-svn') { + print "GIT_SVN_ID set to 'trunk' for $_trunk\n"; + $GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk'; + } + init_vars(); + init($_trunk); + complete_url_ls_init($url, $_branches, '--branches/-b', ''); + complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/'); +} + +sub multi_fetch { + # try to do trunk first, since branches/tags + # may be descended from it. + if (-d "$GIT_DIR/svn/trunk") { + print "Fetching trunk\n"; + defined(my $pid = fork) or croak $!; + if (!$pid) { + $GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk'; + init_vars(); + fetch(@_); + exit 0; + } + waitpid $pid, 0; + croak $? if $?; + } + rec_fetch('', "$GIT_DIR/svn", @_); +} + ########################### utility functions ######################### +sub rec_fetch { + my ($pfx, $p, @args) = @_; + my @dir; + foreach (sort <$p/*>) { + if (-r "$_/info/url") { + $pfx .= '/' if $pfx && $pfx !~ m!/$!; + my $id = $pfx . basename $_; + next if $id eq 'trunk'; + print "Fetching $id\n"; + defined(my $pid = fork) or croak $!; + if (!$pid) { + $GIT_SVN = $ENV{GIT_SVN_ID} = $id; + init_vars(); + fetch(@args); + exit 0; + } + waitpid $pid, 0; + croak $? if $?; + } elsif (-d $_) { + push @dir, $_; + } + } + foreach (@dir) { + my $x = $_; + $x =~ s!^\Q$GIT_DIR\E/svn/!!; + rec_fetch($x, $_); + } +} + +sub complete_url_ls_init { + my ($url, $var, $switch, $pfx) = @_; + unless ($var) { + print STDERR "W: $switch not specified\n"; + return; + } + $var =~ s#/+$##; + if ($var !~ m#^[a-z\+]+://#) { + $var = '/' . $var if ($var !~ m#^/#); + unless ($url) { + print STDERR "E: '$var' is not a complete URL ", + "and a separate URL is not specified\n"; + exit 1; + } + $var = $url . $var; + } + chomp(my @ls = safe_qx(qw/svn ls --non-interactive/, $var)); + my $old = $GIT_SVN; + defined(my $pid = fork) or croak $!; + if (!$pid) { + foreach my $u (map { "$var/$_" } (grep m!/$!, @ls)) { + $u =~ s#/+$##; + if ($u !~ m!\Q$var\E/(.+)$!) { + print STDERR "W: Unrecognized URL: $u\n"; + die "This should never happen\n"; + } + my $id = $pfx.$1; + print "init $u => $id\n"; + $GIT_SVN = $ENV{GIT_SVN_ID} = $id; + init_vars(); + init($u); + } + exit 0; + } + waitpid $pid, 0; + croak $? if $?; +} + +sub common_prefix { + my $paths = shift; + my %common; + foreach (@$paths) { + my @tmp = split m#/#, $_; + my $p = ''; + while (my $x = shift @tmp) { + $p .= "/$x"; + $common{$p} ||= 0; + $common{$p}++; + } + } + foreach (sort {length $b <=> length $a} keys %common) { + if ($common{$_} == @$paths) { + return $_; + } + } + return ''; +} + +# this isn't funky-filename safe, but good enough for now... +sub graft_file_copy { + my ($grafts, $l_map, $u) = @_; + my $paths = $l_map->{$u}; + my $pfx = common_prefix([keys %$paths]); + + my $pid = open my $fh, '-|'; + defined $pid or croak $!; + unless ($pid) { + exec(qw/svn log -v/, $u.$pfx) or croak $!; + } + my ($r, $mp) = (undef, undef); + while (<$fh>) { + chomp; + if (/^\-{72}$/) { + $mp = $r = undef; + } elsif (/^r(\d+) \| /) { + $r = $1 unless defined $r; + } elsif (/^Changed paths:/) { + $mp = 1; + } elsif ($mp && m#^ [AR] /(\S.*?) \(from /(\S+?):(\d+)\)$#) { + my $dbg = "r$r | $_"; + my ($p1, $p0, $r0) = ($1, $2, $3); + my $c; + foreach my $x (keys %$paths) { + next unless ($p1 =~ /^\Q$x\E/); + my $i = $paths->{$x}; + my $f = "$GIT_DIR/svn/$i/revs/$r"; + unless (-r $f) { + print STDERR "r$r of $i not imported,", + " $dbg\n"; + next; + } + $c = file_to_s($f); + } + next unless $c; + foreach my $x (keys %$paths) { + next unless ($p0 =~ /^\Q$x\E/); + my $i = $paths->{$x}; + my $f = "$GIT_DIR/svn/$i/revs/$r0"; + while ($r0 && !-r $f) { + # could be an older revision, too... + $r0--; + $f = "$GIT_DIR/svn/$i/revs/$r0"; + } + unless (-r $f) { + print STDERR "r$r0 of $i not imported,", + " $dbg\n"; + next; + } + my $r1 = file_to_s($f); + $grafts->{$c}->{$r1} = 1; + } + } + } +} + +sub process_merge_msg_matches { + my ($grafts, $l_map, $u, $p, $c, @matches) = @_; + my (@strong, @weak); + foreach (@matches) { + # merging with ourselves is not interesting + next if $_ eq $p; + if ($l_map->{$u}->{$_}) { + push @strong, $_; + } else { + push @weak, $_; + } + } + foreach my $w (@weak) { + last if @strong; + # no exact match, use branch name as regexp. + my $re = qr/\Q$w\E/i; + foreach (keys %{$l_map->{$u}}) { + if (/$re/) { + push @strong, $_; + last; + } + } + last if @strong; + $w = basename($w); + $re = qr/\Q$w\E/i; + foreach (keys %{$l_map->{$u}}) { + if (/$re/) { + push @strong, $_; + last; + } + } + } + my ($rev) = ($c->{m} =~ /^git-svn-id:\s(?:\S+?)\@(\d+) + \s(?:[a-f\d\-]+)$/xsm); + unless (defined $rev) { + ($rev) = ($c->{m} =~/^git-svn-id:\s(\d+) + \@(?:[a-f\d\-]+)/xsm); + return unless defined $rev; + } + foreach my $m (@strong) { + my ($r0, $s0) = find_rev_before($rev, $m); + $grafts->{$c->{c}}->{$s0} = 1 if defined $s0; + } +} + +sub graft_merge_msg { + my ($grafts, $l_map, $u, $p, @re) = @_; + + my $x = $l_map->{$u}->{$p}; + my $rl = rev_list_raw($x); + while (my $c = next_rev_list_entry($rl)) { + foreach my $re (@re) { + my (@br) = ($c->{m} =~ /$re/g); + next unless @br; + process_merge_msg_matches($grafts,$l_map,$u,$p,$c,@br); + } + } +} + sub read_uuid { return if $SVN_UUID; my $info = shift || svn_info('.'); @@ -402,6 +695,7 @@ sub repo_path_split { $url .= "/$n"; } push @repo_path_split_cache, qr/^(\Q$url\E)/; + $path = join('/',@paths); return ($url, $path); } @@ -806,6 +1100,38 @@ sub svn_commit_tree { return fetch("$committed=$commit")->{revision}; } +sub rev_list_raw { + my (@args) = @_; + my $pid = open my $fh, '-|'; + defined $pid or croak $!; + if (!$pid) { + exec(qw/git-rev-list --pretty=raw/, @args) or croak $!; + } + return { fh => $fh, t => { } }; +} + +sub next_rev_list_entry { + my $rl = shift; + my $fh = $rl->{fh}; + my $x = $rl->{t}; + while (<$fh>) { + if (/^commit ($sha1)$/o) { + if ($x->{c}) { + $rl->{t} = { c => $1 }; + return $x; + } else { + $x->{c} = $1; + } + } elsif (/^parent ($sha1)$/o) { + $x->{p}->{$1} = 1; + } elsif (s/^ //) { + $x->{m} ||= ''; + $x->{m} .= $_; + } + } + return ($x != $rl->{t}) ? $x : undef; +} + # read the entire log into a temporary file (which is removed ASAP) # and store the file handle + parser state sub svn_log_raw { @@ -1318,6 +1644,16 @@ sub svn_propget_base { return safe_qx(qw/svn propget/, $p, $f); } +sub git_svn_each { + my $sub = shift; + foreach (`git-rev-parse --symbolic --all`) { + next unless s#^refs/remotes/##; + chomp $_; + next unless -f "$GIT_DIR/svn/$_/info/url"; + &$sub($_); + } +} + sub migration_check { return if (-d "$GIT_DIR/svn" || !-d $GIT_DIR); print "Upgrading repository...\n"; @@ -1344,6 +1680,16 @@ sub migration_check { print "Done upgrading.\n"; } +sub find_rev_before { + my ($r, $git_svn_id) = @_; + my @revs = map { basename $_ } <$GIT_DIR/svn/$git_svn_id/revs/*>; + foreach my $r0 (sort { $b <=> $a } @revs) { + next if $r0 >= $r; + return ($r0, file_to_s("$GIT_DIR/svn/$git_svn_id/revs/$r0")); + } + return (undef, undef); +} + sub init_vars { $GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn'; $GIT_SVN_DIR = "$GIT_DIR/svn/$GIT_SVN"; @@ -1384,6 +1730,79 @@ sub set_default_vals { } } +sub read_grafts { + my $gr_file = shift; + my ($grafts, $comments) = ({}, {}); + if (open my $fh, '<', $gr_file) { + my @tmp; + while (<$fh>) { + if (/^($sha1)\s+/) { + my $c = $1; + if (@tmp) { + @{$comments->{$c}} = @tmp; + @tmp = (); + } + foreach my $p (split /\s+/, $_) { + $grafts->{$c}->{$p} = 1; + } + } else { + push @tmp, $_; + } + } + close $fh or croak $!; + @{$comments->{'END'}} = @tmp if @tmp; + } + return ($grafts, $comments); +} + +sub write_grafts { + my ($grafts, $comments, $gr_file) = @_; + + open my $fh, '>', $gr_file or croak $!; + foreach my $c (sort keys %$grafts) { + if ($comments->{$c}) { + print $fh $_ foreach @{$comments->{$c}}; + } + my $p = $grafts->{$c}; + delete $p->{$c}; # commits are not self-reproducing... + my $pid = open my $ch, '-|'; + defined $pid or croak $!; + if (!$pid) { + exec(qw/git-cat-file commit/, $c) or croak $!; + } + while (<$ch>) { + if (/^parent ([a-f\d]{40})/) { + $p->{$1} = 1; + } else { + last unless /^\S/i; + } + } + close $ch; # breaking the pipe + print $fh $c, ' ', join(' ', sort keys %$p),"\n"; + } + if ($comments->{'END'}) { + print $fh $_ foreach @{$comments->{'END'}}; + } + close $fh or croak $!; +} + +sub read_url_paths { + my $l_map = {}; + git_svn_each(sub { my $x = shift; + my $u = file_to_s("$GIT_DIR/svn/$x/info/repo_url"); + my $p = file_to_s("$GIT_DIR/svn/$x/info/repo_path"); + # we hate trailing slashes + if ($u =~ s#(?:^\/+|\/+$)##g) { + s_to_file($u,"$GIT_DIR/svn/$x/info/repo_url"); + } + if ($p =~ s#(?:^\/+|\/+$)##g) { + s_to_file($p,"$GIT_DIR/svn/$x/info/repo_path"); + } + $l_map->{$u}->{$p} = $x; + }); + return $l_map; +} + __END__ Data structures: -- cgit v1.2.3 From 7a97de4e19757b5576f32ce67d90cb792dbb893b Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 12 Jun 2006 05:57:02 -0700 Subject: git-svn: add UTF-8 message test Signed-off-by: Eric Wong --- contrib/git-svn/t/t0000-contrib-git-svn.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'contrib') diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh index a07fbad68b..0c6ff2066b 100644 --- a/contrib/git-svn/t/t0000-contrib-git-svn.sh +++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh @@ -4,6 +4,7 @@ # test_description='git-svn tests' +GIT_SVN_LC_ALL=$LC_ALL . ./lib-git-svn.sh mkdir import @@ -163,6 +164,18 @@ test_expect_success "$name" \ diff -u help $SVN_TREE/exec-2.sh" +if test -n "$GIT_SVN_LC_ALL" && echo $GIT_SVN_LC_ALL | grep -q '\.UTF-8$' +then + name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL" + echo '# hello' >> exec-2.sh + git update-index exec-2.sh + git commit -m 'éï∏' + export LC_ALL="$GIT_SVN_LC_ALL" + test_expect_success "$name" "git-svn commit HEAD" + unset LC_ALL +else + echo "UTF-8 locale not set, test skipped ($GIT_SVN_LC_ALL)" +fi name='test fetch functionality (svn => git) with alternate GIT_SVN_ID' GIT_SVN_ID=alt -- cgit v1.2.3 From 79bb8d88fc61b03a80fe99915f15a25172286c1f Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 1 Jun 2006 02:35:44 -0700 Subject: git-svn: add 'log' command, a facsimile of basic `svn log' This quick feature should make it easy to look up svn log messages when svn users refer to -r/--revision numbers. The following features from `svn log' are supported: --revision=[:] - is supported, non-numeric args are not: HEAD, NEXT, BASE, PREV, etc ... -v/--verbose - just maps to --raw (in git log), so it's completely incompatible with the --verbose output in svn log --limit= - is NOT the same as --max-count, doesn't count merged/excluded commits --incremental - supported (trivial :P) New features: --show-commit - shows the git commit sha1, as well --oneline - our version of --pretty=oneline Any other arguments are passed directly to `git log' Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 260 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 243 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index d5c7e47967..03416aeec1 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -15,6 +15,7 @@ $GIT_DIR = abs_path($ENV{GIT_DIR} || '.git'); $ENV{GIT_DIR} = $GIT_DIR; my $LC_ALL = $ENV{LC_ALL}; +my $TZ = $ENV{TZ}; # make sure the svn binary gives consistent output between locales and TZs: $ENV{TZ} = 'UTC'; $ENV{LC_ALL} = 'C'; @@ -27,7 +28,7 @@ use Carp qw/croak/; use IO::File qw//; use File::Basename qw/dirname basename/; use File::Path qw/mkpath/; -use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/; +use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/; use File::Spec qw//; use POSIX qw/strftime/; my $sha1 = qr/[a-f\d]{40}/; @@ -36,8 +37,9 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_find_copies_harder, $_l, $_cp_similarity, $_repack, $_repack_nr, $_repack_flags, $_template, $_shared, $_no_default_regex, $_no_graft_copy, + $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit, $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m); -my (@_branch_from, %tree_map, %users); +my (@_branch_from, %tree_map, %users, %rusers); my ($_svn_co_url_revs, $_svn_pg_peg_revs); my @repo_path_split_cache; @@ -87,6 +89,15 @@ my %cmd = ( 'multi-fetch' => [ \&multi_fetch, 'Fetch multiple trees (like git-svnimport)', \%fc_opts ], + 'log' => [ \&show_log, 'Show commit logs', + { 'limit=i' => \$_limit, + 'revision|r=s' => \$_revision, + 'verbose|v' => \$_verbose, + 'incremental' => \$_incremental, + 'oneline' => \$_oneline, + 'show-commit' => \$_show_commit, + 'authors-file|A=s' => \$_authors, + } ], ); my $cmd; @@ -101,9 +112,10 @@ for (my $i = 0; $i < @ARGV; $i++) { my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd); read_repo_config(\%opts); -GetOptions(%opts, 'help|H|h' => \$_help, - 'version|V' => \$_version, - 'id|i=s' => \$GIT_SVN) or exit 1; +my $rv = GetOptions(%opts, 'help|H|h' => \$_help, + 'version|V' => \$_version, + 'id|i=s' => \$GIT_SVN); +exit 1 if (!$rv && $cmd ne 'log'); set_default_vals(); usage(0) if $_help; @@ -173,18 +185,10 @@ sub rebuild { croak "Non-SHA1: $c\n" unless $c =~ /^$sha1$/o; my @commit = grep(/^git-svn-id: /,`git-cat-file commit $c`); next if (!@commit); # skip merges - my $id = $commit[$#commit]; - my ($url, $rev, $uuid) = ($id =~ /^git-svn-id:\s(\S+?)\@(\d+) - \s([a-f\d\-]+)$/x); - if (!$rev || !$uuid || !$url) { - # some of the original repositories I made had - # indentifiers like this: - ($rev, $uuid) = ($id =~/^git-svn-id:\s(\d+) - \@([a-f\d\-]+)/x); - if (!$rev || !$uuid) { - croak "Unable to extract revision or UUID from ", - "$c, $id\n"; - } + my ($url, $rev, $uuid) = extract_metadata($commit[$#commit]); + if (!$rev || !$uuid) { + croak "Unable to extract revision or UUID from ", + "$c, $commit[$#commit]\n"; } # if we merged or otherwise started elsewhere, this is @@ -448,6 +452,81 @@ sub multi_fetch { rec_fetch('', "$GIT_DIR/svn", @_); } +sub show_log { + my (@args) = @_; + my ($r_min, $r_max); + my $r_last = -1; # prevent dupes + rload_authors() if $_authors; + if (defined $TZ) { + $ENV{TZ} = $TZ; + } else { + delete $ENV{TZ}; + } + if (defined $_revision) { + if ($_revision =~ /^(\d+):(\d+)$/) { + ($r_min, $r_max) = ($1, $2); + } elsif ($_revision =~ /^\d+$/) { + $r_min = $r_max = $_revision; + } else { + print STDERR "-r$_revision is not supported, use ", + "standard \'git log\' arguments instead\n"; + exit 1; + } + } + + my $pid = open(my $log,'-|'); + defined $pid or croak $!; + if (!$pid) { + my @rl = (qw/git-log --abbrev-commit --pretty=raw + --default/, "remotes/$GIT_SVN"); + push @rl, '--raw' if $_verbose; + exec(@rl, @args) or croak $!; + } + setup_pager(); + my (@k, $c, $d); + while (<$log>) { + if (/^commit ($sha1_short)/o) { + my $cmt = $1; + if ($c && defined $c->{r} && $c->{r} != $r_last) { + $r_last = $c->{r}; + process_commit($c, $r_min, $r_max, \@k) or + goto out; + } + $d = undef; + $c = { c => $cmt }; + } elsif (/^author (.+) (\d+) ([\-\+]?\d+)$/) { + get_author_info($c, $1, $2, $3); + } elsif (/^(?:tree|parent|committer) /) { + # ignore + } elsif (/^:\d{6} \d{6} $sha1_short/o) { + push @{$c->{raw}}, $_; + } elsif (/^diff /) { + $d = 1; + push @{$c->{diff}}, $_; + } elsif ($d) { + push @{$c->{diff}}, $_; + } elsif (/^ (git-svn-id:.+)$/) { + my ($url, $rev, $uuid) = extract_metadata($1); + $c->{r} = $rev; + } elsif (s/^ //) { + push @{$c->{l}}, $_; + } + } + if ($c && defined $c->{r} && $c->{r} != $r_last) { + $r_last = $c->{r}; + process_commit($c, $r_min, $r_max, \@k); + } + if (@k) { + my $swap = $r_max; + $r_max = $r_min; + $r_min = $swap; + process_commit($_, $r_min, $r_max) foreach reverse @k; + } +out: + close $log; + print '-' x72,"\n" unless $_incremental || $_oneline; +} + ########################### utility functions ######################### sub rec_fetch { @@ -1638,6 +1717,17 @@ sub load_authors { close $authors or croak $!; } +sub rload_authors { + open my $authors, '<', $_authors or die "Can't open $_authors $!\n"; + while (<$authors>) { + chomp; + next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/; + my ($user, $name, $email) = ($1, $2, $3); + $rusers{"$name <$email>"} = $user; + } + close $authors or croak $!; +} + sub svn_propget_base { my ($p, $f) = @_; $f .= '@BASE' if $_svn_pg_peg_revs; @@ -1803,6 +1893,142 @@ sub read_url_paths { return $l_map; } +sub extract_metadata { + my $id = shift; + my ($url, $rev, $uuid) = ($id =~ /^git-svn-id:\s(\S+?)\@(\d+) + \s([a-f\d\-]+)$/x); + if (!$rev || !$uuid || !$url) { + # some of the original repositories I made had + # indentifiers like this: + ($rev, $uuid) = ($id =~/^git-svn-id:\s(\d+)\@([a-f\d\-]+)/); + } + return ($url, $rev, $uuid); +} + +sub tz_to_s_offset { + my ($tz) = @_; + $tz =~ s/(\d\d)$//; + return ($1 * 60) + ($tz * 3600); +} + +sub setup_pager { # translated to Perl from pager.c + return unless (-t *STDOUT); + my $pager = $ENV{PAGER}; + if (!defined $pager) { + $pager = 'less'; + } elsif (length $pager == 0 || $pager eq 'cat') { + return; + } + pipe my $rfd, my $wfd or return; + defined(my $pid = fork) or croak $!; + if (!$pid) { + open STDOUT, '>&', $wfd or croak $!; + return; + } + open STDIN, '<&', $rfd or croak $!; + $ENV{LESS} ||= '-S'; + exec $pager or croak "Can't run pager: $!\n";; +} + +sub get_author_info { + my ($dest, $author, $t, $tz) = @_; + $author =~ s/(?:^\s*|\s*$)//g; + my $_a; + if ($_authors) { + $_a = $rusers{$author} || undef; + } + if (!$_a) { + ($_a) = ($author =~ /<([^>]+)\@[^>]+>$/); + } + $dest->{t} = $t; + $dest->{tz} = $tz; + $dest->{a} = $_a; + # Date::Parse isn't in the standard Perl distro :( + if ($tz =~ s/^\+//) { + $t += tz_to_s_offset($tz); + } elsif ($tz =~ s/^\-//) { + $t -= tz_to_s_offset($tz); + } + $dest->{t_utc} = $t; +} + +sub process_commit { + my ($c, $r_min, $r_max, $defer) = @_; + if (defined $r_min && defined $r_max) { + if ($r_min == $c->{r} && $r_min == $r_max) { + show_commit($c); + return 0; + } + return 1 if $r_min == $r_max; + if ($r_min < $r_max) { + # we need to reverse the print order + return 0 if (defined $_limit && --$_limit < 0); + push @$defer, $c; + return 1; + } + if ($r_min != $r_max) { + return 1 if ($r_min < $c->{r}); + return 1 if ($r_max > $c->{r}); + } + } + return 0 if (defined $_limit && --$_limit < 0); + show_commit($c); + return 1; +} + +sub show_commit { + my $c = shift; + if ($_oneline) { + my $x = "\n"; + if (my $l = $c->{l}) { + while ($l->[0] =~ /^\s*$/) { shift @$l } + $x = $l->[0]; + } + $_l_fmt ||= 'A' . length($c->{r}); + print 'r',pack($_l_fmt, $c->{r}),' | '; + print "$c->{c} | " if $_show_commit; + print $x; + } else { + show_commit_normal($c); + } +} + +sub show_commit_normal { + my ($c) = @_; + print '-' x72, "\nr$c->{r} | "; + print "$c->{c} | " if $_show_commit; + print "$c->{a} | ", strftime("%Y-%m-%d %H:%M:%S %z (%a, %d %b %Y)", + localtime($c->{t_utc})), ' | '; + my $nr_line = 0; + + if (my $l = $c->{l}) { + while ($l->[$#$l] eq "\n" && $l->[($#$l - 1)] eq "\n") { + pop @$l; + } + $nr_line = scalar @$l; + if (!$nr_line) { + print "1 line\n\n\n"; + } else { + if ($nr_line == 1) { + $nr_line = '1 line'; + } else { + $nr_line .= ' lines'; + } + print $nr_line, "\n\n"; + print $_ foreach @$l; + } + } else { + print "1 line\n\n"; + + } + foreach my $x (qw/raw diff/) { + if ($c->{$x}) { + print "\n"; + print $_ foreach @{$c->{$x}} + } + } +} + __END__ Data structures: -- cgit v1.2.3 From a5e0cedc0a4d0018f3e7e4ba8ca54c91742dd859 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 12 Jun 2006 15:23:48 -0700 Subject: git-svn: add support for Perl SVN::* libraries This means we no longer have to deal with having bloated SVN working copies around and we get a nice performance increase as well because we don't have to exec the SVN binary and start a new server connection each time. Of course we have to manually manage memory with SVN::Pool whenever we can, and hack around cases where SVN just eats memory despite pools (I blame Perl, too). I would like to keep memory usage as stable as possible during long fetch/commit processes since I still use computers with only 256-512M RAM. commit should always be faster with the SVN library code. The SVN::Delta interface is leaky (or I'm not using it with pools correctly), so I'm forking on every commit, but that doesn't seem to hurt performance too much (at least on normal Unix/Linux systems where fork() is pretty cheap). fetch should be faster in most common cases, but probably not all. fetches will be faster where client/server delta generation is the bottleneck and not bandwidth. Of course, full-files are generated server-side via deltas, too. Full files are always transferred when they're updated, just like git-svnimport and unlike command-line svn. I'm also hacking around memory leaks (see comments) here by using some more forks. I've tested fetch with http://, https://, file://, and svn:// repositories, so we should be reasonably covered in terms of error handling for fetching. Of course, we'll keep plain command-line svn compatibility as a fallback for people running SVN 1.1 (I'm looking into library support for 1.1.x SVN, too). If you want to force command-line SVN usage, set GIT_SVN_NO_LIB=1 in your environment. We also require two simultaneous connections (just like git-svnimport), but this shouldn't be a problem for most servers. Less important commands: show-ignore is slower because it requires repository access, but -r/--revision can be specified. graft-branches may use more memory, but it's a short-term process and is funky-filename-safe. Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 1068 +++++++++++++++++++++++++--- contrib/git-svn/t/lib-git-svn.sh | 2 +- contrib/git-svn/t/t0000-contrib-git-svn.sh | 15 +- 3 files changed, 974 insertions(+), 111 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 03416aeec1..9618c8bab5 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -31,6 +31,10 @@ use File::Path qw/mkpath/; use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/; use File::Spec qw//; use POSIX qw/strftime/; + +my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib); +$_use_lib = 1 unless $ENV{GIT_SVN_NO_LIB}; +libsvn_load(); my $sha1 = qr/[a-f\d]{40}/; my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, @@ -74,7 +78,8 @@ my %cmd = ( 'copy-similarity|C=i'=> \$_cp_similarity, %fc_opts, } ], - 'show-ignore' => [ \&show_ignore, "Show svn:ignore listings", { } ], + 'show-ignore' => [ \&show_ignore, "Show svn:ignore listings", + { 'revision|r=i' => \$_revision } ], rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)", { 'no-ignore-externals' => \$_no_ignore_ext, 'upgrade' => \$_upgrade } ], @@ -211,6 +216,8 @@ sub rebuild { $newest_rev = $rev if ($rev > $newest_rev); } close $rev_list or croak $?; + + goto out if $_use_lib; if (!chdir $SVN_WC) { svn_cmd_checkout($SVN_URL, $latest, $SVN_WC); chdir $SVN_WC or croak $!; @@ -228,7 +235,7 @@ sub rebuild { } waitpid $pid, 0; croak $? if $?; - +out: if ($_upgrade) { print STDERR <<""; Keeping deprecated refs/head/$GIT_SVN-HEAD for now. Please remove it @@ -251,9 +258,18 @@ sub init { } sub fetch { - my (@parents) = @_; check_upgrade_needed(); $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url"); + my $ret = $_use_lib ? fetch_lib(@_) : fetch_cmd(@_); + if ($ret->{commit} && quiet_run(qw(git-rev-parse --verify + refs/heads/master^0))) { + sys(qw(git-update-ref refs/heads/master),$ret->{commit}); + } + return $ret; +} + +sub fetch_cmd { + my (@parents) = @_; my @log_args = -d $SVN_WC ? ($SVN_WC) : ($SVN_URL); unless ($_revision) { $_revision = -d $SVN_WC ? 'BASE:HEAD' : '0:HEAD'; @@ -301,13 +317,91 @@ sub fetch { $last_commit = git_commit($log_msg, $last_commit, @parents); $last = $log_msg; } - unless (-e "$GIT_DIR/refs/heads/master") { - sys(qw(git-update-ref refs/heads/master),$last_commit); - } close $svn_log->{fh}; + $last->{commit} = $last_commit; return $last; } +sub fetch_lib { + my (@parents) = @_; + $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url"); + my $repo; + ($repo, $SVN_PATH) = repo_path_split($SVN_URL); + $SVN_LOG ||= libsvn_connect($repo); + $SVN ||= libsvn_connect($repo); + my ($last_rev, $last_commit) = svn_grab_base_rev(); + my ($base, $head) = libsvn_parse_revision($last_rev); + if ($base > $head) { + return { revision => $last_rev, commit => $last_commit } + } + my $index = set_index($GIT_SVN_INDEX); + + # limit ourselves and also fork() since get_log won't release memory + # after processing a revision and SVN stuff seems to leak + my $inc = 1000; + my ($min, $max) = ($base, $head < $base+$inc ? $head : $base+$inc); + read_uuid(); + if (defined $last_commit) { + unless (-e $GIT_SVN_INDEX) { + sys(qw/git-read-tree/, $last_commit); + } + chomp (my $x = `git-write-tree`); + my ($y) = (`git-cat-file commit $last_commit` + =~ /^tree ($sha1)/m); + if ($y ne $x) { + unlink $GIT_SVN_INDEX or croak $!; + sys(qw/git-read-tree/, $last_commit); + } + chomp ($x = `git-write-tree`); + if ($y ne $x) { + print STDERR "trees ($last_commit) $y != $x\n", + "Something is seriously wrong...\n"; + } + } + while (1) { + # fork, because using SVN::Pool with get_log() still doesn't + # seem to help enough to keep memory usage down. + defined(my $pid = fork) or croak $!; + if (!$pid) { + $SVN::Error::handler = \&libsvn_skip_unknown_revs; + print "Fetching revisions $min .. $max\n"; + + # Yes I'm perfectly aware that the fourth argument + # below is the limit revisions number. Unfortunately + # performance sucks with it enabled, so it's much + # faster to fetch revision ranges instead of relying + # on the limiter. + $SVN_LOG->get_log( '/'.$SVN_PATH, $min, $max, 0, 1, 1, + sub { + my $log_msg; + if ($last_commit) { + $log_msg = libsvn_fetch( + $last_commit, @_); + $last_commit = git_commit( + $log_msg, + $last_commit, + @parents); + } else { + $log_msg = libsvn_new_tree(@_); + $last_commit = git_commit( + $log_msg, @parents); + } + }); + $SVN::Error::handler = sub { 'quiet warnings' }; + exit 0; + } + waitpid $pid, 0; + croak $? if $?; + ($last_rev, $last_commit) = svn_grab_base_rev(); + last if ($max >= $head); + $min = $max + 1; + $max += $inc; + $max = $head if ($max > $head); + } + restore_index($index); + return { revision => $last_rev, commit => $last_commit }; +} + sub commit { my (@commits) = @_; check_upgrade_needed(); @@ -332,6 +426,12 @@ sub commit { } } chomp @revs; + $_use_lib ? commit_lib(@revs) : commit_cmd(@revs); + print "Done committing ",scalar @revs," revisions to SVN\n"; +} + +sub commit_cmd { + my (@revs) = @_; chdir $SVN_WC or croak "Unable to chdir $SVN_WC: $!\n"; my $info = svn_info('.'); @@ -353,17 +453,95 @@ sub commit { } $svn_current_rev = svn_commit_tree($svn_current_rev, $c); } - print "Done committing ",scalar @revs," revisions to SVN\n"; } -sub show_ignore { - require File::Find or die $!; - my $exclude_file = "$GIT_DIR/info/exclude"; - open my $fh, '<', $exclude_file or croak $!; - chomp(my @excludes = (<$fh>)); - close $fh or croak $!; +sub commit_lib { + my (@revs) = @_; + my ($r_last, $cmt_last) = svn_grab_base_rev(); + defined $r_last or die "Must have an existing revision to commit\n"; + my $fetched = fetch_lib(); + if ($r_last != $fetched->{revision}) { + print STDERR "There are new revisions that were fetched ", + "and need to be merged (or acknowledged) ", + "before committing.\n", + "last rev: $r_last\n", + " current: $fetched->{revision}\n"; + exit 1; + } + read_uuid(); + my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : (); + my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$"; + + foreach my $c (@revs) { + # fork for each commit because there's a memory leak I + # can't track down... (it's probably in the SVN code) + defined(my $pid = open my $fh, '-|') or croak $!; + if (!$pid) { + if (defined $LC_ALL) { + $ENV{LC_ALL} = $LC_ALL; + } else { + delete $ENV{LC_ALL}; + } + my $log_msg = get_commit_message($c, $commit_msg); + my $ed = SVN::Git::Editor->new( + { r => $r_last, + ra => $SVN, + c => $c, + svn_path => $SVN_PATH + }, + $SVN->get_commit_editor( + $log_msg->{msg}, + sub { + libsvn_commit_cb( + @_, $c, + $log_msg->{msg}, + $r_last, + $cmt_last) + }, + @lock) + ); + my $mods = libsvn_checkout_tree($r_last, $c, $ed); + if (@$mods == 0) { + print "No changes\nr$r_last = $cmt_last\n"; + $ed->abort_edit; + } else { + $ed->close_edit; + } + exit 0; + } + my ($r_new, $cmt_new, $no); + while (<$fh>) { + print $_; + chomp; + if (/^r(\d+) = ($sha1)$/o) { + ($r_new, $cmt_new) = ($1, $2); + } elsif ($_ eq 'No changes') { + $no = 1; + } + } + close $fh or croak $!; + if (! defined $r_new && ! defined $cmt_new) { + unless ($no) { + die "Failed to parse revision information\n"; + } + } else { + ($r_last, $cmt_last) = ($r_new, $cmt_new); + } + } + unlink $commit_msg; +} +sub show_ignore { $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url"); + $_use_lib ? show_ignore_lib() : show_ignore_cmd(); +} + +sub show_ignore_cmd { + require File::Find or die $!; + if (defined $_revision) { + die "-r/--revision option doesn't work unless the Perl SVN ", + "libraries are used\n"; + } chdir $SVN_WC or croak $!; my %ign; File::Find::find({wanted=>sub{if(lstat $_ && -d _ && -d "$_/.svn"){ @@ -380,6 +558,14 @@ sub show_ignore { } } +sub show_ignore_lib { + my $repo; + ($repo, $SVN_PATH) = repo_path_split($SVN_URL); + $SVN ||= libsvn_connect($repo); + my $r = defined $_revision ? $_revision : $SVN->get_latest_revnum; + libsvn_traverse_ignore(\*STDOUT, $SVN_PATH, $r); +} + sub graft_branches { my $gr_file = "$GIT_DIR/info/grafts"; my ($grafts, $comments) = read_grafts($gr_file); @@ -403,7 +589,13 @@ sub graft_branches { graft_merge_msg($grafts,$l_map,$u,$p); } } - graft_file_copy($grafts,$l_map,$u) unless $_no_graft_copy; + unless ($_no_graft_copy) { + if ($_use_lib) { + graft_file_copy_lib($grafts,$l_map,$u); + } else { + graft_file_copy_cmd($grafts,$l_map,$u); + } + } } write_grafts($grafts, $comments, $gr_file); @@ -574,7 +766,8 @@ sub complete_url_ls_init { } $var = $url . $var; } - chomp(my @ls = safe_qx(qw/svn ls --non-interactive/, $var)); + chomp(my @ls = $_use_lib ? libsvn_ls_fullurl($var) + : safe_qx(qw/svn ls --non-interactive/, $var)); my $old = $GIT_SVN; defined(my $pid = fork) or croak $!; if (!$pid) { @@ -617,7 +810,7 @@ sub common_prefix { } # this isn't funky-filename safe, but good enough for now... -sub graft_file_copy { +sub graft_file_copy_cmd { my ($grafts, $l_map, $u) = @_; my $paths = $l_map->{$u}; my $pfx = common_prefix([keys %$paths]); @@ -625,7 +818,9 @@ sub graft_file_copy { my $pid = open my $fh, '-|'; defined $pid or croak $!; unless ($pid) { - exec(qw/svn log -v/, $u.$pfx) or croak $!; + my @exec = qw/svn log -v/; + push @exec, "-r$_revision" if defined $_revision; + exec @exec, $u.$pfx or croak $!; } my ($r, $mp) = (undef, undef); while (<$fh>) { @@ -637,42 +832,40 @@ sub graft_file_copy { } elsif (/^Changed paths:/) { $mp = 1; } elsif ($mp && m#^ [AR] /(\S.*?) \(from /(\S+?):(\d+)\)$#) { - my $dbg = "r$r | $_"; my ($p1, $p0, $r0) = ($1, $2, $3); - my $c; - foreach my $x (keys %$paths) { - next unless ($p1 =~ /^\Q$x\E/); - my $i = $paths->{$x}; - my $f = "$GIT_DIR/svn/$i/revs/$r"; - unless (-r $f) { - print STDERR "r$r of $i not imported,", - " $dbg\n"; - next; - } - $c = file_to_s($f); - } + my $c = find_graft_path_commit($paths, $p1, $r); next unless $c; - foreach my $x (keys %$paths) { - next unless ($p0 =~ /^\Q$x\E/); - my $i = $paths->{$x}; - my $f = "$GIT_DIR/svn/$i/revs/$r0"; - while ($r0 && !-r $f) { - # could be an older revision, too... - $r0--; - $f = "$GIT_DIR/svn/$i/revs/$r0"; - } - unless (-r $f) { - print STDERR "r$r0 of $i not imported,", - " $dbg\n"; - next; - } - my $r1 = file_to_s($f); - $grafts->{$c}->{$r1} = 1; - } + find_graft_path_parents($grafts, $paths, $c, $p0, $r0); } } } +sub graft_file_copy_lib { + my ($grafts, $l_map, $u) = @_; + my $tree_paths = $l_map->{$u}; + my $pfx = common_prefix([keys %$tree_paths]); + my ($repo, $path) = repo_path_split($u.$pfx); + $SVN_LOG ||= libsvn_connect($repo); + $SVN ||= libsvn_connect($repo); + + my ($base, $head) = libsvn_parse_revision(); + my $inc = 1000; + my ($min, $max) = ($base, $head < $base+$inc ? $head : $base+$inc); + while (1) { + my $pool = SVN::Pool->new; + $SVN_LOG->get_log( "/$path", $min, $max, 0, 1, 1, + sub { + libsvn_graft_file_copies($grafts, $tree_paths, + $path, @_); + }, $pool); + $pool->clear; + last if ($max >= $head); + $min = $max + 1; + $max += $inc; + $max = $head if ($max > $head); + } +} + sub process_merge_msg_matches { my ($grafts, $l_map, $u, $p, $c, @matches) = @_; my (@strong, @weak); @@ -734,9 +927,15 @@ sub graft_merge_msg { sub read_uuid { return if $SVN_UUID; - my $info = shift || svn_info('.'); - $SVN_UUID = $info->{'Repository UUID'} or + if ($_use_lib) { + my $pool = SVN::Pool->new; + $SVN_UUID = $SVN->get_uuid($pool); + $pool->clear; + } else { + my $info = shift || svn_info('.'); + $SVN_UUID = $info->{'Repository UUID'} or croak "Repository UUID unreadable\n"; + } s_to_file($SVN_UUID,"$GIT_SVN_DIR/info/uuid"); } @@ -769,9 +968,19 @@ sub repo_path_split { $path =~ s#^/+##; my @paths = split(m#/+#, $path); - while (quiet_run(qw/svn ls --non-interactive/, $url)) { - my $n = shift @paths || last; - $url .= "/$n"; + if ($_use_lib) { + while (1) { + $SVN = libsvn_connect($url); + last if (defined $SVN && + defined eval { $SVN->get_latest_revnum }); + my $n = shift @paths || last; + $url .= "/$n"; + } + } else { + while (quiet_run(qw/svn ls --non-interactive/, $url)) { + my $n = shift @paths || last; + $url .= "/$n"; + } } push @repo_path_split_cache, qr/^(\Q$url\E)/; $path = join('/',@paths); @@ -797,6 +1006,7 @@ sub setup_git_svn { } sub assert_svn_wc_clean { + return if $_use_lib; my ($svn_rev) = @_; croak "$svn_rev is not an integer!\n" unless ($svn_rev =~ /^\d+$/); my $lcr = svn_info('.')->{'Last Changed Rev'}; @@ -819,7 +1029,7 @@ sub assert_svn_wc_clean { } } -sub assert_tree { +sub get_tree_from_treeish { my ($treeish) = @_; croak "Not a sha1: $treeish\n" unless $treeish =~ /^$sha1$/o; chomp(my $type = `git-cat-file -t $treeish`); @@ -836,20 +1046,22 @@ sub assert_tree { } else { die "$treeish is a $type, expected tree, tag or commit\n"; } + return $expected; +} + +sub assert_tree { + return if $_use_lib; + my ($treeish) = @_; + my $expected = get_tree_from_treeish($treeish); - my $old_index = $ENV{GIT_INDEX_FILE}; my $tmpindex = $GIT_SVN_INDEX.'.assert-tmp'; if (-e $tmpindex) { unlink $tmpindex or croak $!; } - $ENV{GIT_INDEX_FILE} = $tmpindex; + my $old_index = set_index($tmpindex); index_changes(1); chomp(my $tree = `git-write-tree`); - if ($old_index) { - $ENV{GIT_INDEX_FILE} = $old_index; - } else { - delete $ENV{GIT_INDEX_FILE}; - } + restore_index($old_index); if ($tree ne $expected) { croak "Tree mismatch, Got: $tree, Expected: $expected\n"; } @@ -987,7 +1199,8 @@ sub precommit_check { } } -sub svn_checkout_tree { + +sub get_diff { my ($svn_rev, $treeish) = @_; my $from = file_to_s("$REV_DIR/$svn_rev"); assert_tree($from); @@ -1005,11 +1218,13 @@ sub svn_checkout_tree { push @diff_tree, "-l$_l" if defined $_l; exec(@diff_tree, $from, $treeish) or croak $!; } - my $mods = parse_diff_tree($diff_fh); - unless (@$mods) { - # git can do empty commits, but SVN doesn't allow it... - return $mods; - } + return parse_diff_tree($diff_fh); +} + +sub svn_checkout_tree { + my ($svn_rev, $treeish) = @_; + my $mods = get_diff($svn_rev, $treeish); + return $mods unless (scalar @$mods); my ($rm, $add) = precommit_check($mods); my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 ); @@ -1052,6 +1267,23 @@ sub svn_checkout_tree { return $mods; } +sub libsvn_checkout_tree { + my ($svn_rev, $treeish, $ed) = @_; + my $mods = get_diff($svn_rev, $treeish); + return $mods unless (scalar @$mods); + my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 ); + foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) { + my $f = $m->{chg}; + if (defined $o{$f}) { + $ed->$f($m); + } else { + croak "Invalid change type: $f\n"; + } + } + $ed->rmdirs if $_rmdir; + return $mods; +} + # svn ls doesn't work with respect to the current working tree, but what's # in the repository. There's not even an option for it... *sigh* # (added files don't show up and removed files remain in the ls listing) @@ -1090,12 +1322,12 @@ sub handle_rmdir { } } -sub svn_commit_tree { - my ($svn_rev, $commit) = @_; - my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$"; +sub get_commit_message { + my ($commit, $commit_msg) = (@_); my %log_msg = ( msg => '' ); open my $msg, '>', $commit_msg or croak $!; + print "commit: $commit\n"; chomp(my $type = `git-cat-file -t $commit`); if ($type eq 'commit') { my $pid = open my $msg_fh, '-|'; @@ -1129,7 +1361,14 @@ sub svn_commit_tree { { local $/; chomp($log_msg{msg} = <$msg>); } close $msg or croak $!; - my ($oneline) = ($log_msg{msg} =~ /([^\n\r]+)/); + return \%log_msg; +} + +sub svn_commit_tree { + my ($svn_rev, $commit) = @_; + my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$"; + my $log_msg = get_commit_message($commit, $commit_msg); + my ($oneline) = ($log_msg->{msg} =~ /([^\n\r]+)/); print "Committing $commit: $oneline\n"; if (defined $LC_ALL) { @@ -1165,12 +1404,12 @@ sub svn_commit_tree { /(\d{4})\-(\d\d)\-(\d\d)\s (\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x) or croak "Failed to parse date: $date\n"; - $log_msg{date} = "$tz $Y-$m-$d $H:$M:$S"; - $log_msg{author} = $info->{'Last Changed Author'}; - $log_msg{revision} = $committed; - $log_msg{msg} .= "\n"; + $log_msg->{date} = "$tz $Y-$m-$d $H:$M:$S"; + $log_msg->{author} = $info->{'Last Changed Author'}; + $log_msg->{revision} = $committed; + $log_msg->{msg} .= "\n"; my $parent = file_to_s("$REV_DIR/$svn_rev"); - git_commit(\%log_msg, $parent, $commit); + git_commit($log_msg, $parent, $commit); return $committed; } # resync immediately @@ -1335,8 +1574,14 @@ sub eol_cp { binmode $rfd or croak $!; open my $wfd, '>', $to or croak $!; binmode $wfd or croak $!; + eol_cp_fd($rfd, $wfd, $es); + close $rfd or croak $!; + close $wfd or croak $!; +} - my $eol = $EOL{$es} or undef; +sub eol_cp_fd { + my ($rfd, $wfd, $es) = @_; + my $eol = defined $es ? $EOL{$es} : undef; my $buf; use bytes; while (1) { @@ -1396,6 +1641,7 @@ sub do_update_index { } sub index_changes { + return if $_use_lib; my $no_text_base = shift; do_update_index([qw/git-diff-files --name-only -z/], 'remove', @@ -1459,63 +1705,59 @@ sub assert_revision_eq_or_unknown { sub git_commit { my ($log_msg, @parents) = @_; assert_revision_unknown($log_msg->{revision}); - my $out_fh = IO::File->new_tmpfile or croak $!; - map_tree_joins() if (@_branch_from && !%tree_map); + my (@tmp_parents, @exec_parents, %seen_parent); + if (my $lparents = $log_msg->{parents}) { + @tmp_parents = @$lparents + } # commit parents can be conditionally bound to a particular # svn revision via: "svn_revno=commit_sha1", filter them out here: - my @exec_parents; foreach my $p (@parents) { next unless defined $p; if ($p =~ /^(\d+)=($sha1_short)$/o) { if ($1 == $log_msg->{revision}) { - push @exec_parents, $2; + push @tmp_parents, $2; } } else { - push @exec_parents, $p if $p =~ /$sha1_short/o; + push @tmp_parents, $p if $p =~ /$sha1_short/o; } } - - my $pid = fork; - defined $pid or croak $!; - if ($pid == 0) { - $ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX; + my $tree = $log_msg->{tree}; + if (!defined $tree) { + my $index = set_index($GIT_SVN_INDEX); index_changes(); - chomp(my $tree = `git-write-tree`); + chomp($tree = `git-write-tree`); croak $? if $?; - if (exists $tree_map{$tree}) { - my %seen_parent = map { $_ => 1 } @exec_parents; - foreach (@{$tree_map{$tree}}) { - # MAXPARENT is defined to 16 in commit-tree.c: - if ($seen_parent{$_} || @exec_parents > 16) { - next; - } - push @exec_parents, $_; - $seen_parent{$_} = 1; - } - } + restore_index($index); + } + if (exists $tree_map{$tree}) { + push @tmp_parents, @{$tree_map{$tree}}; + } + foreach (@tmp_parents) { + next if $seen_parent{$_}; + $seen_parent{$_} = 1; + push @exec_parents, $_; + # MAXPARENT is defined to 16 in commit-tree.c: + last if @exec_parents > 16; + } + + defined(my $pid = open my $out_fh, '-|') or croak $!; + if ($pid == 0) { my $msg_fh = IO::File->new_tmpfile or croak $!; print $msg_fh $log_msg->{msg}, "\ngit-svn-id: ", "$SVN_URL\@$log_msg->{revision}", " $SVN_UUID\n" or croak $!; $msg_fh->flush == 0 or croak $!; seek $msg_fh, 0, 0 or croak $!; - set_commit_env($log_msg); - my @exec = ('git-commit-tree',$tree); push @exec, '-p', $_ foreach @exec_parents; open STDIN, '<&', $msg_fh or croak $!; - open STDOUT, '>&', $out_fh or croak $!; exec @exec or croak $!; } - waitpid($pid,0); - croak $? if $?; - - $out_fh->flush == 0 or croak $!; - seek $out_fh, 0, 0 or croak $!; chomp(my $commit = do { local $/; <$out_fh> }); + close $out_fh or croak $?; if ($commit !~ /^$sha1$/o) { croak "Failed to commit, invalid sha1: $commit\n"; } @@ -1534,6 +1776,7 @@ sub git_commit { } sys(@update_ref); sys('git-update-ref',"svn/$GIT_SVN/revs/$log_msg->{revision}",$commit); + # this output is read via pipe, do not change: print "r$log_msg->{revision} = $commit\n"; if ($_repack && (--$_repack_nr == 0)) { $_repack_nr = $_repack; @@ -1545,6 +1788,9 @@ sub git_commit { sub set_commit_env { my ($log_msg) = @_; my $author = $log_msg->{author}; + if (!defined $author || length $author == 0) { + $author = '(no author)'; + } my ($name,$email) = defined $users{$author} ? @{$users{$author}} : ($author,"$author\@$SVN_UUID"); $ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $name; @@ -2029,6 +2275,612 @@ sub show_commit_normal { } } +sub libsvn_load { + return unless $_use_lib; + $_use_lib = eval { + require SVN::Core; + if ($SVN::Core::VERSION lt '1.2.1') { + die "Need SVN::Core 1.2.1 or better ", + "(got $SVN::Core::VERSION) ", + "Falling back to command-line svn\n"; + } + require SVN::Ra; + require SVN::Delta; + push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor'; + my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file. + $SVN::Node::dir.$SVN::Node::unknown. + $SVN::Node::none.$SVN::Node::file. + $SVN::Node::dir.$SVN::Node::unknown; + 1; + }; +} + +sub libsvn_connect { + my ($url) = @_; + my $auth = SVN::Core::auth_open([SVN::Client::get_simple_provider(), + SVN::Client::get_ssl_server_trust_file_provider(), + SVN::Client::get_username_provider()]); + my $s = eval { SVN::Ra->new(url => $url, auth => $auth) }; + return $s; +} + +sub libsvn_get_file { + my ($gui, $f, $rev) = @_; + my $p = $f; + return unless ($p =~ s#^\Q$SVN_PATH\E/?##); + + my $fd = IO::File->new_tmpfile or croak $!; + my $pool = SVN::Pool->new; + my ($r, $props) = $SVN->get_file($f, $rev, $fd, $pool); + $pool->clear; + $fd->flush == 0 or croak $!; + seek $fd, 0, 0 or croak $!; + if (my $es = $props->{'svn:eol-style'}) { + my $new_fd = IO::File->new_tmpfile or croak $!; + eol_cp_fd($fd, $new_fd, $es); + close $fd or croak $!; + $fd = $new_fd; + seek $fd, 0, 0 or croak $!; + $fd->flush == 0 or croak $!; + } + my $mode = '100644'; + if (exists $props->{'svn:executable'}) { + $mode = '100755'; + } + if (exists $props->{'svn:special'}) { + $mode = '120000'; + local $/; + my $link = <$fd>; + $link =~ s/^link // or die "svn:special file with contents: <", + $link, "> is not understood\n"; + seek $fd, 0, 0 or croak $!; + truncate $fd, 0 or croak $!; + print $fd $link or croak $!; + seek $fd, 0, 0 or croak $!; + $fd->flush == 0 or croak $!; + } + my $pid = open my $ho, '-|'; + defined $pid or croak $!; + if (!$pid) { + open STDIN, '<&', $fd or croak $!; + exec qw/git-hash-object -w --stdin/ or croak $!; + } + chomp(my $hash = do { local $/; <$ho> }); + close $ho or croak $?; + $hash =~ /^$sha1$/o or die "not a sha1: $hash\n"; + print $gui $mode,' ',$hash,"\t",$p,"\0" or croak $!; + close $fd or croak $!; +} + +sub libsvn_log_entry { + my ($rev, $author, $date, $msg, $parents) = @_; + my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T + (\d\d)\:(\d\d)\:(\d\d).\d+Z$/x) + or die "Unable to parse date: $date\n"; + if (defined $_authors && ! defined $users{$author}) { + die "Author: $author not defined in $_authors file\n"; + } + return { revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S", + author => $author, msg => $msg."\n", parents => $parents || [] } +} + +sub process_rm { + my ($gui, $last_commit, $f) = @_; + $f =~ s#^\Q$SVN_PATH\E/?## or return; + # remove entire directories. + if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) { + defined(my $pid = open my $ls, '-|') or croak $!; + if (!$pid) { + exec(qw/git-ls-tree -r --name-only -z/, + $last_commit,'--',$f) or croak $!; + } + local $/ = "\0"; + while (<$ls>) { + print $gui '0 ',0 x 40,"\t",$_ or croak $!; + } + close $ls or croak $!; + } else { + print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!; + } +} + +sub libsvn_fetch { + my ($last_commit, $paths, $rev, $author, $date, $msg) = @_; + open my $gui, '| git-update-index -z --index-info' or croak $!; + my @amr; + foreach my $f (keys %$paths) { + my $m = $paths->{$f}->action(); + $f =~ s#^/+##; + if ($m =~ /^[DR]$/) { + process_rm($gui, $last_commit, $f); + next if $m eq 'D'; + # 'R' can be file replacements, too, right? + } + my $pool = SVN::Pool->new; + my $t = $SVN->check_path($f, $rev, $pool); + if ($t == $SVN::Node::file) { + if ($m =~ /^[AMR]$/) { + push @amr, $f; + } else { + die "Unrecognized action: $m, ($f r$rev)\n"; + } + } + $pool->clear; + } + libsvn_get_file($gui, $_, $rev) foreach (@amr); + close $gui or croak $!; + return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]); +} + +sub svn_grab_base_rev { + defined(my $pid = open my $fh, '-|') or croak $!; + if (!$pid) { + open my $null, '>', '/dev/null' or croak $!; + open STDERR, '>&', $null or croak $!; + exec qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0" + or croak $!; + } + chomp(my $c = do { local $/; <$fh> }); + close $fh; + if (defined $c && length $c) { + my ($url, $rev, $uuid) = extract_metadata((grep(/^git-svn-id: /, + safe_qx(qw/git-cat-file commit/, $c)))[0]); + return ($rev, $c); + } + return (undef, undef); +} + +sub libsvn_parse_revision { + my $base = shift; + my $head = $SVN->get_latest_revnum(); + if (!defined $_revision || $_revision eq 'BASE:HEAD') { + return ($base + 1, $head) if (defined $base); + return (0, $head); + } + return ($1, $2) if ($_revision =~ /^(\d+):(\d+)$/); + return ($_revision, $_revision) if ($_revision =~ /^\d+$/); + if ($_revision =~ /^BASE:(\d+)$/) { + return ($base + 1, $1) if (defined $base); + return (0, $head); + } + return ($1, $head) if ($_revision =~ /^(\d+):HEAD$/); + die "revision argument: $_revision not understood by git-svn\n", + "Try using the command-line svn client instead\n"; +} + +sub libsvn_traverse { + my ($gui, $pfx, $path, $rev) = @_; + my $cwd = "$pfx/$path"; + my $pool = SVN::Pool->new; + $cwd =~ s#^/+##g; + my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool); + foreach my $d (keys %$dirent) { + my $t = $dirent->{$d}->kind; + if ($t == $SVN::Node::dir) { + libsvn_traverse($gui, $cwd, $d, $rev); + } elsif ($t == $SVN::Node::file) { + libsvn_get_file($gui, "$cwd/$d", $rev); + } + } + $pool->clear; +} + +sub libsvn_traverse_ignore { + my ($fh, $path, $r) = @_; + $path =~ s#^/+##g; + my $pool = SVN::Pool->new; + my ($dirent, undef, $props) = $SVN->get_dir($path, $r, $pool); + my $p = $path; + $p =~ s#^\Q$SVN_PATH\E/?##; + print $fh length $p ? "\n# $p\n" : "\n# /\n"; + if (my $s = $props->{'svn:ignore'}) { + $s =~ s/[\r\n]+/\n/g; + chomp $s; + if (length $p == 0) { + $s =~ s#\n#\n/$p#g; + print $fh "/$s\n"; + } else { + $s =~ s#\n#\n/$p/#g; + print $fh "/$p/$s\n"; + } + } + foreach (sort keys %$dirent) { + next if $dirent->{$_}->kind != $SVN::Node::dir; + libsvn_traverse_ignore($fh, "$path/$_", $r); + } + $pool->clear; +} + +sub libsvn_new_tree { + my ($paths, $rev, $author, $date, $msg) = @_; + my $svn_path = '/'.$SVN_PATH; + + # look for a parent from another branch: + foreach (keys %$paths) { + next if ($_ ne $svn_path); + my $i = $paths->{$_}; + my $branch_from = $i->copyfrom_path or next; + my $r = $i->copyfrom_rev; + print STDERR "Found possible branch point: ", + "$branch_from => $svn_path, $r\n"; + $branch_from =~ s#^/##; + my $l_map = read_url_paths(); + my $url = $SVN->{url}; + defined $l_map->{$url} or next; + my $id = $l_map->{$url}->{$branch_from} or next; + my $f = "$GIT_DIR/svn/$id/revs/$r"; + while ($r && !-r $f) { + $r--; + $f = "$GIT_DIR/svn/$id/revs/$r"; + } + if (-r $f) { + my $parent = file_to_s($f); + unlink $GIT_SVN_INDEX; + print STDERR "Found branch parent: $parent\n"; + sys(qw/git-read-tree/, $parent); + return libsvn_fetch($parent, $paths, $rev, + $author, $date, $msg); + } + print STDERR "Nope, branch point not imported or unknown\n"; + } + open my $gui, '| git-update-index -z --index-info' or croak $!; + my $pool = SVN::Pool->new; + libsvn_traverse($gui, '', $SVN_PATH, $rev, $pool); + $pool->clear; + close $gui or croak $!; + return libsvn_log_entry($rev, $author, $date, $msg); +} + +sub find_graft_path_commit { + my ($tree_paths, $p1, $r1) = @_; + foreach my $x (keys %$tree_paths) { + next unless ($p1 =~ /^\Q$x\E/); + my $i = $tree_paths->{$x}; + my $f = "$GIT_DIR/svn/$i/revs/$r1"; + + return file_to_s($f) if (-r $f); + + print STDERR "r$r1 of $i not imported\n"; + next; + } + return undef; +} + +sub find_graft_path_parents { + my ($grafts, $tree_paths, $c, $p0, $r0) = @_; + foreach my $x (keys %$tree_paths) { + next unless ($p0 =~ /^\Q$x\E/); + my $i = $tree_paths->{$x}; + my $f = "$GIT_DIR/svn/$i/revs/$r0"; + while ($r0 && !-r $f) { + # could be an older revision, too... + $r0--; + $f = "$GIT_DIR/svn/$i/revs/$r0"; + } + unless (-r $f) { + print STDERR "r$r0 of $i not imported\n"; + next; + } + my $parent = file_to_s($f); + $grafts->{$c}->{$parent} = 1; + } +} + +sub libsvn_graft_file_copies { + my ($grafts, $tree_paths, $path, $paths, $rev) = @_; + foreach (keys %$paths) { + my $i = $paths->{$_}; + my ($m, $p0, $r0) = ($i->action, $i->copyfrom_path, + $i->copyfrom_rev); + next unless (defined $p0 && defined $r0); + + my $p1 = $_; + $p1 =~ s#^/##; + $p0 =~ s#^/##; + my $c = find_graft_path_commit($tree_paths, $p1, $rev); + next unless $c; + find_graft_path_parents($grafts, $tree_paths, $c, $p0, $r0); + } +} + +sub set_index { + my $old = $ENV{GIT_INDEX_FILE}; + $ENV{GIT_INDEX_FILE} = shift; + return $old; +} + +sub restore_index { + my ($old) = @_; + if (defined $old) { + $ENV{GIT_INDEX_FILE} = $old; + } else { + delete $ENV{GIT_INDEX_FILE}; + } +} + +sub libsvn_commit_cb { + my ($rev, $date, $committer, $c, $msg, $r_last, $cmt_last) = @_; + if ($rev == ($r_last + 1)) { + # optimized (avoid fetch) + my $log = libsvn_log_entry($rev,$committer,$date,$msg); + $log->{tree} = get_tree_from_treeish($c); + my $cmt = git_commit($log, $cmt_last, $c); + my @diff = safe_qx('git-diff-tree', $cmt, $c); + if (@diff) { + print STDERR "Trees differ: $cmt $c\n", + join('',@diff),"\n"; + exit 1; + } + } else { + fetch_lib("$rev=$c"); + } +} + +sub libsvn_ls_fullurl { + my $fullurl = shift; + my ($repo, $path) = repo_path_split($fullurl); + $SVN ||= libsvn_connect($repo); + my @ret; + my $pool = SVN::Pool->new; + my ($dirent, undef, undef) = $SVN->get_dir($path, + $SVN->get_latest_revnum, $pool); + foreach my $d (keys %$dirent) { + if ($dirent->{$d}->kind == $SVN::Node::dir) { + push @ret, "$d/"; # add '/' for compat with cli svn + } + } + $pool->clear; + return @ret; +} + + +sub libsvn_skip_unknown_revs { + my $err = shift; + my $errno = $err->apr_err(); + # Maybe the branch we're tracking didn't + # exist when the repo started, so it's + # not an error if it doesn't, just continue + # + # Wonderfully consistent library, eh? + # 160013 - svn:// and file:// + # 175002 - http(s):// + # More codes may be discovered later... + if ($errno == 175002 || $errno == 160013) { + print STDERR "directory non-existent\n"; + return; + } + croak "Error from SVN, ($errno): ", $err->expanded_message,"\n"; +}; + +package SVN::Git::Editor; +use vars qw/@ISA/; +use strict; +use warnings; +use Carp qw/croak/; +use IO::File; + +sub new { + my $class = shift; + my $git_svn = shift; + my $self = SVN::Delta::Editor->new(@_); + bless $self, $class; + foreach (qw/svn_path c r ra /) { + die "$_ required!\n" unless (defined $git_svn->{$_}); + $self->{$_} = $git_svn->{$_}; + } + $self->{pool} = SVN::Pool->new; + $self->{bat} = { '' => $self->open_root($self->{r}, $self->{pool}) }; + $self->{rm} = { }; + require Digest::MD5; + return $self; +} + +sub split_path { + return ($_[0] =~ m#^(.*?)/?([^/]+)$#); +} + +sub repo_path { + (defined $_[1] && length $_[1]) ? "$_[0]->{svn_path}/$_[1]" + : $_[0]->{svn_path} +} + +sub url_path { + my ($self, $path) = @_; + $self->{ra}->{url} . '/' . $self->repo_path($path); +} + +sub rmdirs { + my ($self) = @_; + my $rm = $self->{rm}; + delete $rm->{''}; # we never delete the url we're tracking + return unless %$rm; + + foreach (keys %$rm) { + my @d = split m#/#, $_; + my $c = shift @d; + $rm->{$c} = 1; + while (@d) { + $c .= '/' . shift @d; + $rm->{$c} = 1; + } + } + delete $rm->{$self->{svn_path}}; + delete $rm->{''}; # we never delete the url we're tracking + return unless %$rm; + + defined(my $pid = open my $fh,'-|') or croak $!; + if (!$pid) { + exec qw/git-ls-tree --name-only -r -z/, $self->{c} or croak $!; + } + local $/ = "\0"; + while (<$fh>) { + chomp; + $_ = $self->{svn_path} . '/' . $_; + my ($dn) = ($_ =~ m#^(.*?)/?(?:[^/]+)$#); + delete $rm->{$dn}; + last unless %$rm; + } + my ($r, $p, $bat) = ($self->{r}, $self->{pool}, $self->{bat}); + foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) { + $self->close_directory($bat->{$d}, $p); + my ($dn) = ($d =~ m#^(.*?)/?(?:[^/]+)$#); + $self->SUPER::delete_entry($d, $r, $bat->{$dn}, $p); + delete $bat->{$d}; + } +} + +sub open_or_add_dir { + my ($self, $full_path, $baton) = @_; + my $p = SVN::Pool->new; + my $t = $self->{ra}->check_path($full_path, $self->{r}, $p); + $p->clear; + if ($t == $SVN::Node::none) { + return $self->add_directory($full_path, $baton, + undef, -1, $self->{pool}); + } elsif ($t == $SVN::Node::dir) { + return $self->open_directory($full_path, $baton, + $self->{r}, $self->{pool}); + } + print STDERR "$full_path already exists in repository at ", + "r$self->{r} and it is not a directory (", + ($t == $SVN::Node::file ? 'file' : 'unknown'),"/$t)\n"; + exit 1; +} + +sub ensure_path { + my ($self, $path) = @_; + my $bat = $self->{bat}; + $path = $self->repo_path($path); + return $bat->{''} unless (length $path); + my @p = split m#/+#, $path; + my $c = shift @p; + $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{''}); + while (@p) { + my $c0 = $c; + $c .= '/' . shift @p; + $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{$c0}); + } + return $bat->{$c}; +} + +sub A { + my ($self, $m) = @_; + my ($dir, $file) = split_path($m->{file_b}); + my $pbat = $self->ensure_path($dir); + my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, + undef, -1); + $self->chg_file($fbat, $m); + $self->close_file($fbat,undef,$self->{pool}); +} + +sub C { + my ($self, $m) = @_; + my ($dir, $file) = split_path($m->{file_b}); + my $pbat = $self->ensure_path($dir); + my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, + $self->url_path($m->{file_a}), $self->{r}); + $self->chg_file($fbat, $m); + $self->close_file($fbat,undef,$self->{pool}); +} + +sub delete_entry { + my ($self, $path, $pbat) = @_; + my $rpath = $self->repo_path($path); + my ($dir, $file) = split_path($rpath); + $self->{rm}->{$dir} = 1; + $self->SUPER::delete_entry($rpath, $self->{r}, $pbat, $self->{pool}); +} + +sub R { + my ($self, $m) = @_; + my ($dir, $file) = split_path($m->{file_b}); + my $pbat = $self->ensure_path($dir); + my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, + $self->url_path($m->{file_a}), $self->{r}); + $self->chg_file($fbat, $m); + $self->close_file($fbat,undef,$self->{pool}); + + ($dir, $file) = split_path($m->{file_a}); + $pbat = $self->ensure_path($dir); + $self->delete_entry($m->{file_a}, $pbat); +} + +sub M { + my ($self, $m) = @_; + my ($dir, $file) = split_path($m->{file_b}); + my $pbat = $self->ensure_path($dir); + my $fbat = $self->open_file($self->repo_path($m->{file_b}), + $pbat,$self->{r},$self->{pool}); + $self->chg_file($fbat, $m); + $self->close_file($fbat,undef,$self->{pool}); +} + +sub T { shift->M(@_) } + +sub change_file_prop { + my ($self, $fbat, $pname, $pval) = @_; + $self->SUPER::change_file_prop($fbat, $pname, $pval, $self->{pool}); +} + +sub chg_file { + my ($self, $fbat, $m) = @_; + if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) { + $self->change_file_prop($fbat,'svn:executable','*'); + } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) { + $self->change_file_prop($fbat,'svn:executable',undef); + } + my $fh = IO::File->new_tmpfile or croak $!; + if ($m->{mode_b} =~ /^120/) { + print $fh 'link ' or croak $!; + $self->change_file_prop($fbat,'svn:special','*'); + } elsif ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) { + $self->change_file_prop($fbat,'svn:special',undef); + } + defined(my $pid = fork) or croak $!; + if (!$pid) { + open STDOUT, '>&', $fh or croak $!; + exec qw/git-cat-file blob/, $m->{sha1_b} or croak $!; + } + waitpid $pid, 0; + croak $? if $?; + $fh->flush == 0 or croak $!; + seek $fh, 0, 0 or croak $!; + + my $md5 = Digest::MD5->new; + $md5->addfile($fh) or croak $!; + seek $fh, 0, 0 or croak $!; + + my $exp = $md5->hexdigest; + my $atd = $self->apply_textdelta($fbat, undef, $self->{pool}); + my $got = SVN::TxDelta::send_stream($fh, @$atd, $self->{pool}); + die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp); + + close $fh or croak $!; +} + +sub D { + my ($self, $m) = @_; + my ($dir, $file) = split_path($m->{file_b}); + my $pbat = $self->ensure_path($dir); + $self->delete_entry($m->{file_b}, $pbat); +} + +sub close_edit { + my ($self) = @_; + my ($p,$bat) = ($self->{pool}, $self->{bat}); + foreach (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$bat) { + $self->close_directory($bat->{$_}, $p); + } + $self->SUPER::close_edit($p); + $p->clear; +} + +sub abort_edit { + my ($self) = @_; + $self->SUPER::abort_edit($self->{pool}); + $self->{pool}->clear; +} + __END__ Data structures: @@ -2062,3 +2914,7 @@ diff-index line ($m hash) file_b => new/current file name of a file (any chg) } ; + +Notes: + I don't trust the each() function on unless I created %hash myself + because the internal iterator may not have started at base. diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh index 58408a6c07..2843258fc4 100644 --- a/contrib/git-svn/t/lib-git-svn.sh +++ b/contrib/git-svn/t/lib-git-svn.sh @@ -11,7 +11,7 @@ fi GIT_DIR=$PWD/.git GIT_SVN_DIR=$GIT_DIR/svn/git-svn -SVN_TREE=$GIT_SVN_DIR/tree +SVN_TREE=$GIT_SVN_DIR/svn-tree svnadmin >/dev/null 2>&1 if test $? != 1 diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh index 0c6ff2066b..c33b522d08 100644 --- a/contrib/git-svn/t/t0000-contrib-git-svn.sh +++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh @@ -31,6 +31,7 @@ test_expect_success \ 'import an SVN revision into git' \ 'git-svn fetch' +test_expect_success "checkout from svn" "svn co $svnrepo $SVN_TREE" name='try a deep --rmdir with a commit' git checkout -f -b mybranch remotes/git-svn @@ -41,6 +42,7 @@ git commit -m "$name" test_expect_success "$name" \ "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch && + svn up $SVN_TREE && test -d $SVN_TREE/dir && test ! -d $SVN_TREE/dir/a" @@ -52,7 +54,7 @@ git update-index --remove dir/file git update-index --add dir/file/file git commit -m "$name" -test_expect_code 1 "$name" \ +test_expect_failure "$name" \ 'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch' \ || true @@ -67,7 +69,7 @@ git update-index --remove -- bar/zzz git update-index --add -- bar git commit -m "$name" -test_expect_code 1 "$name" \ +test_expect_failure "$name" \ 'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch2' \ || true @@ -82,7 +84,7 @@ echo yyy > bar/zzz/yyy git-update-index --add bar/zzz/yyy git commit -m "$name" -test_expect_code 1 "$name" \ +test_expect_failure "$name" \ 'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch3' \ || true @@ -97,7 +99,7 @@ echo asdf > dir git update-index --add -- dir git commit -m "$name" -test_expect_code 1 "$name" \ +test_expect_failure "$name" \ 'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch4' \ || true @@ -111,6 +113,7 @@ git commit -m "$name" test_expect_success "$name" \ "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && + svn up $SVN_TREE && test ! -x $SVN_TREE/exec.sh" @@ -121,6 +124,7 @@ git commit -m "$name" test_expect_success "$name" \ "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && + svn up $SVN_TREE && test -x $SVN_TREE/exec.sh" @@ -133,6 +137,7 @@ git commit -m "$name" test_expect_success "$name" \ "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && + svn up $SVN_TREE && test -L $SVN_TREE/exec.sh" @@ -145,6 +150,7 @@ git commit -m "$name" test_expect_success "$name" \ "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && + svn up $SVN_TREE && test -x $SVN_TREE/bar/zzz && test -L $SVN_TREE/exec-2.sh" @@ -159,6 +165,7 @@ git commit -m "$name" test_expect_success "$name" \ "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && + svn up $SVN_TREE && test -f $SVN_TREE/exec-2.sh && test ! -L $SVN_TREE/exec-2.sh && diff -u help $SVN_TREE/exec-2.sh" -- cgit v1.2.3 From 42d328701dbdbc02c3361673629a44df478e69d7 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 13 Jun 2006 04:02:23 -0700 Subject: git-svn: make the $GIT_DIR/svn/*/revs directory obsolete This is a very intrusive change, so I've beefed up the tests significantly. Added 'full-test' a target to the Makefile, to test different possible configurations. This is intended for maintainers only. Users should only be concerned with 'test' succeeding. We now have a very simple custom database format for handling mapping of svn revisions => git commits. Of course, we're not really using it yet, either. Also disabled automatic branch-finding on new trees for now. It's too easily broken. revisions_eq() function should be helpful for branch detection. Also removed an extra assertion in fetch_cmd() that wasn't correctly done. This bug was found by full-test. Signed-off-by: Eric Wong --- contrib/git-svn/Makefile | 12 +- contrib/git-svn/git-svn.perl | 245 +++++++++++++++-------- contrib/git-svn/t/t0000-contrib-git-svn.sh | 13 ++ contrib/git-svn/t/t0001-contrib-git-svn-props.sh | 86 ++++---- 4 files changed, 224 insertions(+), 132 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile index 48f60b3a0d..d73aa5641c 100644 --- a/contrib/git-svn/Makefile +++ b/contrib/git-svn/Makefile @@ -29,8 +29,16 @@ git-svn.html : git-svn.txt asciidoc -b xhtml11 -d manpage \ -f ../../Documentation/asciidoc.conf $< test: git-svn - cd t && $(SHELL) ./t0000-contrib-git-svn.sh - cd t && $(SHELL) ./t0001-contrib-git-svn-props.sh + cd t && $(SHELL) ./t0000-contrib-git-svn.sh $(TEST_FLAGS) + cd t && $(SHELL) ./t0001-contrib-git-svn-props.sh $(TEST_FLAGS) + +full-test: + $(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1 + $(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=1 + $(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \ + LC_ALL=en_US.UTF-8 + $(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \ + LC_ALL=en_US.UTF-8 clean: rm -f git-svn *.xml *.html *.1 diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 9618c8bab5..884969ebdd 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -6,9 +6,9 @@ use strict; use vars qw/ $AUTHOR $VERSION $SVN_URL $SVN_INFO $SVN_WC $SVN_UUID $GIT_SVN_INDEX $GIT_SVN - $GIT_DIR $REV_DIR $GIT_SVN_DIR/; + $GIT_DIR $GIT_SVN_DIR $REVDB/; $AUTHOR = 'Eric Wong '; -$VERSION = '1.1.0-pre'; +$VERSION = '1.1.1-broken'; use Cwd qw/abs_path/; $GIT_DIR = abs_path($ENV{GIT_DIR} || '.git'); @@ -31,10 +31,13 @@ use File::Path qw/mkpath/; use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/; use File::Spec qw//; use POSIX qw/strftime/; +use Memoize; +memoize('revisions_eq'); my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib); $_use_lib = 1 unless $ENV{GIT_SVN_NO_LIB}; libsvn_load(); +my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS}; my $sha1 = qr/[a-f\d]{40}/; my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, @@ -43,7 +46,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_template, $_shared, $_no_default_regex, $_no_graft_copy, $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit, $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m); -my (@_branch_from, %tree_map, %users, %rusers); +my (@_branch_from, %tree_map, %users, %rusers, %equiv); my ($_svn_co_url_revs, $_svn_pg_peg_revs); my @repo_path_split_cache; @@ -201,7 +204,6 @@ sub rebuild { next if (defined $SVN_UUID && ($uuid ne $SVN_UUID)); next if (defined $SVN_URL && defined $url && ($url ne $SVN_URL)); - print "r$rev = $c\n"; unless (defined $latest) { if (!$SVN_URL && !$url) { croak "SVN repository location required: $url\n"; @@ -211,8 +213,8 @@ sub rebuild { setup_git_svn(); $latest = $rev; } - assert_revision_eq_or_unknown($rev, $c); - sys('git-update-ref',"svn/$GIT_SVN/revs/$rev",$c); + revdb_set($REVDB, $rev, $c); + print "r$rev = $c\n"; $newest_rev = $rev if ($rev > $newest_rev); } close $rev_list or croak $?; @@ -280,7 +282,11 @@ sub fetch_cmd { my $svn_log = svn_log_raw(@log_args); my $base = next_log_entry($svn_log) or croak "No base revision!\n"; - my $last_commit = undef; + # don't need last_revision from grab_base_rev() because + # user could've specified a different revision to skip (they + # didn't want to import certain revisions into git for whatever + # reason, so trust $base->{revision} instead. + my (undef, $last_commit) = svn_grab_base_rev(); unless (-d $SVN_WC) { svn_cmd_checkout($SVN_URL,$base->{revision},$SVN_WC); chdir $SVN_WC or croak $!; @@ -290,7 +296,6 @@ sub fetch_cmd { } else { chdir $SVN_WC or croak $!; read_uuid(); - eval { $last_commit = file_to_s("$REV_DIR/$base->{revision}") }; # looks like a user manually cp'd and svn switch'ed unless ($last_commit) { sys(qw/svn revert -R ./); @@ -303,7 +308,6 @@ sub fetch_cmd { push @svn_up, '--ignore-externals' unless $_no_ignore_ext; my $last = $base; while (my $log_msg = next_log_entry($svn_log)) { - assert_tree($last_commit); if ($last->{revision} >= $log_msg->{revision}) { croak "Out of order: last >= current: ", "$last->{revision} >= $log_msg->{revision}\n"; @@ -444,14 +448,14 @@ sub commit_cmd { } $info = svn_info('.'); read_uuid($info); - my $svn_current_rev = $info->{'Last Changed Rev'}; + my $last = $fetched; foreach my $c (@revs) { - my $mods = svn_checkout_tree($svn_current_rev, $c); + my $mods = svn_checkout_tree($last, $c); if (scalar @$mods == 0) { print "Skipping, no changes detected\n"; next; } - $svn_current_rev = svn_commit_tree($svn_current_rev, $c); + $last = svn_commit_tree($last, $c); } } @@ -500,7 +504,7 @@ sub commit_lib { }, @lock) ); - my $mods = libsvn_checkout_tree($r_last, $c, $ed); + my $mods = libsvn_checkout_tree($cmt_last, $c, $ed); if (@$mods == 0) { print "No changes\nr$r_last = $cmt_last\n"; $ed->abort_edit; @@ -814,7 +818,7 @@ sub graft_file_copy_cmd { my ($grafts, $l_map, $u) = @_; my $paths = $l_map->{$u}; my $pfx = common_prefix([keys %$paths]); - + $SVN_URL ||= $u.$pfx; my $pid = open my $fh, '-|'; defined $pid or croak $!; unless ($pid) { @@ -851,6 +855,8 @@ sub graft_file_copy_lib { my ($base, $head) = libsvn_parse_revision(); my $inc = 1000; my ($min, $max) = ($base, $head < $base+$inc ? $head : $base+$inc); + my $eh = $SVN::Error::handler; + $SVN::Error::handler = \&libsvn_skip_unknown_revs; while (1) { my $pool = SVN::Pool->new; $SVN_LOG->get_log( "/$path", $min, $max, 0, 1, 1, @@ -864,6 +870,7 @@ sub graft_file_copy_lib { $max += $inc; $max = $head if ($max > $head); } + $SVN::Error::handler = $eh; } sub process_merge_msg_matches { @@ -994,7 +1001,8 @@ sub setup_git_svn { } mkpath([$GIT_SVN_DIR]); mkpath(["$GIT_SVN_DIR/info"]); - mkpath([$REV_DIR]); + open my $fh, '>>',$REVDB or croak $!; + close $fh; s_to_file($SVN_URL,"$GIT_SVN_DIR/info/url"); open my $fd, '>>', "$GIT_SVN_DIR/info/exclude" or croak $!; @@ -1201,8 +1209,7 @@ sub precommit_check { sub get_diff { - my ($svn_rev, $treeish) = @_; - my $from = file_to_s("$REV_DIR/$svn_rev"); + my ($from, $treeish) = @_; assert_tree($from); print "diff-tree $from $treeish\n"; my $pid = open my $diff_fh, '-|'; @@ -1222,8 +1229,8 @@ sub get_diff { } sub svn_checkout_tree { - my ($svn_rev, $treeish) = @_; - my $mods = get_diff($svn_rev, $treeish); + my ($from, $treeish) = @_; + my $mods = get_diff($from->{commit}, $treeish); return $mods unless (scalar @$mods); my ($rm, $add) = precommit_check($mods); @@ -1268,8 +1275,8 @@ sub svn_checkout_tree { } sub libsvn_checkout_tree { - my ($svn_rev, $treeish, $ed) = @_; - my $mods = get_diff($svn_rev, $treeish); + my ($from, $treeish, $ed) = @_; + my $mods = get_diff($from, $treeish); return $mods unless (scalar @$mods); my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 ); foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) { @@ -1365,7 +1372,7 @@ sub get_commit_message { } sub svn_commit_tree { - my ($svn_rev, $commit) = @_; + my ($last, $commit) = @_; my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$"; my $log_msg = get_commit_message($commit, $commit_msg); my ($oneline) = ($log_msg->{msg} =~ /([^\n\r]+)/); @@ -1392,7 +1399,7 @@ sub svn_commit_tree { my @svn_up = qw(svn up); push @svn_up, '--ignore-externals' unless $_no_ignore_ext; - if ($committed == ($svn_rev + 1)) { + if ($_optimize_commits && ($committed == ($last->{revision} + 1))) { push @svn_up, "-r$committed"; sys(@svn_up); my $info = svn_info('.'); @@ -1408,14 +1415,14 @@ sub svn_commit_tree { $log_msg->{author} = $info->{'Last Changed Author'}; $log_msg->{revision} = $committed; $log_msg->{msg} .= "\n"; - my $parent = file_to_s("$REV_DIR/$svn_rev"); - git_commit($log_msg, $parent, $commit); - return $committed; + $log_msg->{parents} = [ $last->{commit} ]; + $log_msg->{commit} = git_commit($log_msg, $commit); + return $log_msg; } # resync immediately - push @svn_up, "-r$svn_rev"; + push @svn_up, "-r$last->{revision}"; sys(@svn_up); - return fetch("$committed=$commit")->{revision}; + return fetch("$committed=$commit"); } sub rev_list_raw { @@ -1671,10 +1678,9 @@ sub file_to_s { } sub assert_revision_unknown { - my $revno = shift; - if (-f "$REV_DIR/$revno") { - croak "$REV_DIR/$revno already exists! ", - "Why are we refetching it?"; + my $r = shift; + if (my $c = revdb_get($REVDB, $r)) { + croak "$r = $c already exists! Why are we refetching it?"; } } @@ -1690,18 +1696,6 @@ sub trees_eq { return 1; } -sub assert_revision_eq_or_unknown { - my ($revno, $commit) = @_; - if (-f "$REV_DIR/$revno") { - my $current = file_to_s("$REV_DIR/$revno"); - if (($commit ne $current) && !trees_eq($commit, $current)) { - croak "$REV_DIR/$revno already exists!\n", - "current: $current\nexpected: $commit\n"; - } - return; - } -} - sub git_commit { my ($log_msg, @parents) = @_; assert_revision_unknown($log_msg->{revision}); @@ -1763,19 +1757,12 @@ sub git_commit { } my @update_ref = ('git-update-ref',"refs/remotes/$GIT_SVN",$commit); if (my $primary_parent = shift @exec_parents) { - $pid = fork; - defined $pid or croak $!; - if (!$pid) { - close STDERR; - close STDOUT; - exec 'git-rev-parse','--verify', - "refs/remotes/$GIT_SVN^0" or croak $!; - } - waitpid $pid, 0; + quiet_run(qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0"); push @update_ref, $primary_parent unless $?; } sys(@update_ref); - sys('git-update-ref',"svn/$GIT_SVN/revs/$log_msg->{revision}",$commit); + revdb_set($REVDB, $log_msg->{revision}, $commit); + # this output is read via pipe, do not change: print "r$log_msg->{revision} = $commit\n"; if ($_repack && (--$_repack_nr == 0)) { @@ -1990,7 +1977,29 @@ sub git_svn_each { } } +sub migrate_revdb { + git_svn_each(sub { + my $id = shift; + defined(my $pid = fork) or croak $!; + if (!$pid) { + $GIT_SVN = $ENV{GIT_SVN_ID} = $id; + init_vars(); + exit 0 if -r $REVDB; + print "Upgrading svn => git mapping...\n"; + open my $fh, '>>',$REVDB or croak $!; + close $fh; + rebuild(); + print "Done upgrading. You may now delete the ", + "deprecated $GIT_SVN_DIR/revs directory\n"; + exit 0; + } + waitpid $pid, 0; + croak $? if $?; + }); +} + sub migration_check { + migrate_revdb() unless (-e $REVDB); return if (-d "$GIT_DIR/svn" || !-d $GIT_DIR); print "Upgrading repository...\n"; unless (-d "$GIT_DIR/svn") { @@ -2013,15 +2022,19 @@ sub migration_check { s_to_file($url, "$GIT_DIR/svn/$x/info/repo_url"); s_to_file($path, "$GIT_DIR/svn/$x/info/repo_path"); } + migrate_revdb() if (-d $GIT_SVN_DIR && !-w $REVDB); print "Done upgrading.\n"; } sub find_rev_before { - my ($r, $git_svn_id) = @_; - my @revs = map { basename $_ } <$GIT_DIR/svn/$git_svn_id/revs/*>; - foreach my $r0 (sort { $b <=> $a } @revs) { - next if $r0 >= $r; - return ($r0, file_to_s("$GIT_DIR/svn/$git_svn_id/revs/$r0")); + my ($r, $id, $eq_ok) = @_; + my $f = "$GIT_DIR/svn/$id/.rev_db"; + # --$r unless $eq_ok; + while ($r > 0) { + if (my $c = revdb_get($f, $r)) { + return ($r, $c); + } + --$r; } return (undef, undef); } @@ -2029,9 +2042,9 @@ sub find_rev_before { sub init_vars { $GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn'; $GIT_SVN_DIR = "$GIT_DIR/svn/$GIT_SVN"; + $REVDB = "$GIT_SVN_DIR/.rev_db"; $GIT_SVN_INDEX = "$GIT_SVN_DIR/index"; $SVN_URL = undef; - $REV_DIR = "$GIT_SVN_DIR/revs"; $SVN_WC = "$GIT_SVN_DIR/tree"; } @@ -2491,7 +2504,27 @@ sub libsvn_traverse_ignore { $pool->clear; } -sub libsvn_new_tree { +sub revisions_eq { + my ($path, $r0, $r1) = @_; + return 1 if $r0 == $r1; + my $nr = 0; + if ($_use_lib) { + # should be OK to use Pool here (r1 - r0) should be small + my $pool = SVN::Pool->new; + $SVN->get_log("/$path", $r0, $r1, 0, 1, 1, sub {$nr++},$pool); + $pool->clear; + } else { + my ($url, undef) = repo_path_split($SVN_URL); + my $svn_log = svn_log_raw("$url/$path","-r$r0:$r1"); + while (next_log_entry($svn_log)) { $nr++ } + close $svn_log->{fh}; + } + return 0 if ($nr > 1); + return 1; +} + +sub libsvn_find_parent_branch { + return undef; # XXX this function is disabled atm (not tested enough) my ($paths, $rev, $author, $date, $msg) = @_; my $svn_path = '/'.$SVN_PATH; @@ -2502,27 +2535,33 @@ sub libsvn_new_tree { my $branch_from = $i->copyfrom_path or next; my $r = $i->copyfrom_rev; print STDERR "Found possible branch point: ", - "$branch_from => $svn_path, $r\n"; + "$branch_from => $svn_path, $r\n"; $branch_from =~ s#^/##; my $l_map = read_url_paths(); my $url = $SVN->{url}; defined $l_map->{$url} or next; my $id = $l_map->{$url}->{$branch_from} or next; - my $f = "$GIT_DIR/svn/$id/revs/$r"; - while ($r && !-r $f) { - $r--; - $f = "$GIT_DIR/svn/$id/revs/$r"; - } - if (-r $f) { - my $parent = file_to_s($f); + my ($r0, $parent) = find_rev_before($r,$id,1); + if (defined $r0 && defined $parent && + revisions_eq($branch_from, $r0, $r)) { unlink $GIT_SVN_INDEX; print STDERR "Found branch parent: $parent\n"; sys(qw/git-read-tree/, $parent); return libsvn_fetch($parent, $paths, $rev, $author, $date, $msg); + } else { + print STDERR + "Nope, branch point not imported or unknown\n"; } - print STDERR "Nope, branch point not imported or unknown\n"; } + return undef; +} + +sub libsvn_new_tree { + if (my $log_entry = libsvn_find_parent_branch(@_)) { + return $log_entry; + } + my ($paths, $rev, $author, $date, $msg) = @_; open my $gui, '| git-update-index -z --index-info' or croak $!; my $pool = SVN::Pool->new; libsvn_traverse($gui, '', $SVN_PATH, $rev, $pool); @@ -2536,10 +2575,8 @@ sub find_graft_path_commit { foreach my $x (keys %$tree_paths) { next unless ($p1 =~ /^\Q$x\E/); my $i = $tree_paths->{$x}; - my $f = "$GIT_DIR/svn/$i/revs/$r1"; - - return file_to_s($f) if (-r $f); - + my ($r0, $parent) = find_rev_before($r1,$i,1); + return $parent if (defined $r0 && $r0 == $r1); print STDERR "r$r1 of $i not imported\n"; next; } @@ -2551,18 +2588,10 @@ sub find_graft_path_parents { foreach my $x (keys %$tree_paths) { next unless ($p0 =~ /^\Q$x\E/); my $i = $tree_paths->{$x}; - my $f = "$GIT_DIR/svn/$i/revs/$r0"; - while ($r0 && !-r $f) { - # could be an older revision, too... - $r0--; - $f = "$GIT_DIR/svn/$i/revs/$r0"; - } - unless (-r $f) { - print STDERR "r$r0 of $i not imported\n"; - next; + my ($r, $parent) = find_rev_before($r0, $i, 1); + if (defined $r && defined $parent && revisions_eq($x,$r,$r0)) { + $grafts->{$c}->{$parent} = 1; } - my $parent = file_to_s($f); - $grafts->{$c}->{$parent} = 1; } } @@ -2600,8 +2629,7 @@ sub restore_index { sub libsvn_commit_cb { my ($rev, $date, $committer, $c, $msg, $r_last, $cmt_last) = @_; - if ($rev == ($r_last + 1)) { - # optimized (avoid fetch) + if ($_optimize_commits && $rev == ($r_last + 1)) { my $log = libsvn_log_entry($rev,$committer,$date,$msg); $log->{tree} = get_tree_from_treeish($c); my $cmt = git_commit($log, $cmt_last, $c); @@ -2652,6 +2680,49 @@ sub libsvn_skip_unknown_revs { croak "Error from SVN, ($errno): ", $err->expanded_message,"\n"; }; +# Tie::File seems to be prone to offset errors if revisions get sparse, +# it's not that fast, either. Tie::File is also not in Perl 5.6. So +# one of my favorite modules is out :< Next up would be one of the DBM +# modules, but I'm not sure which is most portable... So I'll just +# go with something that's plain-text, but still capable of +# being randomly accessed. So here's my ultra-simple fixed-width +# database. All records are 40 characters + "\n", so it's easy to seek +# to a revision: (41 * rev) is the byte offset. +# A record of 40 0s denotes an empty revision. +# And yes, it's still pretty fast (faster than Tie::File). +sub revdb_set { + my ($file, $rev, $commit) = @_; + length $commit == 40 or croak "arg3 must be a full SHA1 hexsum\n"; + open my $fh, '+<', $file or croak $!; + my $offset = $rev * 41; + # assume that append is the common case: + seek $fh, 0, 2 or croak $!; + my $pos = tell $fh; + if ($pos < $offset) { + print $fh (('0' x 40),"\n") x (($offset - $pos) / 41); + } + seek $fh, $offset, 0 or croak $!; + print $fh $commit,"\n"; + close $fh or croak $!; +} + +sub revdb_get { + my ($file, $rev) = @_; + my $ret; + my $offset = $rev * 41; + open my $fh, '<', $file or croak $!; + seek $fh, $offset, 0; + if (tell $fh == $offset) { + $ret = readline $fh; + if (defined $ret) { + chomp $ret; + $ret = undef if ($ret =~ /^0{40}$/); + } + } + close $fh or croak $!; + return $ret; +} + package SVN::Git::Editor; use vars qw/@ISA/; use strict; diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh index c33b522d08..f896e2c2a8 100644 --- a/contrib/git-svn/t/t0000-contrib-git-svn.sh +++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh @@ -193,5 +193,18 @@ test_expect_success "$name" \ git-rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b && diff -u a b" +name='check imported tree checksums expected tree checksums' +cat > expected <<\EOF +tree f735671b89a7eb30cab1d8597de35bd4271ab813 +tree 4b9af72bb861eaed053854ec502cf7df72618f0f +tree 031b8d557afc6fea52894eaebb45bec52f1ba6d1 +tree 0b094cbff17168f24c302e297f55bfac65eb8bd3 +tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e +tree 56a30b966619b863674f5978696f4a3594f2fca9 +tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e +tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4 +EOF +test_expect_success "$name" "diff -u a expected" + test_done diff --git a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh index 23a5a2a223..54e0ed7353 100644 --- a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh +++ b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh @@ -52,49 +52,49 @@ EOF cd .. rm -rf import -svn co "$svnrepo" test_wc +test_expect_success 'checkout working copy from svn' "svn co $svnrepo test_wc" +test_expect_success 'setup some commits to svn' \ + 'cd test_wc && + echo Greetings >> kw.c && + svn commit -m "Not yet an Id" && + svn up && + echo Hello world >> kw.c && + svn commit -m "Modified file, but still not yet an Id" && + svn up && + svn propset svn:keywords Id kw.c && + svn commit -m "Propset Id" && + svn up && + cd ..' + +test_expect_success 'initialize git-svn' "git-svn init $svnrepo" +test_expect_success 'fetch revisions from svn' 'git-svn fetch' -cd test_wc - echo 'Greetings' >> kw.c - svn commit -m 'Not yet an $Id$' - svn up - - echo 'Hello world' >> kw.c - svn commit -m 'Modified file, but still not yet an $Id$' - svn up - - svn propset svn:keywords Id kw.c - svn commit -m 'Propset $Id$' - svn up -cd .. - -git-svn init "$svnrepo" -git-svn fetch - -git checkout -b mybranch remotes/git-svn -echo 'Hi again' >> kw.c name='test svn:keywords ignoring' - -git commit -a -m "$name" -git-svn commit remotes/git-svn..mybranch -git pull . remotes/git-svn +test_expect_success "$name" \ + 'git checkout -b mybranch remotes/git-svn && + echo Hi again >> kw.c && + git commit -a -m "test keywoards ignoring" && + git-svn commit remotes/git-svn..mybranch && + git pull . remotes/git-svn' expect='/* $Id$ */' got="`sed -ne 2p kw.c`" test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'" -cd test_wc - svn propset svn:eol-style CR empty - svn propset svn:eol-style CR crlf - svn propset svn:eol-style CR ne_crlf - svn commit -m 'propset CR on crlf files' - svn up -cd .. +test_expect_success "propset CR on crlf files" \ + 'cd test_wc && + svn propset svn:eol-style CR empty && + svn propset svn:eol-style CR crlf && + svn propset svn:eol-style CR ne_crlf && + svn commit -m "propset CR on crlf files" && + svn up && + cd ..' -git-svn fetch -git pull . remotes/git-svn +test_expect_success 'fetch and pull latest from svn and checkout a new wc' \ + "git-svn fetch && + git pull . remotes/git-svn && + svn co $svnrepo new_wc" -svn co "$svnrepo" new_wc for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf do test_expect_success "Comparing $i" "cmp $i new_wc/$i" @@ -106,16 +106,16 @@ cd test_wc printf '$Id$\rHello\rWorld' > ne_cr a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git-hash-object --stdin` a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git-hash-object --stdin` - svn propset svn:eol-style CRLF cr - svn propset svn:eol-style CRLF ne_cr - svn propset svn:keywords Id cr - svn propset svn:keywords Id ne_cr - svn commit -m 'propset CRLF on cr files' - svn up + test_expect_success 'Set CRLF on cr files' \ + 'svn propset svn:eol-style CRLF cr && + svn propset svn:eol-style CRLF ne_cr && + svn propset svn:keywords Id cr && + svn propset svn:keywords Id ne_cr && + svn commit -m "propset CRLF on cr files" && + svn up' cd .. - -git-svn fetch -git pull . remotes/git-svn +test_expect_success 'fetch and pull latest from svn' \ + 'git-svn fetch && git pull . remotes/git-svn' b_cr="`git-hash-object cr`" b_ne_cr="`git-hash-object ne_cr`" -- cgit v1.2.3 From 6c5cda89e93915ed2984ba560f3c3d048c7a5702 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 14 Jun 2006 21:24:03 -0700 Subject: git-svn: avoid creating some small files repo_path_split() is already pretty fast, and is already optimized via caching. We also don't need to create an exclude file if we're relying on the SVN libraries. Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 884969ebdd..88af9c5704 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -1005,12 +1005,6 @@ sub setup_git_svn { close $fh; s_to_file($SVN_URL,"$GIT_SVN_DIR/info/url"); - open my $fd, '>>', "$GIT_SVN_DIR/info/exclude" or croak $!; - print $fd '.svn',"\n"; - close $fd or croak $!; - my ($url, $path) = repo_path_split($SVN_URL); - s_to_file($url, "$GIT_SVN_DIR/info/repo_url"); - s_to_file($path, "$GIT_SVN_DIR/info/repo_path"); } sub assert_svn_wc_clean { @@ -1649,6 +1643,12 @@ sub do_update_index { sub index_changes { return if $_use_lib; + + if (!-f "$GIT_SVN_DIR/info/exclude") { + open my $fd, '>>', "$GIT_SVN_DIR/info/exclude" or croak $!; + print $fd '.svn',"\n"; + close $fd or croak $!; + } my $no_text_base = shift; do_update_index([qw/git-diff-files --name-only -z/], 'remove', @@ -2018,9 +2018,6 @@ sub migration_check { my $dn = dirname("$GIT_DIR/svn/$x"); mkpath([$dn]) unless -d $dn; rename "$GIT_DIR/$x", "$GIT_DIR/svn/$x" or croak "$!: $x"; - my ($url, $path) = repo_path_split($u); - s_to_file($url, "$GIT_DIR/svn/$x/info/repo_url"); - s_to_file($path, "$GIT_DIR/svn/$x/info/repo_path"); } migrate_revdb() if (-d $GIT_SVN_DIR && !-w $REVDB); print "Done upgrading.\n"; @@ -2138,15 +2135,8 @@ sub write_grafts { sub read_url_paths { my $l_map = {}; git_svn_each(sub { my $x = shift; - my $u = file_to_s("$GIT_DIR/svn/$x/info/repo_url"); - my $p = file_to_s("$GIT_DIR/svn/$x/info/repo_path"); - # we hate trailing slashes - if ($u =~ s#(?:^\/+|\/+$)##g) { - s_to_file($u,"$GIT_DIR/svn/$x/info/repo_url"); - } - if ($p =~ s#(?:^\/+|\/+$)##g) { - s_to_file($p,"$GIT_DIR/svn/$x/info/repo_path"); - } + my $url = file_to_s("$GIT_DIR/svn/$x/info/url"); + my ($u, $p) = repo_path_split($url); $l_map->{$u}->{$p} = $x; }); return $l_map; -- cgit v1.2.3 From cf7424b021f99fbc5dc1127725e57ca3d48e981c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 15 Jun 2006 12:50:12 -0700 Subject: git-svn: fix several small bugs, enable branch optimization Share the repack counter between branches when doing multi-fetch. Pass the -d flag to git repack by default. That's the main reason we will want automatic pack generation, to save space and improve disk cache performance. I won't add -a by default since it can generate extremely large packs that make RAM-starved systems unhappy. We no longer generate the .git/svn/$GIT_SVN_ID/info/uuid file, either. It was never read in the first place. Check for and create .rev_db if we need to during fetch (in case somebody manually blew away their .rev_db and wanted to start over. Mainly makes debugging easier). Croak with $? instead of $! if there's an error closing pipes Quiet down some of the chatter, too. Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 146 ++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 65 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 88af9c5704..27f1d682c8 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -368,7 +368,6 @@ sub fetch_lib { defined(my $pid = fork) or croak $!; if (!$pid) { $SVN::Error::handler = \&libsvn_skip_unknown_revs; - print "Fetching revisions $min .. $max\n"; # Yes I'm perfectly aware that the fourth argument # below is the limit revisions number. Unfortunately @@ -391,7 +390,6 @@ sub fetch_lib { $log_msg, @parents); } }); - $SVN::Error::handler = sub { 'quiet warnings' }; exit 0; } waitpid $pid, 0; @@ -463,7 +461,7 @@ sub commit_lib { my (@revs) = @_; my ($r_last, $cmt_last) = svn_grab_base_rev(); defined $r_last or die "Must have an existing revision to commit\n"; - my $fetched = fetch_lib(); + my $fetched = fetch(); if ($r_last != $fetched->{revision}) { print STDERR "There are new revisions that were fetched ", "and need to be merged (or acknowledged) ", @@ -523,7 +521,7 @@ sub commit_lib { $no = 1; } } - close $fh or croak $!; + close $fh or croak $?; if (! defined $r_new && ! defined $cmt_new) { unless ($no) { die "Failed to parse revision information\n"; @@ -633,17 +631,8 @@ sub multi_init { sub multi_fetch { # try to do trunk first, since branches/tags # may be descended from it. - if (-d "$GIT_DIR/svn/trunk") { - print "Fetching trunk\n"; - defined(my $pid = fork) or croak $!; - if (!$pid) { - $GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk'; - init_vars(); - fetch(@_); - exit 0; - } - waitpid $pid, 0; - croak $? if $?; + if (-e "$GIT_DIR/svn/trunk/info/url") { + fetch_child_id('trunk', @_); } rec_fetch('', "$GIT_DIR/svn", @_); } @@ -725,6 +714,41 @@ out: ########################### utility functions ######################### +sub fetch_child_id { + my $id = shift; + print "Fetching $id\n"; + my $ref = "$GIT_DIR/refs/remotes/$id"; + my $ca = file_to_s($ref) if (-r $ref); + defined(my $pid = fork) or croak $!; + if (!$pid) { + $GIT_SVN = $ENV{GIT_SVN_ID} = $id; + init_vars(); + fetch(@_); + exit 0; + } + waitpid $pid, 0; + croak $? if $?; + return unless $_repack || -r $ref; + + my $cb = file_to_s($ref); + + defined($pid = open my $fh, '-|') or croak $!; + my $url = file_to_s("$GIT_DIR/svn/$id/info/url"); + $url = qr/\Q$url\E/; + if (!$pid) { + exec qw/git-rev-list --pretty=raw/, + $ca ? "$ca..$cb" : $cb or croak $!; + } + while (<$fh>) { + if (/^ git-svn-id: $url\@\d+ [a-f0-9\-]+$/) { + check_repack(); + } elsif (/^ git-svn-id: \S+\@\d+ [a-f0-9\-]+$/) { + last; + } + } + close $fh; +} + sub rec_fetch { my ($pfx, $p, @args) = @_; my @dir; @@ -733,16 +757,7 @@ sub rec_fetch { $pfx .= '/' if $pfx && $pfx !~ m!/$!; my $id = $pfx . basename $_; next if $id eq 'trunk'; - print "Fetching $id\n"; - defined(my $pid = fork) or croak $!; - if (!$pid) { - $GIT_SVN = $ENV{GIT_SVN_ID} = $id; - init_vars(); - fetch(@args); - exit 0; - } - waitpid $pid, 0; - croak $? if $?; + fetch_child_id($id, @args); } elsif (-d $_) { push @dir, $_; } @@ -943,7 +958,6 @@ sub read_uuid { $SVN_UUID = $info->{'Repository UUID'} or croak "Repository UUID unreadable\n"; } - s_to_file($SVN_UUID,"$GIT_SVN_DIR/info/uuid"); } sub quiet_run { @@ -1107,7 +1121,7 @@ sub parse_diff_tree { croak "Error parsing $_\n"; } } - close $diff_fh or croak $!; + close $diff_fh or croak $?; return \@mods; } @@ -1348,7 +1362,7 @@ sub get_commit_message { print $msg $_ or croak $!; } } - close $msg_fh or croak $!; + close $msg_fh or croak $?; } close $msg or croak $!; @@ -1562,7 +1576,7 @@ sub svn_info { push @{$ret->{-order}}, $1; } } - close $info_fh or croak $!; + close $info_fh or croak $?; return $ret; } @@ -1638,7 +1652,7 @@ sub do_update_index { } print $ui $x,"\0"; } - close $ui or croak $!; + close $ui or croak $?; } sub index_changes { @@ -1765,11 +1779,15 @@ sub git_commit { # this output is read via pipe, do not change: print "r$log_msg->{revision} = $commit\n"; + check_repack(); + return $commit; +} + +sub check_repack { if ($_repack && (--$_repack_nr == 0)) { $_repack_nr = $_repack; sys("git repack $_repack_flags"); } - return $commit; } sub set_commit_env { @@ -1877,6 +1895,10 @@ sub svn_cmd_checkout { } sub check_upgrade_needed { + if (!-r $REVDB) { + open my $fh, '>>',$REVDB or croak $!; + close $fh; + } my $old = eval { my $pid = open my $child, '-|'; defined $pid or croak $!; @@ -2026,7 +2048,8 @@ sub migration_check { sub find_rev_before { my ($r, $id, $eq_ok) = @_; my $f = "$GIT_DIR/svn/$id/.rev_db"; - # --$r unless $eq_ok; + return (undef,undef) unless -r $f; + --$r unless $eq_ok; while ($r > 0) { if (my $c = revdb_get($f, $r)) { return ($r, $c); @@ -2072,7 +2095,7 @@ sub set_default_vals { if (defined $_repack) { $_repack = 1000 if ($_repack <= 0); $_repack_nr = $_repack; - $_repack_flags ||= ''; + $_repack_flags ||= '-d'; } } @@ -2352,7 +2375,7 @@ sub libsvn_get_file { close $ho or croak $?; $hash =~ /^$sha1$/o or die "not a sha1: $hash\n"; print $gui $mode,' ',$hash,"\t",$p,"\0" or croak $!; - close $fd or croak $!; + close $fd or croak $?; } sub libsvn_log_entry { @@ -2381,7 +2404,7 @@ sub process_rm { while (<$ls>) { print $gui '0 ',0 x 40,"\t",$_ or croak $!; } - close $ls or croak $!; + close $ls or croak $?; } else { print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!; } @@ -2411,7 +2434,7 @@ sub libsvn_fetch { $pool->clear; } libsvn_get_file($gui, $_, $rev) foreach (@amr); - close $gui or croak $!; + close $gui or croak $?; return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]); } @@ -2514,36 +2537,30 @@ sub revisions_eq { } sub libsvn_find_parent_branch { - return undef; # XXX this function is disabled atm (not tested enough) my ($paths, $rev, $author, $date, $msg) = @_; my $svn_path = '/'.$SVN_PATH; # look for a parent from another branch: - foreach (keys %$paths) { - next if ($_ ne $svn_path); - my $i = $paths->{$_}; - my $branch_from = $i->copyfrom_path or next; - my $r = $i->copyfrom_rev; - print STDERR "Found possible branch point: ", - "$branch_from => $svn_path, $r\n"; - $branch_from =~ s#^/##; - my $l_map = read_url_paths(); - my $url = $SVN->{url}; - defined $l_map->{$url} or next; - my $id = $l_map->{$url}->{$branch_from} or next; - my ($r0, $parent) = find_rev_before($r,$id,1); - if (defined $r0 && defined $parent && - revisions_eq($branch_from, $r0, $r)) { - unlink $GIT_SVN_INDEX; - print STDERR "Found branch parent: $parent\n"; - sys(qw/git-read-tree/, $parent); - return libsvn_fetch($parent, $paths, $rev, - $author, $date, $msg); - } else { - print STDERR - "Nope, branch point not imported or unknown\n"; - } - } + my $i = $paths->{$svn_path} or return; + my $branch_from = $i->copyfrom_path or return; + my $r = $i->copyfrom_rev; + print STDERR "Found possible branch point: ", + "$branch_from => $svn_path, $r\n"; + $branch_from =~ s#^/##; + my $l_map = read_url_paths(); + my $url = $SVN->{url}; + defined $l_map->{$url} or return; + my $id = $l_map->{$url}->{$branch_from} or return; + my ($r0, $parent) = find_rev_before($r,$id,1); + return unless (defined $r0 && defined $parent); + if (revisions_eq($branch_from, $r0, $r)) { + unlink $GIT_SVN_INDEX; + print STDERR "Found branch parent: $parent\n"; + sys(qw/git-read-tree/, $parent); + return libsvn_fetch($parent, $paths, $rev, + $author, $date, $msg); + } + print STDERR "Nope, branch point not imported or unknown\n"; return undef; } @@ -2556,7 +2573,7 @@ sub libsvn_new_tree { my $pool = SVN::Pool->new; libsvn_traverse($gui, '', $SVN_PATH, $rev, $pool); $pool->clear; - close $gui or croak $!; + close $gui or croak $?; return libsvn_log_entry($rev, $author, $date, $msg); } @@ -2630,7 +2647,7 @@ sub libsvn_commit_cb { exit 1; } } else { - fetch_lib("$rev=$c"); + fetch("$rev=$c"); } } @@ -2664,7 +2681,6 @@ sub libsvn_skip_unknown_revs { # 175002 - http(s):// # More codes may be discovered later... if ($errno == 175002 || $errno == 160013) { - print STDERR "directory non-existent\n"; return; } croak "Error from SVN, ($errno): ", $err->expanded_message,"\n"; -- cgit v1.2.3 From 968bdf1f3da677255c8950bb5b5a9de7e1150279 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 15 Jun 2006 13:36:12 -0700 Subject: git-svn: Eliminate temp file usage in libsvn_get_file() This means we'll have a loose object when we encounter a symlink but that's not the common case. We also don't have to worry about svn:eol-style when using the SVN libraries, either. So remove the code to deal with that. Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 56 ++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 27f1d682c8..149149f0ef 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -31,6 +31,7 @@ use File::Path qw/mkpath/; use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/; use File::Spec qw//; use POSIX qw/strftime/; +use IPC::Open3; use Memoize; memoize('revisions_eq'); @@ -2335,47 +2336,36 @@ sub libsvn_get_file { my $p = $f; return unless ($p =~ s#^\Q$SVN_PATH\E/?##); - my $fd = IO::File->new_tmpfile or croak $!; + my ($hash, $pid, $in, $out); my $pool = SVN::Pool->new; - my ($r, $props) = $SVN->get_file($f, $rev, $fd, $pool); + defined($pid = open3($in, $out, '>&STDERR', + qw/git-hash-object -w --stdin/)) or croak $!; + my ($r, $props) = $SVN->get_file($f, $rev, $in, $pool); + $in->flush == 0 or croak $!; + close $in or croak $!; $pool->clear; - $fd->flush == 0 or croak $!; - seek $fd, 0, 0 or croak $!; - if (my $es = $props->{'svn:eol-style'}) { - my $new_fd = IO::File->new_tmpfile or croak $!; - eol_cp_fd($fd, $new_fd, $es); - close $fd or croak $!; - $fd = $new_fd; - seek $fd, 0, 0 or croak $!; - $fd->flush == 0 or croak $!; - } - my $mode = '100644'; - if (exists $props->{'svn:executable'}) { - $mode = '100755'; - } + chomp($hash = do { local $/; <$out> }); + close $out or croak $!; + waitpid $pid, 0; + $hash =~ /^$sha1$/o or die "not a sha1: $hash\n"; + + my $mode = exists $props->{'svn:executable'} ? '100755' : '100644'; if (exists $props->{'svn:special'}) { $mode = '120000'; - local $/; - my $link = <$fd>; + my $link = `git-cat-file blob $hash`; $link =~ s/^link // or die "svn:special file with contents: <", $link, "> is not understood\n"; - seek $fd, 0, 0 or croak $!; - truncate $fd, 0 or croak $!; - print $fd $link or croak $!; - seek $fd, 0, 0 or croak $!; - $fd->flush == 0 or croak $!; - } - my $pid = open my $ho, '-|'; - defined $pid or croak $!; - if (!$pid) { - open STDIN, '<&', $fd or croak $!; - exec qw/git-hash-object -w --stdin/ or croak $!; + defined($pid = open3($in, $out, '>&STDERR', + qw/git-hash-object -w --stdin/)) or croak $!; + print $in $link; + $in->flush == 0 or croak $!; + close $in or croak $!; + chomp($hash = do { local $/; <$out> }); + close $out or croak $!; + waitpid $pid, 0; + $hash =~ /^$sha1$/o or die "not a sha1: $hash\n"; } - chomp(my $hash = do { local $/; <$ho> }); - close $ho or croak $?; - $hash =~ /^$sha1$/o or die "not a sha1: $hash\n"; print $gui $mode,' ',$hash,"\t",$p,"\0" or croak $!; - close $fd or croak $?; } sub libsvn_log_entry { -- cgit v1.2.3 From c0d4822268d30b1668e94986a845b8bb5441e8b3 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 15 Jun 2006 18:48:22 -0700 Subject: git-svn: bugfix and optimize the 'log' command Revisions with long commit messages were being skipped, since the 'git-svn-id' metadata line was at the end and git-log uses a 32k buffer to print the commits. Also the last 'git-svn-id' metadata line in a commit is always the valid one, so make sure we use that, as well. Made the verbose flag work by passing the correct option switch ('--summary') to git-log. Finally, optimize -r/--revision argument handling by passing the appropriate limits to revision Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 60 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 149149f0ef..417fcf1feb 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -663,17 +663,15 @@ sub show_log { my $pid = open(my $log,'-|'); defined $pid or croak $!; if (!$pid) { - my @rl = (qw/git-log --abbrev-commit --pretty=raw - --default/, "remotes/$GIT_SVN"); - push @rl, '--raw' if $_verbose; - exec(@rl, @args) or croak $!; + exec(git_svn_log_cmd($r_min,$r_max), @args) or croak $!; } setup_pager(); my (@k, $c, $d); + while (<$log>) { if (/^commit ($sha1_short)/o) { my $cmt = $1; - if ($c && defined $c->{r} && $c->{r} != $r_last) { + if ($c && cmt_showable($c) && $c->{r} != $r_last) { $r_last = $c->{r}; process_commit($c, $r_min, $r_max, \@k) or goto out; @@ -692,8 +690,7 @@ sub show_log { } elsif ($d) { push @{$c->{diff}}, $_; } elsif (/^ (git-svn-id:.+)$/) { - my ($url, $rev, $uuid) = extract_metadata($1); - $c->{r} = $rev; + (undef, $c->{r}, undef) = extract_metadata($1); } elsif (s/^ //) { push @{$c->{l}}, $_; } @@ -715,6 +712,52 @@ out: ########################### utility functions ######################### +sub cmt_showable { + my ($c) = @_; + return 1 if defined $c->{r}; + if ($c->{l} && $c->{l}->[-1] eq "...\n" && + $c->{a_raw} =~ /\@([a-f\d\-]+)>$/) { + my @msg = safe_qx(qw/git-cat-file commit/, $c->{c}); + shift @msg while ($msg[0] ne "\n"); + shift @msg; + @{$c->{l}} = grep !/^git-svn-id: /, @msg; + + (undef, $c->{r}, undef) = extract_metadata( + (grep(/^git-svn-id: /, @msg))[-1]); + } + return defined $c->{r}; +} + +sub git_svn_log_cmd { + my ($r_min, $r_max) = @_; + my @cmd = (qw/git-log --abbrev-commit --pretty=raw + --default/, "refs/remotes/$GIT_SVN"); + push @cmd, '--summary' if $_verbose; + return @cmd unless defined $r_max; + if ($r_max == $r_min) { + push @cmd, '--max-count=1'; + if (my $c = revdb_get($REVDB, $r_max)) { + push @cmd, $c; + } + } else { + my ($c_min, $c_max); + $c_max = revdb_get($REVDB, $r_max); + $c_min = revdb_get($REVDB, $r_min); + if ($c_min && $c_max) { + if ($r_max > $r_max) { + push @cmd, "$c_min..$c_max"; + } else { + push @cmd, "$c_max..$c_min"; + } + } elsif ($r_max > $r_min) { + push @cmd, $c_max; + } else { + push @cmd, $c_min; + } + } + return @cmd; +} + sub fetch_child_id { my $id = shift; print "Fetching $id\n"; @@ -2206,6 +2249,7 @@ sub setup_pager { # translated to Perl from pager.c sub get_author_info { my ($dest, $author, $t, $tz) = @_; $author =~ s/(?:^\s*|\s*$)//g; + $dest->{a_raw} = $author; my $_a; if ($_authors) { $_a = $rusers{$author} || undef; @@ -2440,7 +2484,7 @@ sub svn_grab_base_rev { close $fh; if (defined $c && length $c) { my ($url, $rev, $uuid) = extract_metadata((grep(/^git-svn-id: /, - safe_qx(qw/git-cat-file commit/, $c)))[0]); + safe_qx(qw/git-cat-file commit/, $c)))[-1]); return ($rev, $c); } return (undef, undef); -- cgit v1.2.3 From 86f363791b281fb916414a89282b2e67cdaa36c0 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 15 Jun 2006 19:13:56 -0700 Subject: git-svn: tests no longer fail if LC_ALL is not a UTF-8 locale Signed-off-by: Eric Wong --- contrib/git-svn/Makefile | 5 +++-- contrib/git-svn/t/t0000-contrib-git-svn.sh | 8 ++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile index d73aa5641c..6aedb10f12 100644 --- a/contrib/git-svn/Makefile +++ b/contrib/git-svn/Makefile @@ -32,9 +32,10 @@ test: git-svn cd t && $(SHELL) ./t0000-contrib-git-svn.sh $(TEST_FLAGS) cd t && $(SHELL) ./t0001-contrib-git-svn-props.sh $(TEST_FLAGS) +# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL full-test: - $(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1 - $(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=1 + $(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C + $(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C $(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \ LC_ALL=en_US.UTF-8 $(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \ diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh index f896e2c2a8..0f52746647 100644 --- a/contrib/git-svn/t/t0000-contrib-git-svn.sh +++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh @@ -194,8 +194,12 @@ test_expect_success "$name" \ diff -u a b" name='check imported tree checksums expected tree checksums' -cat > expected <<\EOF -tree f735671b89a7eb30cab1d8597de35bd4271ab813 +rm -f expected +if test -n "$GIT_SVN_LC_ALL" && echo $GIT_SVN_LC_ALL | grep -q '\.UTF-8$' +then + echo tree f735671b89a7eb30cab1d8597de35bd4271ab813 > expected +fi +cat >> expected <<\EOF tree 4b9af72bb861eaed053854ec502cf7df72618f0f tree 031b8d557afc6fea52894eaebb45bec52f1ba6d1 tree 0b094cbff17168f24c302e297f55bfac65eb8bd3 -- cgit v1.2.3 From 0e8a002c59cbaca692dac81320be44c965588291 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 15 Jun 2006 19:51:05 -0700 Subject: git-svn: svn (command-line) 1.0.x compatibility Tested on a plain Ubuntu Warty installation using subversion 1.0.6-1.2ubuntu3 svn add --force was never needed, as it only affected directories, which git (thankfully) doesn't track The 1.0.x also didn't support symlinks(!), so allow NO_SYMLINK to be defined for running tests Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 4 +- contrib/git-svn/t/t0000-contrib-git-svn.sh | 90 ++++++++++++++++-------------- 2 files changed, 51 insertions(+), 43 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 417fcf1feb..ab1d06500d 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -1306,12 +1306,12 @@ sub svn_checkout_tree { } elsif ($m->{chg} eq 'T') { sys(qw(svn rm --force),$m->{file_b}); apply_mod_line_blob($m); - sys(qw(svn add --force), $m->{file_b}); + sys(qw(svn add), $m->{file_b}); svn_check_prop_executable($m); } elsif ($m->{chg} eq 'A') { svn_ensure_parent_path( $m->{file_b} ); apply_mod_line_blob($m); - sys(qw(svn add --force), $m->{file_b}); + sys(qw(svn add), $m->{file_b}); svn_check_prop_executable($m); } else { croak "Invalid chg: $m->{chg}\n"; diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh index 0f52746647..443d518367 100644 --- a/contrib/git-svn/t/t0000-contrib-git-svn.sh +++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh @@ -11,7 +11,10 @@ mkdir import cd import echo foo > foo -ln -s foo foo.link +if test -z "$NO_SYMLINK" +then + ln -s foo foo.link +fi mkdir -p dir/a/b/c/d/e echo 'deep dir' > dir/a/b/c/d/e/file mkdir -p bar @@ -129,46 +132,45 @@ test_expect_success "$name" \ -name='executable file becomes a symlink to bar/zzz (file)' -rm exec.sh -ln -s bar/zzz exec.sh -git update-index exec.sh -git commit -m "$name" - -test_expect_success "$name" \ - "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && - svn up $SVN_TREE && - test -L $SVN_TREE/exec.sh" - - - -name='new symlink is added to a file that was also just made executable' -chmod +x bar/zzz -ln -s bar/zzz exec-2.sh -git update-index --add bar/zzz exec-2.sh -git commit -m "$name" - -test_expect_success "$name" \ - "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && - svn up $SVN_TREE && - test -x $SVN_TREE/bar/zzz && - test -L $SVN_TREE/exec-2.sh" - - - -name='modify a symlink to become a file' -git help > help || true -rm exec-2.sh -cp help exec-2.sh -git update-index exec-2.sh -git commit -m "$name" - -test_expect_success "$name" \ - "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && - svn up $SVN_TREE && - test -f $SVN_TREE/exec-2.sh && - test ! -L $SVN_TREE/exec-2.sh && - diff -u help $SVN_TREE/exec-2.sh" +if test -z "$NO_SYMLINK" +then + name='executable file becomes a symlink to bar/zzz (file)' + rm exec.sh + ln -s bar/zzz exec.sh + git update-index exec.sh + git commit -m "$name" + + test_expect_success "$name" \ + "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && + svn up $SVN_TREE && + test -L $SVN_TREE/exec.sh" + + name='new symlink is added to a file that was also just made executable' + chmod +x bar/zzz + ln -s bar/zzz exec-2.sh + git update-index --add bar/zzz exec-2.sh + git commit -m "$name" + + test_expect_success "$name" \ + "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && + svn up $SVN_TREE && + test -x $SVN_TREE/bar/zzz && + test -L $SVN_TREE/exec-2.sh" + + name='modify a symlink to become a file' + git help > help || true + rm exec-2.sh + cp help exec-2.sh + git update-index exec-2.sh + git commit -m "$name" + + test_expect_success "$name" \ + "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && + svn up $SVN_TREE && + test -f $SVN_TREE/exec-2.sh && + test ! -L $SVN_TREE/exec-2.sh && + diff -u help $SVN_TREE/exec-2.sh" +fi if test -n "$GIT_SVN_LC_ALL" && echo $GIT_SVN_LC_ALL | grep -q '\.UTF-8$' @@ -193,6 +195,12 @@ test_expect_success "$name" \ git-rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b && diff -u a b" +if test -n "$NO_SYMLINK" +then + test_done + exit 0 +fi + name='check imported tree checksums expected tree checksums' rm -f expected if test -n "$GIT_SVN_LC_ALL" && echo $GIT_SVN_LC_ALL | grep -q '\.UTF-8$' -- cgit v1.2.3 From 1a82e79315ed633f6b0b1fc4076054950c5380d3 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 16 Jun 2006 02:55:13 -0700 Subject: git-svn: rebuild convenience and bugfixes We will now automatically fetch the refs/remotes/git-svn ref from origin and store a Pull: line for it. --remote= may be passed if your remote is named something other than 'origin' Also, remember to make GIT_SVN_DIR whenever we need to create .rev_db Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index ab1d06500d..da0ff9ad8a 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -42,7 +42,7 @@ my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS}; my $sha1 = qr/[a-f\d]{40}/; my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, - $_find_copies_harder, $_l, $_cp_similarity, + $_find_copies_harder, $_l, $_cp_similarity, $_cp_remote, $_repack, $_repack_nr, $_repack_flags, $_template, $_shared, $_no_default_regex, $_no_graft_copy, $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit, @@ -86,6 +86,7 @@ my %cmd = ( { 'revision|r=i' => \$_revision } ], rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)", { 'no-ignore-externals' => \$_no_ignore_ext, + 'copy-remote|remote=s' => \$_cp_remote, 'upgrade' => \$_upgrade } ], 'graft-branches' => [ \&graft_branches, 'Detect merges/branches from already imported history', @@ -134,7 +135,7 @@ init_vars(); load_authors() if $_authors; load_all_refs() if $_branch_all_refs; svn_compat_check(); -migration_check() unless $cmd =~ /^(?:init|multi-init)$/; +migration_check() unless $cmd =~ /^(?:init|rebuild|multi-init)$/; $cmd{$cmd}->[0]->(@ARGV); exit 0; @@ -174,6 +175,9 @@ sub version { } sub rebuild { + if (quiet_run(qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0")) { + copy_remote_ref(); + } $SVN_URL = shift or undef; my $newest_rev = 0; if ($_upgrade) { @@ -1940,6 +1944,7 @@ sub svn_cmd_checkout { sub check_upgrade_needed { if (!-r $REVDB) { + -d $GIT_SVN_DIR or mkpath([$GIT_SVN_DIR]); open my $fh, '>>',$REVDB or croak $!; close $fh; } @@ -2052,6 +2057,7 @@ sub migrate_revdb { init_vars(); exit 0 if -r $REVDB; print "Upgrading svn => git mapping...\n"; + -d $GIT_SVN_DIR or mkpath([$GIT_SVN_DIR]); open my $fh, '>>',$REVDB or croak $!; close $fh; rebuild(); @@ -2763,6 +2769,17 @@ sub revdb_get { return $ret; } +sub copy_remote_ref { + my $origin = $_cp_remote ? $_cp_remote : 'origin'; + my $ref = "refs/remotes/$GIT_SVN"; + if (safe_qx('git-ls-remote', $origin, $ref)) { + sys(qw/git fetch/, $origin, "$ref:$ref"); + } else { + die "Unable to find remote reference: ", + "refs/remotes/$GIT_SVN on $origin\n"; + } +} + package SVN::Git::Editor; use vars qw/@ISA/; use strict; -- cgit v1.2.3 From c07eee1f2ab9e9b168e050c0ef0b48d039c71470 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 19 Jun 2006 17:59:35 -0700 Subject: git-svn: fix --rmdir when using SVN:: libraries When tracking directories with nearly all of its files at the most nested levels, --rmdir would accidentally go too far when deleting. Of course, we'll add a test for this condition, too. Makefile: automatically run new tests as they appear in t/ Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/Makefile | 3 +-- contrib/git-svn/git-svn.perl | 15 +++++++++++---- contrib/git-svn/t/t0002-deep-rmdir.sh | 29 +++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 contrib/git-svn/t/t0002-deep-rmdir.sh (limited to 'contrib') diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile index 6aedb10f12..7c20946943 100644 --- a/contrib/git-svn/Makefile +++ b/contrib/git-svn/Makefile @@ -29,8 +29,7 @@ git-svn.html : git-svn.txt asciidoc -b xhtml11 -d manpage \ -f ../../Documentation/asciidoc.conf $< test: git-svn - cd t && $(SHELL) ./t0000-contrib-git-svn.sh $(TEST_FLAGS) - cd t && $(SHELL) ./t0001-contrib-git-svn-props.sh $(TEST_FLAGS) + cd t && for i in t????-*.sh; do $(SHELL) ./$$i $(TEST_FLAGS); done # we can test NO_OPTIMIZE_COMMITS independently of LC_ALL full-test: diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index da0ff9ad8a..7e7f2f0cd9 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -2841,13 +2841,20 @@ sub rmdirs { exec qw/git-ls-tree --name-only -r -z/, $self->{c} or croak $!; } local $/ = "\0"; + my @svn_path = split m#/#, $self->{svn_path}; while (<$fh>) { chomp; - $_ = $self->{svn_path} . '/' . $_; - my ($dn) = ($_ =~ m#^(.*?)/?(?:[^/]+)$#); - delete $rm->{$dn}; - last unless %$rm; + my @dn = (@svn_path, (split m#/#, $_)); + while (pop @dn) { + delete $rm->{join '/', @dn}; + } + unless (%$rm) { + close $fh; + return; + } } + close $fh; + my ($r, $p, $bat) = ($self->{r}, $self->{pool}, $self->{bat}); foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) { $self->close_directory($bat->{$d}, $p); diff --git a/contrib/git-svn/t/t0002-deep-rmdir.sh b/contrib/git-svn/t/t0002-deep-rmdir.sh new file mode 100644 index 0000000000..d693d183c8 --- /dev/null +++ b/contrib/git-svn/t/t0002-deep-rmdir.sh @@ -0,0 +1,29 @@ +test_description='git-svn rmdir' +. ./lib-git-svn.sh + +test_expect_success 'initialize repo' " + mkdir import && + cd import && + mkdir -p deeply/nested/directory/number/1 && + mkdir -p deeply/nested/directory/number/2 && + echo foo > deeply/nested/directory/number/1/file && + echo foo > deeply/nested/directory/number/2/another && + svn import -m 'import for git-svn' . $svnrepo && + cd .. + " + +test_expect_success 'mirror via git-svn' " + git-svn init $svnrepo && + git-svn fetch && + git checkout -f -b test-rmdir remotes/git-svn + " + +test_expect_success 'Try a commit on rmdir' " + git rm -f deeply/nested/directory/number/2/another && + git commit -a -m 'remove another' && + git-svn commit --rmdir HEAD && + svn ls -R $svnrepo | grep ^deeply/nested/directory/number/1 + " + + +test_done -- cgit v1.2.3 From ec9d00d078ed1625d8c85a25618dfa2aa8042b69 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 22 Jun 2006 01:22:46 -0700 Subject: git-svn: fix commit --edit flag when using SVN:: libraries Trying to open an interactive editor in the console while stdout is being piped to the parent process doesn't work out very well. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 7e7f2f0cd9..08c30103f5 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -479,17 +479,18 @@ sub commit_lib { my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : (); my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$"; + if (defined $LC_ALL) { + $ENV{LC_ALL} = $LC_ALL; + } else { + delete $ENV{LC_ALL}; + } foreach my $c (@revs) { + my $log_msg = get_commit_message($c, $commit_msg); + # fork for each commit because there's a memory leak I # can't track down... (it's probably in the SVN code) defined(my $pid = open my $fh, '-|') or croak $!; if (!$pid) { - if (defined $LC_ALL) { - $ENV{LC_ALL} = $LC_ALL; - } else { - delete $ENV{LC_ALL}; - } - my $log_msg = get_commit_message($c, $commit_msg); my $ed = SVN::Git::Editor->new( { r => $r_last, ra => $SVN, @@ -535,6 +536,7 @@ sub commit_lib { ($r_last, $cmt_last) = ($r_new, $cmt_new); } } + $ENV{LC_ALL} = 'C'; unlink $commit_msg; } -- cgit v1.2.3 From dc62e25cbd0ec1726d5108a8caa539e60492cc7a Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 28 Jun 2006 03:07:14 -0700 Subject: git-svn: SVN 1.1.x library compatibility Tested on a plain Ubuntu Hoary installation using subversion 1.1.1-2ubuntu3 1.1.x issues I had to deal with: * Avoid the noisy command-line client compatibility check if we use the libraries. * get_log() arguments differ (now using a nice wrapper from Junio's suggestion) * get_file() is picky about what kind of file handles it gets, so I ended up redirecting STDOUT. I'm probably overflushing my file handles, but that's the safest thing to do... * BDB kept segfaulting on me during tests, so svnadmin will use FSFS whenever we can. * If somebody used an expanded CVS $Id$ line inside a file, then propsetting it to use svn:keywords will cause the original CVS $Id$ to be retained when asked for the original file. As far as I can see, this is a server-side issue. We won't care in the test anymore, as long as it's not expanded by SVN, a static CVS $Id$ line is fine. While we're at making ourselves more compatible, avoid grep along with the -q flag, which is GNU-specific. (grep avoidance tip from Junio, too) Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 30 ++++++++++++++++++------ contrib/git-svn/t/lib-git-svn.sh | 8 ++++++- contrib/git-svn/t/t0000-contrib-git-svn.sh | 14 +++++++++-- contrib/git-svn/t/t0001-contrib-git-svn-props.sh | 4 ++-- 4 files changed, 44 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 08c30103f5..f026b240b8 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -134,7 +134,7 @@ usage(1) unless defined $cmd; init_vars(); load_authors() if $_authors; load_all_refs() if $_branch_all_refs; -svn_compat_check(); +svn_compat_check() unless $_use_lib; migration_check() unless $cmd =~ /^(?:init|rebuild|multi-init)$/; $cmd{$cmd}->[0]->(@ARGV); exit 0; @@ -379,7 +379,8 @@ sub fetch_lib { # performance sucks with it enabled, so it's much # faster to fetch revision ranges instead of relying # on the limiter. - $SVN_LOG->get_log( '/'.$SVN_PATH, $min, $max, 0, 1, 1, + libsvn_get_log($SVN_LOG, '/'.$SVN_PATH, + $min, $max, 0, 1, 1, sub { my $log_msg; if ($last_commit) { @@ -924,7 +925,7 @@ sub graft_file_copy_lib { $SVN::Error::handler = \&libsvn_skip_unknown_revs; while (1) { my $pool = SVN::Pool->new; - $SVN_LOG->get_log( "/$path", $min, $max, 0, 1, 1, + libsvn_get_log($SVN_LOG, "/$path", $min, $max, 0, 1, 1, sub { libsvn_graft_file_copies($grafts, $tree_paths, $path, @_); @@ -2358,8 +2359,8 @@ sub libsvn_load { return unless $_use_lib; $_use_lib = eval { require SVN::Core; - if ($SVN::Core::VERSION lt '1.2.1') { - die "Need SVN::Core 1.2.1 or better ", + if ($SVN::Core::VERSION lt '1.1.0') { + die "Need SVN::Core 1.1.0 or better ", "(got $SVN::Core::VERSION) ", "Falling back to command-line svn\n"; } @@ -2392,9 +2393,15 @@ sub libsvn_get_file { my $pool = SVN::Pool->new; defined($pid = open3($in, $out, '>&STDERR', qw/git-hash-object -w --stdin/)) or croak $!; - my ($r, $props) = $SVN->get_file($f, $rev, $in, $pool); + # redirect STDOUT for SVN 1.1.x compatibility + open my $stdout, '>&', \*STDOUT or croak $!; + open STDOUT, '>&', $in or croak $!; + $| = 1; # not sure if this is necessary, better safe than sorry... + my ($r, $props) = $SVN->get_file($f, $rev, \*STDOUT, $pool); $in->flush == 0 or croak $!; + open STDOUT, '>&', $stdout or croak $!; close $in or croak $!; + close $stdout or croak $!; $pool->clear; chomp($hash = do { local $/; <$out> }); close $out or croak $!; @@ -2566,7 +2573,8 @@ sub revisions_eq { if ($_use_lib) { # should be OK to use Pool here (r1 - r0) should be small my $pool = SVN::Pool->new; - $SVN->get_log("/$path", $r0, $r1, 0, 1, 1, sub {$nr++},$pool); + libsvn_get_log($SVN, "/$path", $r0, $r1, + 0, 1, 1, sub {$nr++}, $pool); $pool->clear; } else { my ($url, undef) = repo_path_split($SVN_URL); @@ -2606,6 +2614,14 @@ sub libsvn_find_parent_branch { return undef; } +sub libsvn_get_log { + my ($ra, @args) = @_; + if ($SVN::Core::VERSION le '1.2.0') { + splice(@args, 3, 1); + } + $ra->get_log(@args); +} + sub libsvn_new_tree { if (my $log_entry = libsvn_find_parent_branch(@_)) { return $log_entry; diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh index 2843258fc4..d7f972a0c8 100644 --- a/contrib/git-svn/t/lib-git-svn.sh +++ b/contrib/git-svn/t/lib-git-svn.sh @@ -33,7 +33,13 @@ svnrepo=$PWD/svnrepo set -e -svnadmin create $svnrepo +if svnadmin create --help | grep fs-type >/dev/null +then + svnadmin create --fs-type fsfs "$svnrepo" +else + svnadmin create "$svnrepo" +fi + svnrepo="file://$svnrepo/test-git-svn" diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh index 443d518367..b482bb64c0 100644 --- a/contrib/git-svn/t/t0000-contrib-git-svn.sh +++ b/contrib/git-svn/t/t0000-contrib-git-svn.sh @@ -5,6 +5,16 @@ test_description='git-svn tests' GIT_SVN_LC_ALL=$LC_ALL + +case "$LC_ALL" in +*.UTF-8) + have_utf8=t + ;; +*) + have_utf8= + ;; +esac + . ./lib-git-svn.sh mkdir import @@ -173,7 +183,7 @@ then fi -if test -n "$GIT_SVN_LC_ALL" && echo $GIT_SVN_LC_ALL | grep -q '\.UTF-8$' +if test "$have_utf8" = t then name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL" echo '# hello' >> exec-2.sh @@ -203,7 +213,7 @@ fi name='check imported tree checksums expected tree checksums' rm -f expected -if test -n "$GIT_SVN_LC_ALL" && echo $GIT_SVN_LC_ALL | grep -q '\.UTF-8$' +if test "$have_utf8" = t then echo tree f735671b89a7eb30cab1d8597de35bd4271ab813 > expected fi diff --git a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh index 54e0ed7353..a5a235f100 100644 --- a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh +++ b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh @@ -21,8 +21,8 @@ a_empty_crlf= cd import cat >> kw.c <<\EOF -/* Make it look like somebody copied a file from CVS into SVN: */ -/* $Id: kw.c,v 1.1.1.1 1994/03/06 00:00:00 eric Exp $ */ +/* Somebody prematurely put a keyword into this file */ +/* $Id$ */ EOF printf "Hello\r\nWorld\r\n" > crlf -- cgit v1.2.3 From c1927a8554284d0dc5bb7fcf8e79d3877e4a497c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 27 Jun 2006 19:39:11 -0700 Subject: git-svn: several graft-branches improvements The 'graft-branches' command can now analyze tree matches for merge detection after commits are done, when --branch or --branch-all-refs options are used. We ensure that tree joins (--branch and --branch-all-refs options) during commit time only add SVN parents that occurred before the commit we're importing Also fixed branch detection via merge messages, this manner of merge detection (a la git-svnimport) is really all fuzzy, but at least it actually works now :) Add some new tests to go along with these fixes, too. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 180 +++++++++++++++++++++++++++--- contrib/git-svn/t/t0003-graft-branches.sh | 63 +++++++++++ 2 files changed, 230 insertions(+), 13 deletions(-) create mode 100644 contrib/git-svn/t/t0003-graft-branches.sh (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index f026b240b8..be38f94170 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -34,6 +34,8 @@ use POSIX qw/strftime/; use IPC::Open3; use Memoize; memoize('revisions_eq'); +memoize('cmt_metadata'); +memoize('get_commit_time'); my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib); $_use_lib = 1 unless $ENV{GIT_SVN_NO_LIB}; @@ -91,6 +93,8 @@ my %cmd = ( 'graft-branches' => [ \&graft_branches, 'Detect merges/branches from already imported history', { 'merge-rx|m' => \@_opt_m, + 'branch|b=s' => \@_branch_from, + 'branch-all-refs|B' => \$_branch_all_refs, 'no-default-regex' => \$_no_default_regex, 'no-graft-copy' => \$_no_graft_copy } ], 'multi-init' => [ \&multi_init, @@ -590,13 +594,14 @@ sub graft_branches { my $l_map = read_url_paths(); my @re = map { qr/$_/is } @_opt_m if @_opt_m; unless ($_no_default_regex) { - push @re, ( qr/\b(?:merge|merging|merged)\s+(\S.+)/is, - qr/\b(?:from|of)\s+(\S.+)/is ); + push @re, (qr/\b(?:merge|merging|merged)\s+with\s+([\w\.\-]+)/i, + qr/\b(?:merge|merging|merged)\s+([\w\.\-]+)/i, + qr/\b(?:from|of)\s+([\w\.\-]+)/i ); } foreach my $u (keys %$l_map) { if (@re) { foreach my $p (keys %{$l_map->{$u}}) { - graft_merge_msg($grafts,$l_map,$u,$p); + graft_merge_msg($grafts,$l_map,$u,$p,@re); } } unless ($_no_graft_copy) { @@ -607,6 +612,7 @@ sub graft_branches { } } } + graft_tree_joins($grafts); write_grafts($grafts, $comments, $gr_file); unlink "$gr_file~$gr_sha1" if $gr_sha1; @@ -879,6 +885,77 @@ sub common_prefix { return ''; } +# grafts set here are 'stronger' in that they're based on actual tree +# matches, and won't be deleted from merge-base checking in write_grafts() +sub graft_tree_joins { + my $grafts = shift; + map_tree_joins() if (@_branch_from && !%tree_map); + return unless %tree_map; + + git_svn_each(sub { + my $i = shift; + defined(my $pid = open my $fh, '-|') or croak $!; + if (!$pid) { + exec qw/git-rev-list --pretty=raw/, + "refs/remotes/$i" or croak $!; + } + while (<$fh>) { + next unless /^commit ($sha1)$/o; + my $c = $1; + my ($t) = (<$fh> =~ /^tree ($sha1)$/o); + next unless $tree_map{$t}; + + my $l; + do { + $l = readline $fh; + } until ($l =~ /^committer (?:.+) (\d+) ([\-\+]?\d+)$/); + + my ($s, $tz) = ($1, $2); + if ($tz =~ s/^\+//) { + $s += tz_to_s_offset($tz); + } elsif ($tz =~ s/^\-//) { + $s -= tz_to_s_offset($tz); + } + + my ($url_a, $r_a, $uuid_a) = cmt_metadata($c); + + foreach my $p (@{$tree_map{$t}}) { + next if $p eq $c; + my $mb = eval { + safe_qx('git-merge-base', $c, $p) + }; + next unless ($@ || $?); + if (defined $r_a) { + # see if SVN says it's a relative + my ($url_b, $r_b, $uuid_b) = + cmt_metadata($p); + next if (defined $url_b && + defined $url_a && + ($url_a eq $url_b) && + ($uuid_a eq $uuid_b)); + if ($uuid_a eq $uuid_b) { + if ($r_b < $r_a) { + $grafts->{$c}->{$p} = 2; + next; + } elsif ($r_b > $r_a) { + $grafts->{$p}->{$c} = 2; + next; + } + } + } + my $ct = get_commit_time($p); + if ($ct < $s) { + $grafts->{$c}->{$p} = 2; + } elsif ($ct > $s) { + $grafts->{$p}->{$c} = 2; + } + # what should we do when $ct == $s ? + } + } + close $fh or croak $?; + }); +} + # this isn't funky-filename safe, but good enough for now... sub graft_file_copy_cmd { my ($grafts, $l_map, $u) = @_; @@ -957,7 +1034,7 @@ sub process_merge_msg_matches { my $re = qr/\Q$w\E/i; foreach (keys %{$l_map->{$u}}) { if (/$re/) { - push @strong, $_; + push @strong, $l_map->{$u}->{$_}; last; } } @@ -966,7 +1043,7 @@ sub process_merge_msg_matches { $re = qr/\Q$w\E/i; foreach (keys %{$l_map->{$u}}) { if (/$re/) { - push @strong, $_; + push @strong, $l_map->{$u}->{$_}; last; } } @@ -979,7 +1056,7 @@ sub process_merge_msg_matches { return unless defined $rev; } foreach my $m (@strong) { - my ($r0, $s0) = find_rev_before($rev, $m); + my ($r0, $s0) = find_rev_before($rev, $m, 1); $grafts->{$c->{c}}->{$s0} = 1 if defined $s0; } } @@ -1791,7 +1868,26 @@ sub git_commit { restore_index($index); } if (exists $tree_map{$tree}) { - push @tmp_parents, @{$tree_map{$tree}}; + foreach my $p (@{$tree_map{$tree}}) { + my $skip; + foreach (@tmp_parents) { + # see if a common parent is found + my $mb = eval { + safe_qx('git-merge-base', $_, $p) + }; + next if ($@ || $?); + $skip = 1; + last; + } + next if $skip; + my ($url_p, $r_p, $uuid_p) = cmt_metadata($p); + next if (($SVN_UUID eq $uuid_p) && + ($log_msg->{revision} > $r_p)); + next if (defined $url_p && defined $SVN_URL && + ($SVN_UUID eq $uuid_p) && + ($url_p eq $SVN_URL)); + push @tmp_parents, $p; + } } foreach (@tmp_parents) { next if $seen_parent{$_}; @@ -2119,6 +2215,7 @@ sub init_vars { $GIT_SVN_INDEX = "$GIT_SVN_DIR/index"; $SVN_URL = undef; $SVN_WC = "$GIT_SVN_DIR/tree"; + %tree_map = (); } # convert GetOpt::Long specs for use by git-repo-config @@ -2186,6 +2283,7 @@ sub write_grafts { print $fh $_ foreach @{$comments->{$c}}; } my $p = $grafts->{$c}; + my %x; # real parents delete $p->{$c}; # commits are not self-reproducing... my $pid = open my $ch, '-|'; defined $pid or croak $!; @@ -2193,13 +2291,41 @@ sub write_grafts { exec(qw/git-cat-file commit/, $c) or croak $!; } while (<$ch>) { - if (/^parent ([a-f\d]{40})/) { - $p->{$1} = 1; + if (/^parent ($sha1)/) { + $x{$1} = $p->{$1} = 1; } else { - last unless /^\S/i; + last unless /^\S/; } } close $ch; # breaking the pipe + + # if real parents are the only ones in the grafts, drop it + next if join(' ',sort keys %$p) eq join(' ',sort keys %x); + + my (@ip, @jp, $mb); + my %del = %x; + @ip = @jp = keys %$p; + foreach my $i (@ip) { + next if $del{$i} || $p->{$i} == 2; + foreach my $j (@jp) { + next if $i eq $j || $del{$j} || $p->{$j} == 2; + $mb = eval { safe_qx('git-merge-base',$i,$j) }; + next unless $mb; + chomp $mb; + next if $x{$mb}; + if ($mb eq $j) { + delete $p->{$i}; + $del{$i} = 1; + } elsif ($mb eq $i) { + delete $p->{$j}; + $del{$j} = 1; + } + } + } + + # if real parents are the only ones in the grafts, drop it + next if join(' ',sort keys %$p) eq join(' ',sort keys %x); + print $fh $c, ' ', join(' ', sort keys %$p),"\n"; } if ($comments->{'END'}) { @@ -2219,7 +2345,7 @@ sub read_url_paths { } sub extract_metadata { - my $id = shift; + my $id = shift or return (undef, undef, undef); my ($url, $rev, $uuid) = ($id =~ /^git-svn-id:\s(\S+?)\@(\d+) \s([a-f\d\-]+)$/x); if (!$rev || !$uuid || !$url) { @@ -2230,6 +2356,31 @@ sub extract_metadata { return ($url, $rev, $uuid); } +sub cmt_metadata { + return extract_metadata((grep(/^git-svn-id: /, + safe_qx(qw/git-cat-file commit/, shift)))[-1]); +} + +sub get_commit_time { + my $cmt = shift; + defined(my $pid = open my $fh, '-|') or croak $!; + if (!$pid) { + exec qw/git-rev-list --pretty=raw -n1/, $cmt or croak $!; + } + while (<$fh>) { + /^committer\s(?:.+) (\d+) ([\-\+]?\d+)$/ or next; + my ($s, $tz) = ($1, $2); + if ($tz =~ s/^\+//) { + $s += tz_to_s_offset($tz); + } elsif ($tz =~ s/^\-//) { + $s -= tz_to_s_offset($tz); + } + close $fh; + return $s; + } + die "Can't get commit time for commit: $cmt\n"; +} + sub tz_to_s_offset { my ($tz) = @_; $tz =~ s/(\d\d)$//; @@ -2498,8 +2649,7 @@ sub svn_grab_base_rev { chomp(my $c = do { local $/; <$fh> }); close $fh; if (defined $c && length $c) { - my ($url, $rev, $uuid) = extract_metadata((grep(/^git-svn-id: /, - safe_qx(qw/git-cat-file commit/, $c)))[-1]); + my ($url, $rev, $uuid) = cmt_metadata($c); return ($rev, $c); } return (undef, undef); @@ -2655,6 +2805,10 @@ sub find_graft_path_parents { my $i = $tree_paths->{$x}; my ($r, $parent) = find_rev_before($r0, $i, 1); if (defined $r && defined $parent && revisions_eq($x,$r,$r0)) { + my ($url_b, undef, $uuid_b) = cmt_metadata($c); + my ($url_a, undef, $uuid_a) = cmt_metadata($parent); + next if ($url_a && $url_b && $url_a eq $url_b && + $uuid_b eq $uuid_a); $grafts->{$c}->{$parent} = 1; } } diff --git a/contrib/git-svn/t/t0003-graft-branches.sh b/contrib/git-svn/t/t0003-graft-branches.sh new file mode 100644 index 0000000000..cc62d4ece8 --- /dev/null +++ b/contrib/git-svn/t/t0003-graft-branches.sh @@ -0,0 +1,63 @@ +test_description='git-svn graft-branches' +. ./lib-git-svn.sh + +test_expect_success 'initialize repo' " + mkdir import && + cd import && + mkdir -p trunk branches tags && + echo hello > trunk/readme && + svn import -m 'import for git-svn' . $svnrepo && + cd .. && + svn cp -m 'tag a' $svnrepo/trunk $svnrepo/tags/a && + svn cp -m 'branch a' $svnrepo/trunk $svnrepo/branches/a && + svn co $svnrepo wc && + cd wc && + echo feedme >> branches/a/readme && + svn commit -m hungry && + svn up && + cd trunk && + svn merge -r3:4 $svnrepo/branches/a && + svn commit -m 'merge with a' && + cd ../.. && + svn log -v $svnrepo && + git-svn init -i trunk $svnrepo/trunk && + git-svn init -i a $svnrepo/branches/a && + git-svn init -i tags/a $svnrepo/tags/a && + git-svn fetch -i tags/a && + git-svn fetch -i a && + git-svn fetch -i trunk + " + +r1=`git-rev-list remotes/trunk | tail -n1` +r2=`git-rev-list remotes/tags/a | tail -n1` +r3=`git-rev-list remotes/a | tail -n1` +r4=`git-rev-list remotes/a | head -n1` +r5=`git-rev-list remotes/trunk | head -n1` + +test_expect_success 'test graft-branches regexes and copies' " + test -n "$r1" && + test -n "$r2" && + test -n "$r3" && + test -n "$r4" && + test -n "$r5" && + git-svn graft-branches && + grep '^$r2 $r1' $GIT_DIR/info/grafts && + grep '^$r3 $r1' $GIT_DIR/info/grafts && + grep '^$r5 ' $GIT_DIR/info/grafts | grep '$r4' | grep '$r1' + " + +test_debug 'gitk --all & sleep 1' + +test_expect_success 'test graft-branches with tree-joins' " + rm $GIT_DIR/info/grafts && + git-svn graft-branches --no-default-regex --no-graft-copy -B && + grep '^$r3 ' $GIT_DIR/info/grafts | grep '$r1' | grep '$r2' && + grep '^$r2 $r1' $GIT_DIR/info/grafts && + grep '^$r5 ' $GIT_DIR/info/grafts | grep '$r1' | grep '$r4' + " + +# the result of this is kinda funky, we have a strange history and +# this is just a test :) +test_debug 'gitk --all &' + +test_done -- cgit v1.2.3 From 27e9fb8d415a7bbc14001c14580f4c568e246381 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 27 Jun 2006 19:39:12 -0700 Subject: git-svn: add the commit-diff command This is intended for interoperability with git-svnimport. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 90 +++++++++++++++++++++++++++------- contrib/git-svn/t/t0005-commit-diff.sh | 41 ++++++++++++++++ 2 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 contrib/git-svn/t/t0005-commit-diff.sh (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index be38f94170..4bdc9766cd 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -46,6 +46,7 @@ my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_find_copies_harder, $_l, $_cp_similarity, $_cp_remote, $_repack, $_repack_nr, $_repack_flags, + $_message, $_file, $_template, $_shared, $_no_default_regex, $_no_graft_copy, $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit, $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m); @@ -65,6 +66,12 @@ my %multi_opts = ( 'trunk|T=s' => \$_trunk, 'tags|t=s' => \$_tags, 'branches|b=s' => \$_branches ); my %init_opts = ( 'template=s' => \$_template, 'shared' => \$_shared ); +my %cmt_opts = ( 'edit|e' => \$_edit, + 'rmdir' => \$_rmdir, + 'find-copies-harder' => \$_find_copies_harder, + 'l=i' => \$_l, + 'copy-similarity|C=i'=> \$_cp_similarity +); # yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome: my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" ); @@ -76,14 +83,7 @@ my %cmd = ( " (requires URL argument)", \%init_opts ], commit => [ \&commit, "Commit git revisions to SVN", - { 'stdin|' => \$_stdin, - 'edit|e' => \$_edit, - 'rmdir' => \$_rmdir, - 'find-copies-harder' => \$_find_copies_harder, - 'l=i' => \$_l, - 'copy-similarity|C=i'=> \$_cp_similarity, - %fc_opts, - } ], + { 'stdin|' => \$_stdin, %cmt_opts, %fc_opts, } ], 'show-ignore' => [ \&show_ignore, "Show svn:ignore listings", { 'revision|r=i' => \$_revision } ], rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)", @@ -112,6 +112,10 @@ my %cmd = ( 'show-commit' => \$_show_commit, 'authors-file|A=s' => \$_authors, } ], + 'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees', + { 'message|m=s' => \$_message, + 'file|F=s' => \$_file, + %cmt_opts } ], ); my $cmd; @@ -484,11 +488,7 @@ sub commit_lib { my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : (); my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$"; - if (defined $LC_ALL) { - $ENV{LC_ALL} = $LC_ALL; - } else { - delete $ENV{LC_ALL}; - } + set_svn_commit_env(); foreach my $c (@revs) { my $log_msg = get_commit_message($c, $commit_msg); @@ -723,6 +723,55 @@ out: print '-' x72,"\n" unless $_incremental || $_oneline; } +sub commit_diff_usage { + print STDERR "Usage: $0 commit-diff []\n"; + exit 1 +} + +sub commit_diff { + if (!$_use_lib) { + print STDERR "commit-diff must be used with SVN libraries\n"; + exit 1; + } + my $ta = shift or commit_diff_usage(); + my $tb = shift or commit_diff_usage(); + if (!eval { $SVN_URL = shift || file_to_s("$GIT_SVN_DIR/info/url") }) { + print STDERR "Needed URL or usable git-svn id command-line\n"; + commit_diff_usage(); + } + if (defined $_message && defined $_file) { + print STDERR "Both --message/-m and --file/-F specified ", + "for the commit message.\n", + "I have no idea what you mean\n"; + exit 1; + } + if (defined $_file) { + $_message = file_to_s($_message); + } else { + $_message ||= get_commit_message($tb, + "$GIT_DIR/.svn-commit.tmp.$$")->{msg}; + } + my $repo; + ($repo, $SVN_PATH) = repo_path_split($SVN_URL); + $SVN_LOG ||= libsvn_connect($repo); + $SVN ||= libsvn_connect($repo); + my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : (); + my $ed = SVN::Git::Editor->new({ r => $SVN->get_latest_revnum, + ra => $SVN, c => $tb, + svn_path => $SVN_PATH + }, + $SVN->get_commit_editor($_message, + sub {print "Committed $_[0]\n"},@lock) + ); + my $mods = libsvn_checkout_tree($ta, $tb, $ed); + if (@$mods == 0) { + print "No changes\n$ta == $tb\n"; + $ed->abort_edit; + } else { + $ed->close_edit; + } +} + ########################### utility functions ######################### sub cmt_showable { @@ -1470,7 +1519,6 @@ sub get_commit_message { my %log_msg = ( msg => '' ); open my $msg, '>', $commit_msg or croak $!; - print "commit: $commit\n"; chomp(my $type = `git-cat-file -t $commit`); if ($type eq 'commit') { my $pid = open my $msg_fh, '-|'; @@ -1507,6 +1555,14 @@ sub get_commit_message { return \%log_msg; } +sub set_svn_commit_env { + if (defined $LC_ALL) { + $ENV{LC_ALL} = $LC_ALL; + } else { + delete $ENV{LC_ALL}; + } +} + sub svn_commit_tree { my ($last, $commit) = @_; my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$"; @@ -1514,11 +1570,7 @@ sub svn_commit_tree { my ($oneline) = ($log_msg->{msg} =~ /([^\n\r]+)/); print "Committing $commit: $oneline\n"; - if (defined $LC_ALL) { - $ENV{LC_ALL} = $LC_ALL; - } else { - delete $ENV{LC_ALL}; - } + set_svn_commit_env(); my @ci_output = safe_qx(qw(svn commit -F),$commit_msg); $ENV{LC_ALL} = 'C'; unlink $commit_msg; diff --git a/contrib/git-svn/t/t0005-commit-diff.sh b/contrib/git-svn/t/t0005-commit-diff.sh new file mode 100644 index 0000000000..f994b72f80 --- /dev/null +++ b/contrib/git-svn/t/t0005-commit-diff.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# +# Copyright (c) 2006 Eric Wong +test_description='git-svn commit-diff' +. ./lib-git-svn.sh + +if test -n "$GIT_SVN_NO_LIB" && test "$GIT_SVN_NO_LIB" -ne 0 +then + echo 'Skipping: commit-diff needs SVN libraries' + test_done + exit 0 +fi + +test_expect_success 'initialize repo' " + mkdir import && + cd import && + echo hello > readme && + svn import -m 'initial' . $svnrepo && + cd .. && + echo hello > readme && + git update-index --add readme && + git commit -a -m 'initial' && + echo world >> readme && + git commit -a -m 'another' + " + +head=`git rev-parse --verify HEAD^0` +prev=`git rev-parse --verify HEAD^1` + +# the internals of the commit-diff command are the same as the regular +# commit, so only a basic test of functionality is needed since we've +# already tested commit extensively elsewhere + +test_expect_success 'test the commit-diff command' " + test -n '$prev' && test -n '$head' && + git-svn commit-diff '$prev' '$head' '$svnrepo' && + svn co $svnrepo wc && + cmp readme wc/readme + " + +test_done -- cgit v1.2.3 From a00439acd209142167bf891dcf646a6501a814e5 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 27 Jun 2006 19:39:13 -0700 Subject: git-svn: add --follow-parent and --no-metadata options to fetch --follow-parent: This is especially helpful when we're tracking a directory that has been moved around within the repository, or if we started tracking a branch and never tracked the trunk it was descended from. This relies on the SVN::* libraries to work. We can't reliably parse path info from the svn command-line client without relying on XML, so it's better just to have the SVN::* libs installed. This also removes oldvalue verification when calling update-ref In SVN, branches can be deleted, and then recreated under the same path as the original one with different ancestry information, causing parent information to be mismatched / misordered. Also force the current ref, if existing, to be a parent, regardless of whether or not it was specified. --no-metadata: This gets rid of the git-svn-id: lines at the end of every commit. With this, you lose the ability to use the rebuild command. If you ever lose your .git/svn/git-svn/.rev_db file, you won't be able to fetch again, either. This is fine for one-shot imports. Also fix some issues with multi-fetch --follow-parent that were exposed while testing this. Additionally, repack checking is simplified greatly. git-svn log will not work on repositories using this, either. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 169 ++++++++++++++++++++++--------- contrib/git-svn/t/t0004-follow-parent.sh | 44 ++++++++ 2 files changed, 167 insertions(+), 46 deletions(-) create mode 100644 contrib/git-svn/t/t0004-follow-parent.sh (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 4bdc9766cd..a865a11a3e 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -19,6 +19,7 @@ my $TZ = $ENV{TZ}; # make sure the svn binary gives consistent output between locales and TZs: $ENV{TZ} = 'UTC'; $ENV{LC_ALL} = 'C'; +$| = 1; # unbuffer STDOUT # If SVN:: library support is added, please make the dependencies # optional and preserve the capability to use the command-line client. @@ -46,7 +47,7 @@ my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_find_copies_harder, $_l, $_cp_similarity, $_cp_remote, $_repack, $_repack_nr, $_repack_flags, - $_message, $_file, + $_message, $_file, $_follow_parent, $_no_metadata, $_template, $_shared, $_no_default_regex, $_no_graft_copy, $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit, $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m); @@ -56,9 +57,11 @@ my @repo_path_split_cache; my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, 'branch|b=s' => \@_branch_from, + 'follow-parent|follow' => \$_follow_parent, 'branch-all-refs|B' => \$_branch_all_refs, 'authors-file|A=s' => \$_authors, 'repack:i' => \$_repack, + 'no-metadata' => \$_no_metadata, 'repack-flags|repack-args|repack-opts=s' => \$_repack_flags); my ($_trunk, $_tags, $_branches); @@ -824,35 +827,19 @@ sub fetch_child_id { my $id = shift; print "Fetching $id\n"; my $ref = "$GIT_DIR/refs/remotes/$id"; - my $ca = file_to_s($ref) if (-r $ref); - defined(my $pid = fork) or croak $!; + defined(my $pid = open my $fh, '-|') or croak $!; if (!$pid) { + $_repack = undef; $GIT_SVN = $ENV{GIT_SVN_ID} = $id; init_vars(); fetch(@_); exit 0; } - waitpid $pid, 0; - croak $? if $?; - return unless $_repack || -r $ref; - - my $cb = file_to_s($ref); - - defined($pid = open my $fh, '-|') or croak $!; - my $url = file_to_s("$GIT_DIR/svn/$id/info/url"); - $url = qr/\Q$url\E/; - if (!$pid) { - exec qw/git-rev-list --pretty=raw/, - $ca ? "$ca..$cb" : $cb or croak $!; - } while (<$fh>) { - if (/^ git-svn-id: $url\@\d+ [a-f0-9\-]+$/) { - check_repack(); - } elsif (/^ git-svn-id: \S+\@\d+ [a-f0-9\-]+$/) { - last; - } + print $_; + check_repack() if (/^r\d+ = $sha1/); } - close $fh; + close $fh or croak $?; } sub rec_fetch { @@ -1919,6 +1906,13 @@ sub git_commit { croak $? if $?; restore_index($index); } + + # just in case we clobber the existing ref, we still want that ref + # as our parent: + if (my $cur = eval { file_to_s("$GIT_DIR/refs/remotes/$GIT_SVN") }) { + push @tmp_parents, $cur; + } + if (exists $tree_map{$tree}) { foreach my $p (@{$tree_map{$tree}}) { my $skip; @@ -1949,31 +1943,26 @@ sub git_commit { last if @exec_parents > 16; } - defined(my $pid = open my $out_fh, '-|') or croak $!; - if ($pid == 0) { - my $msg_fh = IO::File->new_tmpfile or croak $!; - print $msg_fh $log_msg->{msg}, "\ngit-svn-id: ", - "$SVN_URL\@$log_msg->{revision}", + set_commit_env($log_msg); + my @exec = ('git-commit-tree', $tree); + push @exec, '-p', $_ foreach @exec_parents; + defined(my $pid = open3(my $msg_fh, my $out_fh, '>&STDERR', @exec)) + or croak $!; + print $msg_fh $log_msg->{msg} or croak $!; + unless ($_no_metadata) { + print $msg_fh "\ngit-svn-id: $SVN_URL\@$log_msg->{revision}", " $SVN_UUID\n" or croak $!; - $msg_fh->flush == 0 or croak $!; - seek $msg_fh, 0, 0 or croak $!; - set_commit_env($log_msg); - my @exec = ('git-commit-tree',$tree); - push @exec, '-p', $_ foreach @exec_parents; - open STDIN, '<&', $msg_fh or croak $!; - exec @exec or croak $!; } + $msg_fh->flush == 0 or croak $!; + close $msg_fh or croak $!; chomp(my $commit = do { local $/; <$out_fh> }); - close $out_fh or croak $?; + close $out_fh or croak $!; + waitpid $pid, 0; + croak $? if $?; if ($commit !~ /^$sha1$/o) { - croak "Failed to commit, invalid sha1: $commit\n"; + die "Failed to commit, invalid sha1: $commit\n"; } - my @update_ref = ('git-update-ref',"refs/remotes/$GIT_SVN",$commit); - if (my $primary_parent = shift @exec_parents) { - quiet_run(qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0"); - push @update_ref, $primary_parent unless $?; - } - sys(@update_ref); + sys('git-update-ref',"refs/remotes/$GIT_SVN",$commit); revdb_set($REVDB, $log_msg->{revision}, $commit); # this output is read via pipe, do not change: @@ -2058,6 +2047,11 @@ sub safe_qx { } sub svn_compat_check { + if ($_follow_parent) { + print STDERR 'E: --follow-parent functionality is only ', + "available when SVN libraries are used\n"; + exit 1; + } my @co_help = safe_qx(qw(svn co -h)); unless (grep /ignore-externals/,@co_help) { print STDERR "W: Installed svn version does not support ", @@ -2386,6 +2380,28 @@ sub write_grafts { close $fh or croak $!; } +sub read_url_paths_all { + my ($l_map, $pfx, $p) = @_; + my @dir; + foreach (<$p/*>) { + if (-r "$_/info/url") { + $pfx .= '/' if $pfx && $pfx !~ m!/$!; + my $id = $pfx . basename $_; + my $url = file_to_s("$_/info/url"); + my ($u, $p) = repo_path_split($url); + $l_map->{$u}->{$p} = $id; + } elsif (-d $_) { + push @dir, $_; + } + } + foreach (@dir) { + my $x = $_; + $x =~ s!^\Q$GIT_DIR\E/svn/!!o; + read_url_paths_all($l_map, $x, $_); + } +} + +# this one only gets ids that have been imported, not new ones sub read_url_paths { my $l_map = {}; git_svn_each(sub { my $x = shift; @@ -2599,7 +2615,6 @@ sub libsvn_get_file { # redirect STDOUT for SVN 1.1.x compatibility open my $stdout, '>&', \*STDOUT or croak $!; open STDOUT, '>&', $in or croak $!; - $| = 1; # not sure if this is necessary, better safe than sorry... my ($r, $props) = $SVN->get_file($f, $rev, \*STDOUT, $pool); $in->flush == 0 or croak $!; open STDOUT, '>&', $stdout or croak $!; @@ -2702,6 +2717,28 @@ sub svn_grab_base_rev { close $fh; if (defined $c && length $c) { my ($url, $rev, $uuid) = cmt_metadata($c); + return ($rev, $c) if defined $rev; + } + if ($_no_metadata) { + my $offset = -41; # from tail + my $rl; + open my $fh, '<', $REVDB or + die "--no-metadata specified and $REVDB not readable\n"; + seek $fh, $offset, 2; + $rl = readline $fh; + defined $rl or return (undef, undef); + chomp $rl; + while ($c ne $rl && tell $fh != 0) { + $offset -= 41; + seek $fh, $offset, 2; + $rl = readline $fh; + defined $rl or return (undef, undef); + chomp $rl; + } + my $rev = tell $fh; + croak $! if ($rev < -1); + $rev = ($rev - 41) / 41; + close $fh or croak $!; return ($rev, $c); } return (undef, undef); @@ -2799,15 +2836,45 @@ sub libsvn_find_parent_branch { print STDERR "Found possible branch point: ", "$branch_from => $svn_path, $r\n"; $branch_from =~ s#^/##; - my $l_map = read_url_paths(); + my $l_map = {}; + read_url_paths_all($l_map, '', "$GIT_DIR/svn"); my $url = $SVN->{url}; defined $l_map->{$url} or return; - my $id = $l_map->{$url}->{$branch_from} or return; + my $id = $l_map->{$url}->{$branch_from}; + if (!defined $id && $_follow_parent) { + print STDERR "Following parent: $branch_from\@$r\n"; + # auto create a new branch and follow it + $id = basename($branch_from); + $id .= '@'.$r if -r "$GIT_DIR/svn/$id"; + while (-r "$GIT_DIR/svn/$id") { + # just grow a tail if we're not unique enough :x + $id .= '-'; + } + } + return unless defined $id; + my ($r0, $parent) = find_rev_before($r,$id,1); + if ($_follow_parent && (!defined $r0 || !defined $parent)) { + defined(my $pid = fork) or croak $!; + if (!$pid) { + $GIT_SVN = $ENV{GIT_SVN_ID} = $id; + init_vars(); + $SVN_URL = "$url/$branch_from"; + $SVN_LOG = $SVN = undef; + setup_git_svn(); + # we can't assume SVN_URL exists at r+1: + $_revision = "0:$r"; + fetch_lib(); + exit 0; + } + waitpid $pid, 0; + croak $? if $?; + ($r0, $parent) = find_rev_before($r,$id,1); + } return unless (defined $r0 && defined $parent); if (revisions_eq($branch_from, $r0, $r)) { unlink $GIT_SVN_INDEX; - print STDERR "Found branch parent: $parent\n"; + print STDERR "Found branch parent: ($GIT_SVN) $parent\n"; sys(qw/git-read-tree/, $parent); return libsvn_fetch($parent, $paths, $rev, $author, $date, $msg); @@ -3274,6 +3341,16 @@ diff-index line ($m hash) } ; +# retval of read_url_paths{,_all}(); +$l_map = { + # repository root url + 'https://svn.musicpd.org' => { + # repository path # GIT_SVN_ID + 'mpd/trunk' => 'trunk', + 'mpd/tags/0.11.5' => 'tags/0.11.5', + }, +} + Notes: I don't trust the each() function on unless I created %hash myself because the internal iterator may not have started at base. diff --git a/contrib/git-svn/t/t0004-follow-parent.sh b/contrib/git-svn/t/t0004-follow-parent.sh new file mode 100644 index 0000000000..01488ff78a --- /dev/null +++ b/contrib/git-svn/t/t0004-follow-parent.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# +# Copyright (c) 2006 Eric Wong +# + +test_description='git-svn --follow-parent fetching' +. ./lib-git-svn.sh + +if test -n "$GIT_SVN_NO_LIB" && test "$GIT_SVN_NO_LIB" -ne 0 +then + echo 'Skipping: --follow-parent needs SVN libraries' + test_done + exit 0 +fi + +test_expect_success 'initialize repo' " + mkdir import && + cd import && + mkdir -p trunk && + echo hello > trunk/readme && + svn import -m 'initial' . $svnrepo && + cd .. && + svn co $svnrepo wc && + cd wc && + echo world >> trunk/readme && + svn commit -m 'another commit' && + svn up && + svn mv -m 'rename to thunk' trunk thunk && + svn up && + echo goodbye >> thunk/readme && + svn commit -m 'bye now' && + cd .. + " + +test_expect_success 'init and fetch --follow-parent a moved directory' " + git-svn init -i thunk $svnrepo/thunk && + git-svn fetch --follow-parent -i thunk && + git-rev-parse --verify refs/remotes/trunk && + test '$?' -eq '0' + " + +test_debug 'gitk --all &' + +test_done -- cgit v1.2.3 From 80f50749da3441502aabd5b6c5129c9eb3bdf870 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 27 Jun 2006 19:39:14 -0700 Subject: git-svn: be verbose by default on fetch/commit, add -q/--quiet option Slower connections can make git-svn look as if it's doing nothing for a long time; leaving the user wondering if we're actually doing anything. Now we print some file progress just to assure the user that something is going on while they're waiting. Added the -q/--quiet option to users to revert to the old method if they preferred it. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index a865a11a3e..b3d3f479da 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -46,7 +46,7 @@ my $sha1 = qr/[a-f\d]{40}/; my $sha1_short = qr/[a-f\d]{4,40}/; my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, $_find_copies_harder, $_l, $_cp_similarity, $_cp_remote, - $_repack, $_repack_nr, $_repack_flags, + $_repack, $_repack_nr, $_repack_flags, $_q, $_message, $_file, $_follow_parent, $_no_metadata, $_template, $_shared, $_no_default_regex, $_no_graft_copy, $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit, @@ -62,6 +62,7 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, 'authors-file|A=s' => \$_authors, 'repack:i' => \$_repack, 'no-metadata' => \$_no_metadata, + 'quiet|q' => \$_q, 'repack-flags|repack-args|repack-opts=s' => \$_repack_flags); my ($_trunk, $_tags, $_branches); @@ -1454,12 +1455,12 @@ sub libsvn_checkout_tree { foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) { my $f = $m->{chg}; if (defined $o{$f}) { - $ed->$f($m); + $ed->$f($m, $_q); } else { croak "Invalid change type: $f\n"; } } - $ed->rmdirs if $_rmdir; + $ed->rmdirs($_q) if $_rmdir; return $mods; } @@ -2685,6 +2686,7 @@ sub libsvn_fetch { my $m = $paths->{$f}->action(); $f =~ s#^/+##; if ($m =~ /^[DR]$/) { + print "\t$m\t$f\n" unless $_q; process_rm($gui, $last_commit, $f); next if $m eq 'D'; # 'R' can be file replacements, too, right? @@ -2693,14 +2695,17 @@ sub libsvn_fetch { my $t = $SVN->check_path($f, $rev, $pool); if ($t == $SVN::Node::file) { if ($m =~ /^[AMR]$/) { - push @amr, $f; + push @amr, [ $m, $f ]; } else { die "Unrecognized action: $m, ($f r$rev)\n"; } } $pool->clear; } - libsvn_get_file($gui, $_, $rev) foreach (@amr); + foreach (@amr) { + print "\t$_->[0]\t$_->[1]\n" unless $_q; + libsvn_get_file($gui, $_->[1], $rev) + } close $gui or croak $?; return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]); } @@ -2773,6 +2778,7 @@ sub libsvn_traverse { if ($t == $SVN::Node::dir) { libsvn_traverse($gui, $cwd, $d, $rev); } elsif ($t == $SVN::Node::file) { + print "\tA\t$cwd/$d\n" unless $_q; libsvn_get_file($gui, "$cwd/$d", $rev); } } @@ -3109,7 +3115,7 @@ sub url_path { } sub rmdirs { - my ($self) = @_; + my ($self, $q) = @_; my $rm = $self->{rm}; delete $rm->{''}; # we never delete the url we're tracking return unless %$rm; @@ -3150,6 +3156,7 @@ sub rmdirs { foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) { $self->close_directory($bat->{$d}, $p); my ($dn) = ($d =~ m#^(.*?)/?(?:[^/]+)$#); + print "\tD+\t/$d/\n" unless $q; $self->SUPER::delete_entry($d, $r, $bat->{$dn}, $p); delete $bat->{$d}; } @@ -3190,21 +3197,23 @@ sub ensure_path { } sub A { - my ($self, $m) = @_; + my ($self, $m, $q) = @_; my ($dir, $file) = split_path($m->{file_b}); my $pbat = $self->ensure_path($dir); my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, undef, -1); + print "\tA\t$m->{file_b}\n" unless $q; $self->chg_file($fbat, $m); $self->close_file($fbat,undef,$self->{pool}); } sub C { - my ($self, $m) = @_; + my ($self, $m, $q) = @_; my ($dir, $file) = split_path($m->{file_b}); my $pbat = $self->ensure_path($dir); my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, $self->url_path($m->{file_a}), $self->{r}); + print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $q; $self->chg_file($fbat, $m); $self->close_file($fbat,undef,$self->{pool}); } @@ -3218,11 +3227,12 @@ sub delete_entry { } sub R { - my ($self, $m) = @_; + my ($self, $m, $q) = @_; my ($dir, $file) = split_path($m->{file_b}); my $pbat = $self->ensure_path($dir); my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, $self->url_path($m->{file_a}), $self->{r}); + print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $q; $self->chg_file($fbat, $m); $self->close_file($fbat,undef,$self->{pool}); @@ -3232,11 +3242,12 @@ sub R { } sub M { - my ($self, $m) = @_; + my ($self, $m, $q) = @_; my ($dir, $file) = split_path($m->{file_b}); my $pbat = $self->ensure_path($dir); my $fbat = $self->open_file($self->repo_path($m->{file_b}), $pbat,$self->{r},$self->{pool}); + print "\t$m->{chg}\t$m->{file_b}\n" unless $q; $self->chg_file($fbat, $m); $self->close_file($fbat,undef,$self->{pool}); } @@ -3285,9 +3296,10 @@ sub chg_file { } sub D { - my ($self, $m) = @_; + my ($self, $m, $q) = @_; my ($dir, $file) = split_path($m->{file_b}); my $pbat = $self->ensure_path($dir); + print "\tD\t$m->{file_b}\n" unless $q; $self->delete_entry($m->{file_b}, $pbat); } -- cgit v1.2.3 From e14421b9aa85f11853a0dacae09498515daab7b8 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 29 Jun 2006 22:11:25 +0200 Subject: Allow INSTALL, bindir, mandir to be set in main Makefile Makefiles in subdirectories now use existing value of INSTALL, bindir, mandir if it is set, allowing those to be set in main Makefile or in included config.mak. Main Makefile exports variables which it sets. Accidentally it renames bin to bindir in Documentation/Makefile (should be bindir from start, but is unused, perhaps to be removed). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- contrib/emacs/Makefile | 4 ++-- contrib/git-svn/Makefile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile index d3619db510..350846de90 100644 --- a/contrib/emacs/Makefile +++ b/contrib/emacs/Makefile @@ -3,9 +3,9 @@ EMACS = emacs ELC = git.elc vc-git.elc -INSTALL = install +INSTALL ?= install INSTALL_ELC = $(INSTALL) -m 644 -prefix = $(HOME) +prefix ?= $(HOME) emacsdir = $(prefix)/share/emacs/site-lisp all: $(ELC) diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile index 7c20946943..1a6585eeec 100644 --- a/contrib/git-svn/Makefile +++ b/contrib/git-svn/Makefile @@ -1,8 +1,8 @@ all: git-svn prefix?=$(HOME) -bindir=$(prefix)/bin -mandir=$(prefix)/man +bindir?=$(prefix)/bin +mandir?=$(prefix)/man man1=$(mandir)/man1 INSTALL?=install doc_conf=../../Documentation/asciidoc.conf -- cgit v1.2.3 From 7b8cf0cf2973cc8df3bdd36b9b36542b1f04d70a Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 29 Jun 2006 23:26:54 +0200 Subject: Rename man1 and man7 variables to man1dir and man7dir This patch renames man1 and man7 variables to man1dir and man7dir, according to "Makefile Conventions: Variables for Installation Directories" in make.info of GNU Make. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- contrib/git-svn/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile index 1a6585eeec..8cac68873b 100644 --- a/contrib/git-svn/Makefile +++ b/contrib/git-svn/Makefile @@ -3,7 +3,7 @@ all: git-svn prefix?=$(HOME) bindir?=$(prefix)/bin mandir?=$(prefix)/man -man1=$(mandir)/man1 +man1dir=$(mandir)/man1 INSTALL?=install doc_conf=../../Documentation/asciidoc.conf -include ../../config.mak @@ -17,7 +17,7 @@ install: all $(INSTALL) git-svn $(DESTDIR)$(bindir) install-doc: doc - $(INSTALL) git-svn.1 $(DESTDIR)$(man1) + $(INSTALL) git-svn.1 $(DESTDIR)$(man1dir) doc: git-svn.1 git-svn.1 : git-svn.xml -- cgit v1.2.3 From 03e0ea871206e50bcd1c5167c6fc9a41c6642abb Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 30 Jun 2006 21:42:53 -0700 Subject: git-svn: allow a local target directory to be specified for init git-svn init url://to/the/repo local-repo will create the local-repo dirrectory if doesn't exist yet and populate it as expected. Original patch by Luca Barbato, cleaned up and made to work for the current version of git-svn by me (Eric Wong). Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/git-svn.perl | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index b3d3f479da..1e19aa19b2 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -264,9 +264,19 @@ when you have upgraded your tools and habits to use refs/remotes/$GIT_SVN } sub init { - $SVN_URL = shift or die "SVN repository location required " . + my $url = shift or die "SVN repository location required " . "as a command-line argument\n"; - $SVN_URL =~ s!/+$!!; # strip trailing slash + $url =~ s!/+$!!; # strip trailing slash + + if (my $repo_path = shift) { + unless (-d $repo_path) { + mkpath([$repo_path]); + } + $GIT_DIR = $ENV{GIT_DIR} = $repo_path . "/.git"; + init_vars(); + } + + $SVN_URL = $url; unless (-d $GIT_DIR) { my @init_db = ('git-init-db'); push @init_db, "--template=$_template" if defined $_template; -- cgit v1.2.3 From 4bbf599f7bb6bd6debb3624b27815f366b7b34a1 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 5 Jul 2006 05:14:00 -0700 Subject: git-svn: avoid fetching files outside of the URL we're tracking Thanks to Santi for the bug report and explanation: > /path/to/repository/project/file > /path/to/repository/project-2/file <...> > you end up with a project with the following files: > > file > -2/file Signed-off-by: Eric Wong --- contrib/git-svn/git-svn.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 1e19aa19b2..8bc4188e03 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -2617,7 +2617,7 @@ sub libsvn_connect { sub libsvn_get_file { my ($gui, $f, $rev) = @_; my $p = $f; - return unless ($p =~ s#^\Q$SVN_PATH\E/?##); + return unless ($p =~ s#^\Q$SVN_PATH\E/##); my ($hash, $pid, $in, $out); my $pool = SVN::Pool->new; -- cgit v1.2.3 From b5dd9d2027c1bd5758033c7baf6d087752b0263d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=1B=2CAd=1B=28B?= Date: Wed, 5 Jul 2006 01:35:52 +0300 Subject: Fix print-log and diff compatibility with recent vc versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Here's a patch that fixes print-log and diff compatibility with recent vc versions, such as current GNU Emacs CVS. Signed-off-by: Ville Skytt,Ad(B --- contrib/emacs/vc-git.el | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index 2453cdcfae..3f6ed699f0 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -95,16 +95,17 @@ "Register FILE into the git version-control system." (vc-git--run-command file "update-index" "--add" "--")) -(defun vc-git-print-log (file) +(defun vc-git-print-log (file &optional buffer) (let ((name (file-relative-name file)) (coding-system-for-read git-commits-coding-system)) - (vc-do-command nil 'async "git" name "rev-list" "--pretty" "HEAD" "--"))) + (vc-do-command buffer 'async "git" name "rev-list" "--pretty" "HEAD" "--"))) -(defun vc-git-diff (file &optional rev1 rev2) - (let ((name (file-relative-name file))) +(defun vc-git-diff (file &optional rev1 rev2 buffer) + (let ((name (file-relative-name file)) + (buf (or buffer "*vc-diff*"))) (if (and rev1 rev2) - (vc-do-command "*vc-diff*" 0 "git" name "diff-tree" "-p" rev1 rev2 "--") - (vc-do-command "*vc-diff*" 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--")) + (vc-do-command buf 0 "git" name "diff-tree" "-p" rev1 rev2 "--") + (vc-do-command buf 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--")) ; git-diff-index doesn't set exit status like diff does (if (vc-git-workfile-unchanged-p file) 0 1))) -- cgit v1.2.3 From 60d02ccc18408e54ace8692532fcc73d4035b3c2 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 6 Jul 2006 00:14:16 -0700 Subject: git-svn: migrate out of contrib Allow NO_SVN_TESTS to be defined to skip git-svn tests. These tests are time-consuming due to SVN being slow, and even more so if SVN Perl libraries are not available. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/git-svn/.gitignore | 4 - contrib/git-svn/Makefile | 44 - contrib/git-svn/git-svn.perl | 3378 ---------------------- contrib/git-svn/git-svn.txt | 319 -- contrib/git-svn/t/lib-git-svn.sh | 45 - contrib/git-svn/t/t0000-contrib-git-svn.sh | 232 -- contrib/git-svn/t/t0001-contrib-git-svn-props.sh | 126 - contrib/git-svn/t/t0002-deep-rmdir.sh | 29 - contrib/git-svn/t/t0003-graft-branches.sh | 63 - contrib/git-svn/t/t0004-follow-parent.sh | 44 - contrib/git-svn/t/t0005-commit-diff.sh | 41 - 11 files changed, 4325 deletions(-) delete mode 100644 contrib/git-svn/.gitignore delete mode 100644 contrib/git-svn/Makefile delete mode 100755 contrib/git-svn/git-svn.perl delete mode 100644 contrib/git-svn/git-svn.txt delete mode 100644 contrib/git-svn/t/lib-git-svn.sh delete mode 100644 contrib/git-svn/t/t0000-contrib-git-svn.sh delete mode 100644 contrib/git-svn/t/t0001-contrib-git-svn-props.sh delete mode 100644 contrib/git-svn/t/t0002-deep-rmdir.sh delete mode 100644 contrib/git-svn/t/t0003-graft-branches.sh delete mode 100644 contrib/git-svn/t/t0004-follow-parent.sh delete mode 100644 contrib/git-svn/t/t0005-commit-diff.sh (limited to 'contrib') diff --git a/contrib/git-svn/.gitignore b/contrib/git-svn/.gitignore deleted file mode 100644 index d8d87e3af9..0000000000 --- a/contrib/git-svn/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -git-svn -git-svn.xml -git-svn.html -git-svn.1 diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile deleted file mode 100644 index 7c20946943..0000000000 --- a/contrib/git-svn/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -all: git-svn - -prefix?=$(HOME) -bindir=$(prefix)/bin -mandir=$(prefix)/man -man1=$(mandir)/man1 -INSTALL?=install -doc_conf=../../Documentation/asciidoc.conf --include ../../config.mak - -git-svn: git-svn.perl - cp $< $@ - chmod +x $@ - -install: all - $(INSTALL) -d -m755 $(DESTDIR)$(bindir) - $(INSTALL) git-svn $(DESTDIR)$(bindir) - -install-doc: doc - $(INSTALL) git-svn.1 $(DESTDIR)$(man1) - -doc: git-svn.1 -git-svn.1 : git-svn.xml - xmlto man git-svn.xml -git-svn.xml : git-svn.txt - asciidoc -b docbook -d manpage \ - -f ../../Documentation/asciidoc.conf $< -git-svn.html : git-svn.txt - asciidoc -b xhtml11 -d manpage \ - -f ../../Documentation/asciidoc.conf $< -test: git-svn - cd t && for i in t????-*.sh; do $(SHELL) ./$$i $(TEST_FLAGS); done - -# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL -full-test: - $(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C - $(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C - $(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \ - LC_ALL=en_US.UTF-8 - $(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \ - LC_ALL=en_US.UTF-8 - -clean: - rm -f git-svn *.xml *.html *.1 diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl deleted file mode 100755 index 8bc4188e03..0000000000 --- a/contrib/git-svn/git-svn.perl +++ /dev/null @@ -1,3378 +0,0 @@ -#!/usr/bin/env perl -# Copyright (C) 2006, Eric Wong -# License: GPL v2 or later -use warnings; -use strict; -use vars qw/ $AUTHOR $VERSION - $SVN_URL $SVN_INFO $SVN_WC $SVN_UUID - $GIT_SVN_INDEX $GIT_SVN - $GIT_DIR $GIT_SVN_DIR $REVDB/; -$AUTHOR = 'Eric Wong '; -$VERSION = '1.1.1-broken'; - -use Cwd qw/abs_path/; -$GIT_DIR = abs_path($ENV{GIT_DIR} || '.git'); -$ENV{GIT_DIR} = $GIT_DIR; - -my $LC_ALL = $ENV{LC_ALL}; -my $TZ = $ENV{TZ}; -# make sure the svn binary gives consistent output between locales and TZs: -$ENV{TZ} = 'UTC'; -$ENV{LC_ALL} = 'C'; -$| = 1; # unbuffer STDOUT - -# If SVN:: library support is added, please make the dependencies -# optional and preserve the capability to use the command-line client. -# use eval { require SVN::... } to make it lazy load -# We don't use any modules not in the standard Perl distribution: -use Carp qw/croak/; -use IO::File qw//; -use File::Basename qw/dirname basename/; -use File::Path qw/mkpath/; -use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/; -use File::Spec qw//; -use POSIX qw/strftime/; -use IPC::Open3; -use Memoize; -memoize('revisions_eq'); -memoize('cmt_metadata'); -memoize('get_commit_time'); - -my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib); -$_use_lib = 1 unless $ENV{GIT_SVN_NO_LIB}; -libsvn_load(); -my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS}; -my $sha1 = qr/[a-f\d]{40}/; -my $sha1_short = qr/[a-f\d]{4,40}/; -my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit, - $_find_copies_harder, $_l, $_cp_similarity, $_cp_remote, - $_repack, $_repack_nr, $_repack_flags, $_q, - $_message, $_file, $_follow_parent, $_no_metadata, - $_template, $_shared, $_no_default_regex, $_no_graft_copy, - $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit, - $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m); -my (@_branch_from, %tree_map, %users, %rusers, %equiv); -my ($_svn_co_url_revs, $_svn_pg_peg_revs); -my @repo_path_split_cache; - -my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, - 'branch|b=s' => \@_branch_from, - 'follow-parent|follow' => \$_follow_parent, - 'branch-all-refs|B' => \$_branch_all_refs, - 'authors-file|A=s' => \$_authors, - 'repack:i' => \$_repack, - 'no-metadata' => \$_no_metadata, - 'quiet|q' => \$_q, - 'repack-flags|repack-args|repack-opts=s' => \$_repack_flags); - -my ($_trunk, $_tags, $_branches); -my %multi_opts = ( 'trunk|T=s' => \$_trunk, - 'tags|t=s' => \$_tags, - 'branches|b=s' => \$_branches ); -my %init_opts = ( 'template=s' => \$_template, 'shared' => \$_shared ); -my %cmt_opts = ( 'edit|e' => \$_edit, - 'rmdir' => \$_rmdir, - 'find-copies-harder' => \$_find_copies_harder, - 'l=i' => \$_l, - 'copy-similarity|C=i'=> \$_cp_similarity -); - -# yes, 'native' sets "\n". Patches to fix this for non-*nix systems welcome: -my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" ); - -my %cmd = ( - fetch => [ \&fetch, "Download new revisions from SVN", - { 'revision|r=s' => \$_revision, %fc_opts } ], - init => [ \&init, "Initialize a repo for tracking" . - " (requires URL argument)", - \%init_opts ], - commit => [ \&commit, "Commit git revisions to SVN", - { 'stdin|' => \$_stdin, %cmt_opts, %fc_opts, } ], - 'show-ignore' => [ \&show_ignore, "Show svn:ignore listings", - { 'revision|r=i' => \$_revision } ], - rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)", - { 'no-ignore-externals' => \$_no_ignore_ext, - 'copy-remote|remote=s' => \$_cp_remote, - 'upgrade' => \$_upgrade } ], - 'graft-branches' => [ \&graft_branches, - 'Detect merges/branches from already imported history', - { 'merge-rx|m' => \@_opt_m, - 'branch|b=s' => \@_branch_from, - 'branch-all-refs|B' => \$_branch_all_refs, - 'no-default-regex' => \$_no_default_regex, - 'no-graft-copy' => \$_no_graft_copy } ], - 'multi-init' => [ \&multi_init, - 'Initialize multiple trees (like git-svnimport)', - { %multi_opts, %fc_opts } ], - 'multi-fetch' => [ \&multi_fetch, - 'Fetch multiple trees (like git-svnimport)', - \%fc_opts ], - 'log' => [ \&show_log, 'Show commit logs', - { 'limit=i' => \$_limit, - 'revision|r=s' => \$_revision, - 'verbose|v' => \$_verbose, - 'incremental' => \$_incremental, - 'oneline' => \$_oneline, - 'show-commit' => \$_show_commit, - 'authors-file|A=s' => \$_authors, - } ], - 'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees', - { 'message|m=s' => \$_message, - 'file|F=s' => \$_file, - %cmt_opts } ], -); - -my $cmd; -for (my $i = 0; $i < @ARGV; $i++) { - if (defined $cmd{$ARGV[$i]}) { - $cmd = $ARGV[$i]; - splice @ARGV, $i, 1; - last; - } -}; - -my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd); - -read_repo_config(\%opts); -my $rv = GetOptions(%opts, 'help|H|h' => \$_help, - 'version|V' => \$_version, - 'id|i=s' => \$GIT_SVN); -exit 1 if (!$rv && $cmd ne 'log'); - -set_default_vals(); -usage(0) if $_help; -version() if $_version; -usage(1) unless defined $cmd; -init_vars(); -load_authors() if $_authors; -load_all_refs() if $_branch_all_refs; -svn_compat_check() unless $_use_lib; -migration_check() unless $cmd =~ /^(?:init|rebuild|multi-init)$/; -$cmd{$cmd}->[0]->(@ARGV); -exit 0; - -####################### primary functions ###################### -sub usage { - my $exit = shift || 0; - my $fd = $exit ? \*STDERR : \*STDOUT; - print $fd <<""; -git-svn - bidirectional operations between a single Subversion tree and git -Usage: $0 [options] [arguments]\n - - print $fd "Available commands:\n" unless $cmd; - - foreach (sort keys %cmd) { - next if $cmd && $cmd ne $_; - print $fd ' ',pack('A13',$_),$cmd{$_}->[1],"\n"; - foreach (keys %{$cmd{$_}->[2]}) { - # prints out arguments as they should be passed: - my $x = s#[:=]s$## ? '' : s#[:=]i$## ? '' : ''; - print $fd ' ' x 17, join(', ', map { length $_ > 1 ? - "--$_" : "-$_" } - split /\|/,$_)," $x\n"; - } - } - print $fd <<""; -\nGIT_SVN_ID may be set in the environment or via the --id/-i switch to an -arbitrary identifier if you're tracking multiple SVN branches/repositories in -one git repository and want to keep them separate. See git-svn(1) for more -information. - - exit $exit; -} - -sub version { - print "git-svn version $VERSION\n"; - exit 0; -} - -sub rebuild { - if (quiet_run(qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0")) { - copy_remote_ref(); - } - $SVN_URL = shift or undef; - my $newest_rev = 0; - if ($_upgrade) { - sys('git-update-ref',"refs/remotes/$GIT_SVN","$GIT_SVN-HEAD"); - } else { - check_upgrade_needed(); - } - - my $pid = open(my $rev_list,'-|'); - defined $pid or croak $!; - if ($pid == 0) { - exec("git-rev-list","refs/remotes/$GIT_SVN") or croak $!; - } - my $latest; - while (<$rev_list>) { - chomp; - my $c = $_; - croak "Non-SHA1: $c\n" unless $c =~ /^$sha1$/o; - my @commit = grep(/^git-svn-id: /,`git-cat-file commit $c`); - next if (!@commit); # skip merges - my ($url, $rev, $uuid) = extract_metadata($commit[$#commit]); - if (!$rev || !$uuid) { - croak "Unable to extract revision or UUID from ", - "$c, $commit[$#commit]\n"; - } - - # if we merged or otherwise started elsewhere, this is - # how we break out of it - next if (defined $SVN_UUID && ($uuid ne $SVN_UUID)); - next if (defined $SVN_URL && defined $url && ($url ne $SVN_URL)); - - unless (defined $latest) { - if (!$SVN_URL && !$url) { - croak "SVN repository location required: $url\n"; - } - $SVN_URL ||= $url; - $SVN_UUID ||= $uuid; - setup_git_svn(); - $latest = $rev; - } - revdb_set($REVDB, $rev, $c); - print "r$rev = $c\n"; - $newest_rev = $rev if ($rev > $newest_rev); - } - close $rev_list or croak $?; - - goto out if $_use_lib; - if (!chdir $SVN_WC) { - svn_cmd_checkout($SVN_URL, $latest, $SVN_WC); - chdir $SVN_WC or croak $!; - } - - $pid = fork; - defined $pid or croak $!; - if ($pid == 0) { - my @svn_up = qw(svn up); - push @svn_up, '--ignore-externals' unless $_no_ignore_ext; - sys(@svn_up,"-r$newest_rev"); - $ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX; - index_changes(); - exec('git-write-tree') or croak $!; - } - waitpid $pid, 0; - croak $? if $?; -out: - if ($_upgrade) { - print STDERR <<""; -Keeping deprecated refs/head/$GIT_SVN-HEAD for now. Please remove it -when you have upgraded your tools and habits to use refs/remotes/$GIT_SVN - - } -} - -sub init { - my $url = shift or die "SVN repository location required " . - "as a command-line argument\n"; - $url =~ s!/+$!!; # strip trailing slash - - if (my $repo_path = shift) { - unless (-d $repo_path) { - mkpath([$repo_path]); - } - $GIT_DIR = $ENV{GIT_DIR} = $repo_path . "/.git"; - init_vars(); - } - - $SVN_URL = $url; - unless (-d $GIT_DIR) { - my @init_db = ('git-init-db'); - push @init_db, "--template=$_template" if defined $_template; - push @init_db, "--shared" if defined $_shared; - sys(@init_db); - } - setup_git_svn(); -} - -sub fetch { - check_upgrade_needed(); - $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url"); - my $ret = $_use_lib ? fetch_lib(@_) : fetch_cmd(@_); - if ($ret->{commit} && quiet_run(qw(git-rev-parse --verify - refs/heads/master^0))) { - sys(qw(git-update-ref refs/heads/master),$ret->{commit}); - } - return $ret; -} - -sub fetch_cmd { - my (@parents) = @_; - my @log_args = -d $SVN_WC ? ($SVN_WC) : ($SVN_URL); - unless ($_revision) { - $_revision = -d $SVN_WC ? 'BASE:HEAD' : '0:HEAD'; - } - push @log_args, "-r$_revision"; - push @log_args, '--stop-on-copy' unless $_no_stop_copy; - - my $svn_log = svn_log_raw(@log_args); - - my $base = next_log_entry($svn_log) or croak "No base revision!\n"; - # don't need last_revision from grab_base_rev() because - # user could've specified a different revision to skip (they - # didn't want to import certain revisions into git for whatever - # reason, so trust $base->{revision} instead. - my (undef, $last_commit) = svn_grab_base_rev(); - unless (-d $SVN_WC) { - svn_cmd_checkout($SVN_URL,$base->{revision},$SVN_WC); - chdir $SVN_WC or croak $!; - read_uuid(); - $last_commit = git_commit($base, @parents); - assert_tree($last_commit); - } else { - chdir $SVN_WC or croak $!; - read_uuid(); - # looks like a user manually cp'd and svn switch'ed - unless ($last_commit) { - sys(qw/svn revert -R ./); - assert_svn_wc_clean($base->{revision}); - $last_commit = git_commit($base, @parents); - assert_tree($last_commit); - } - } - my @svn_up = qw(svn up); - push @svn_up, '--ignore-externals' unless $_no_ignore_ext; - my $last = $base; - while (my $log_msg = next_log_entry($svn_log)) { - if ($last->{revision} >= $log_msg->{revision}) { - croak "Out of order: last >= current: ", - "$last->{revision} >= $log_msg->{revision}\n"; - } - # Revert is needed for cases like: - # https://svn.musicpd.org/Jamming/trunk (r166:167), but - # I can't seem to reproduce something like that on a test... - sys(qw/svn revert -R ./); - assert_svn_wc_clean($last->{revision}); - sys(@svn_up,"-r$log_msg->{revision}"); - $last_commit = git_commit($log_msg, $last_commit, @parents); - $last = $log_msg; - } - close $svn_log->{fh}; - $last->{commit} = $last_commit; - return $last; -} - -sub fetch_lib { - my (@parents) = @_; - $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url"); - my $repo; - ($repo, $SVN_PATH) = repo_path_split($SVN_URL); - $SVN_LOG ||= libsvn_connect($repo); - $SVN ||= libsvn_connect($repo); - my ($last_rev, $last_commit) = svn_grab_base_rev(); - my ($base, $head) = libsvn_parse_revision($last_rev); - if ($base > $head) { - return { revision => $last_rev, commit => $last_commit } - } - my $index = set_index($GIT_SVN_INDEX); - - # limit ourselves and also fork() since get_log won't release memory - # after processing a revision and SVN stuff seems to leak - my $inc = 1000; - my ($min, $max) = ($base, $head < $base+$inc ? $head : $base+$inc); - read_uuid(); - if (defined $last_commit) { - unless (-e $GIT_SVN_INDEX) { - sys(qw/git-read-tree/, $last_commit); - } - chomp (my $x = `git-write-tree`); - my ($y) = (`git-cat-file commit $last_commit` - =~ /^tree ($sha1)/m); - if ($y ne $x) { - unlink $GIT_SVN_INDEX or croak $!; - sys(qw/git-read-tree/, $last_commit); - } - chomp ($x = `git-write-tree`); - if ($y ne $x) { - print STDERR "trees ($last_commit) $y != $x\n", - "Something is seriously wrong...\n"; - } - } - while (1) { - # fork, because using SVN::Pool with get_log() still doesn't - # seem to help enough to keep memory usage down. - defined(my $pid = fork) or croak $!; - if (!$pid) { - $SVN::Error::handler = \&libsvn_skip_unknown_revs; - - # Yes I'm perfectly aware that the fourth argument - # below is the limit revisions number. Unfortunately - # performance sucks with it enabled, so it's much - # faster to fetch revision ranges instead of relying - # on the limiter. - libsvn_get_log($SVN_LOG, '/'.$SVN_PATH, - $min, $max, 0, 1, 1, - sub { - my $log_msg; - if ($last_commit) { - $log_msg = libsvn_fetch( - $last_commit, @_); - $last_commit = git_commit( - $log_msg, - $last_commit, - @parents); - } else { - $log_msg = libsvn_new_tree(@_); - $last_commit = git_commit( - $log_msg, @parents); - } - }); - exit 0; - } - waitpid $pid, 0; - croak $? if $?; - ($last_rev, $last_commit) = svn_grab_base_rev(); - last if ($max >= $head); - $min = $max + 1; - $max += $inc; - $max = $head if ($max > $head); - } - restore_index($index); - return { revision => $last_rev, commit => $last_commit }; -} - -sub commit { - my (@commits) = @_; - check_upgrade_needed(); - if ($_stdin || !@commits) { - print "Reading from stdin...\n"; - @commits = (); - while () { - if (/\b($sha1_short)\b/o) { - unshift @commits, $1; - } - } - } - my @revs; - foreach my $c (@commits) { - chomp(my @tmp = safe_qx('git-rev-parse',$c)); - if (scalar @tmp == 1) { - push @revs, $tmp[0]; - } elsif (scalar @tmp > 1) { - push @revs, reverse (safe_qx('git-rev-list',@tmp)); - } else { - die "Failed to rev-parse $c\n"; - } - } - chomp @revs; - $_use_lib ? commit_lib(@revs) : commit_cmd(@revs); - print "Done committing ",scalar @revs," revisions to SVN\n"; -} - -sub commit_cmd { - my (@revs) = @_; - - chdir $SVN_WC or croak "Unable to chdir $SVN_WC: $!\n"; - my $info = svn_info('.'); - my $fetched = fetch(); - if ($info->{Revision} != $fetched->{revision}) { - print STDERR "There are new revisions that were fetched ", - "and need to be merged (or acknowledged) ", - "before committing.\n"; - exit 1; - } - $info = svn_info('.'); - read_uuid($info); - my $last = $fetched; - foreach my $c (@revs) { - my $mods = svn_checkout_tree($last, $c); - if (scalar @$mods == 0) { - print "Skipping, no changes detected\n"; - next; - } - $last = svn_commit_tree($last, $c); - } -} - -sub commit_lib { - my (@revs) = @_; - my ($r_last, $cmt_last) = svn_grab_base_rev(); - defined $r_last or die "Must have an existing revision to commit\n"; - my $fetched = fetch(); - if ($r_last != $fetched->{revision}) { - print STDERR "There are new revisions that were fetched ", - "and need to be merged (or acknowledged) ", - "before committing.\n", - "last rev: $r_last\n", - " current: $fetched->{revision}\n"; - exit 1; - } - read_uuid(); - my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : (); - my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$"; - - set_svn_commit_env(); - foreach my $c (@revs) { - my $log_msg = get_commit_message($c, $commit_msg); - - # fork for each commit because there's a memory leak I - # can't track down... (it's probably in the SVN code) - defined(my $pid = open my $fh, '-|') or croak $!; - if (!$pid) { - my $ed = SVN::Git::Editor->new( - { r => $r_last, - ra => $SVN, - c => $c, - svn_path => $SVN_PATH - }, - $SVN->get_commit_editor( - $log_msg->{msg}, - sub { - libsvn_commit_cb( - @_, $c, - $log_msg->{msg}, - $r_last, - $cmt_last) - }, - @lock) - ); - my $mods = libsvn_checkout_tree($cmt_last, $c, $ed); - if (@$mods == 0) { - print "No changes\nr$r_last = $cmt_last\n"; - $ed->abort_edit; - } else { - $ed->close_edit; - } - exit 0; - } - my ($r_new, $cmt_new, $no); - while (<$fh>) { - print $_; - chomp; - if (/^r(\d+) = ($sha1)$/o) { - ($r_new, $cmt_new) = ($1, $2); - } elsif ($_ eq 'No changes') { - $no = 1; - } - } - close $fh or croak $?; - if (! defined $r_new && ! defined $cmt_new) { - unless ($no) { - die "Failed to parse revision information\n"; - } - } else { - ($r_last, $cmt_last) = ($r_new, $cmt_new); - } - } - $ENV{LC_ALL} = 'C'; - unlink $commit_msg; -} - -sub show_ignore { - $SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url"); - $_use_lib ? show_ignore_lib() : show_ignore_cmd(); -} - -sub show_ignore_cmd { - require File::Find or die $!; - if (defined $_revision) { - die "-r/--revision option doesn't work unless the Perl SVN ", - "libraries are used\n"; - } - chdir $SVN_WC or croak $!; - my %ign; - File::Find::find({wanted=>sub{if(lstat $_ && -d _ && -d "$_/.svn"){ - s#^\./##; - @{$ign{$_}} = svn_propget_base('svn:ignore', $_); - }}, no_chdir=>1},'.'); - - print "\n# /\n"; - foreach (@{$ign{'.'}}) { print '/',$_ if /\S/ } - delete $ign{'.'}; - foreach my $i (sort keys %ign) { - print "\n# ",$i,"\n"; - foreach (@{$ign{$i}}) { print '/',$i,'/',$_ if /\S/ } - } -} - -sub show_ignore_lib { - my $repo; - ($repo, $SVN_PATH) = repo_path_split($SVN_URL); - $SVN ||= libsvn_connect($repo); - my $r = defined $_revision ? $_revision : $SVN->get_latest_revnum; - libsvn_traverse_ignore(\*STDOUT, $SVN_PATH, $r); -} - -sub graft_branches { - my $gr_file = "$GIT_DIR/info/grafts"; - my ($grafts, $comments) = read_grafts($gr_file); - my $gr_sha1; - - if (%$grafts) { - # temporarily disable our grafts file to make this idempotent - chomp($gr_sha1 = safe_qx(qw/git-hash-object -w/,$gr_file)); - rename $gr_file, "$gr_file~$gr_sha1" or croak $!; - } - - my $l_map = read_url_paths(); - my @re = map { qr/$_/is } @_opt_m if @_opt_m; - unless ($_no_default_regex) { - push @re, (qr/\b(?:merge|merging|merged)\s+with\s+([\w\.\-]+)/i, - qr/\b(?:merge|merging|merged)\s+([\w\.\-]+)/i, - qr/\b(?:from|of)\s+([\w\.\-]+)/i ); - } - foreach my $u (keys %$l_map) { - if (@re) { - foreach my $p (keys %{$l_map->{$u}}) { - graft_merge_msg($grafts,$l_map,$u,$p,@re); - } - } - unless ($_no_graft_copy) { - if ($_use_lib) { - graft_file_copy_lib($grafts,$l_map,$u); - } else { - graft_file_copy_cmd($grafts,$l_map,$u); - } - } - } - graft_tree_joins($grafts); - - write_grafts($grafts, $comments, $gr_file); - unlink "$gr_file~$gr_sha1" if $gr_sha1; -} - -sub multi_init { - my $url = shift; - $_trunk ||= 'trunk'; - $_trunk =~ s#/+$##; - $url =~ s#/+$## if $url; - if ($_trunk !~ m#^[a-z\+]+://#) { - $_trunk = '/' . $_trunk if ($_trunk !~ m#^/#); - unless ($url) { - print STDERR "E: '$_trunk' is not a complete URL ", - "and a separate URL is not specified\n"; - exit 1; - } - $_trunk = $url . $_trunk; - } - if ($GIT_SVN eq 'git-svn') { - print "GIT_SVN_ID set to 'trunk' for $_trunk\n"; - $GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk'; - } - init_vars(); - init($_trunk); - complete_url_ls_init($url, $_branches, '--branches/-b', ''); - complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/'); -} - -sub multi_fetch { - # try to do trunk first, since branches/tags - # may be descended from it. - if (-e "$GIT_DIR/svn/trunk/info/url") { - fetch_child_id('trunk', @_); - } - rec_fetch('', "$GIT_DIR/svn", @_); -} - -sub show_log { - my (@args) = @_; - my ($r_min, $r_max); - my $r_last = -1; # prevent dupes - rload_authors() if $_authors; - if (defined $TZ) { - $ENV{TZ} = $TZ; - } else { - delete $ENV{TZ}; - } - if (defined $_revision) { - if ($_revision =~ /^(\d+):(\d+)$/) { - ($r_min, $r_max) = ($1, $2); - } elsif ($_revision =~ /^\d+$/) { - $r_min = $r_max = $_revision; - } else { - print STDERR "-r$_revision is not supported, use ", - "standard \'git log\' arguments instead\n"; - exit 1; - } - } - - my $pid = open(my $log,'-|'); - defined $pid or croak $!; - if (!$pid) { - exec(git_svn_log_cmd($r_min,$r_max), @args) or croak $!; - } - setup_pager(); - my (@k, $c, $d); - - while (<$log>) { - if (/^commit ($sha1_short)/o) { - my $cmt = $1; - if ($c && cmt_showable($c) && $c->{r} != $r_last) { - $r_last = $c->{r}; - process_commit($c, $r_min, $r_max, \@k) or - goto out; - } - $d = undef; - $c = { c => $cmt }; - } elsif (/^author (.+) (\d+) ([\-\+]?\d+)$/) { - get_author_info($c, $1, $2, $3); - } elsif (/^(?:tree|parent|committer) /) { - # ignore - } elsif (/^:\d{6} \d{6} $sha1_short/o) { - push @{$c->{raw}}, $_; - } elsif (/^diff /) { - $d = 1; - push @{$c->{diff}}, $_; - } elsif ($d) { - push @{$c->{diff}}, $_; - } elsif (/^ (git-svn-id:.+)$/) { - (undef, $c->{r}, undef) = extract_metadata($1); - } elsif (s/^ //) { - push @{$c->{l}}, $_; - } - } - if ($c && defined $c->{r} && $c->{r} != $r_last) { - $r_last = $c->{r}; - process_commit($c, $r_min, $r_max, \@k); - } - if (@k) { - my $swap = $r_max; - $r_max = $r_min; - $r_min = $swap; - process_commit($_, $r_min, $r_max) foreach reverse @k; - } -out: - close $log; - print '-' x72,"\n" unless $_incremental || $_oneline; -} - -sub commit_diff_usage { - print STDERR "Usage: $0 commit-diff []\n"; - exit 1 -} - -sub commit_diff { - if (!$_use_lib) { - print STDERR "commit-diff must be used with SVN libraries\n"; - exit 1; - } - my $ta = shift or commit_diff_usage(); - my $tb = shift or commit_diff_usage(); - if (!eval { $SVN_URL = shift || file_to_s("$GIT_SVN_DIR/info/url") }) { - print STDERR "Needed URL or usable git-svn id command-line\n"; - commit_diff_usage(); - } - if (defined $_message && defined $_file) { - print STDERR "Both --message/-m and --file/-F specified ", - "for the commit message.\n", - "I have no idea what you mean\n"; - exit 1; - } - if (defined $_file) { - $_message = file_to_s($_message); - } else { - $_message ||= get_commit_message($tb, - "$GIT_DIR/.svn-commit.tmp.$$")->{msg}; - } - my $repo; - ($repo, $SVN_PATH) = repo_path_split($SVN_URL); - $SVN_LOG ||= libsvn_connect($repo); - $SVN ||= libsvn_connect($repo); - my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : (); - my $ed = SVN::Git::Editor->new({ r => $SVN->get_latest_revnum, - ra => $SVN, c => $tb, - svn_path => $SVN_PATH - }, - $SVN->get_commit_editor($_message, - sub {print "Committed $_[0]\n"},@lock) - ); - my $mods = libsvn_checkout_tree($ta, $tb, $ed); - if (@$mods == 0) { - print "No changes\n$ta == $tb\n"; - $ed->abort_edit; - } else { - $ed->close_edit; - } -} - -########################### utility functions ######################### - -sub cmt_showable { - my ($c) = @_; - return 1 if defined $c->{r}; - if ($c->{l} && $c->{l}->[-1] eq "...\n" && - $c->{a_raw} =~ /\@([a-f\d\-]+)>$/) { - my @msg = safe_qx(qw/git-cat-file commit/, $c->{c}); - shift @msg while ($msg[0] ne "\n"); - shift @msg; - @{$c->{l}} = grep !/^git-svn-id: /, @msg; - - (undef, $c->{r}, undef) = extract_metadata( - (grep(/^git-svn-id: /, @msg))[-1]); - } - return defined $c->{r}; -} - -sub git_svn_log_cmd { - my ($r_min, $r_max) = @_; - my @cmd = (qw/git-log --abbrev-commit --pretty=raw - --default/, "refs/remotes/$GIT_SVN"); - push @cmd, '--summary' if $_verbose; - return @cmd unless defined $r_max; - if ($r_max == $r_min) { - push @cmd, '--max-count=1'; - if (my $c = revdb_get($REVDB, $r_max)) { - push @cmd, $c; - } - } else { - my ($c_min, $c_max); - $c_max = revdb_get($REVDB, $r_max); - $c_min = revdb_get($REVDB, $r_min); - if ($c_min && $c_max) { - if ($r_max > $r_max) { - push @cmd, "$c_min..$c_max"; - } else { - push @cmd, "$c_max..$c_min"; - } - } elsif ($r_max > $r_min) { - push @cmd, $c_max; - } else { - push @cmd, $c_min; - } - } - return @cmd; -} - -sub fetch_child_id { - my $id = shift; - print "Fetching $id\n"; - my $ref = "$GIT_DIR/refs/remotes/$id"; - defined(my $pid = open my $fh, '-|') or croak $!; - if (!$pid) { - $_repack = undef; - $GIT_SVN = $ENV{GIT_SVN_ID} = $id; - init_vars(); - fetch(@_); - exit 0; - } - while (<$fh>) { - print $_; - check_repack() if (/^r\d+ = $sha1/); - } - close $fh or croak $?; -} - -sub rec_fetch { - my ($pfx, $p, @args) = @_; - my @dir; - foreach (sort <$p/*>) { - if (-r "$_/info/url") { - $pfx .= '/' if $pfx && $pfx !~ m!/$!; - my $id = $pfx . basename $_; - next if $id eq 'trunk'; - fetch_child_id($id, @args); - } elsif (-d $_) { - push @dir, $_; - } - } - foreach (@dir) { - my $x = $_; - $x =~ s!^\Q$GIT_DIR\E/svn/!!; - rec_fetch($x, $_); - } -} - -sub complete_url_ls_init { - my ($url, $var, $switch, $pfx) = @_; - unless ($var) { - print STDERR "W: $switch not specified\n"; - return; - } - $var =~ s#/+$##; - if ($var !~ m#^[a-z\+]+://#) { - $var = '/' . $var if ($var !~ m#^/#); - unless ($url) { - print STDERR "E: '$var' is not a complete URL ", - "and a separate URL is not specified\n"; - exit 1; - } - $var = $url . $var; - } - chomp(my @ls = $_use_lib ? libsvn_ls_fullurl($var) - : safe_qx(qw/svn ls --non-interactive/, $var)); - my $old = $GIT_SVN; - defined(my $pid = fork) or croak $!; - if (!$pid) { - foreach my $u (map { "$var/$_" } (grep m!/$!, @ls)) { - $u =~ s#/+$##; - if ($u !~ m!\Q$var\E/(.+)$!) { - print STDERR "W: Unrecognized URL: $u\n"; - die "This should never happen\n"; - } - my $id = $pfx.$1; - print "init $u => $id\n"; - $GIT_SVN = $ENV{GIT_SVN_ID} = $id; - init_vars(); - init($u); - } - exit 0; - } - waitpid $pid, 0; - croak $? if $?; -} - -sub common_prefix { - my $paths = shift; - my %common; - foreach (@$paths) { - my @tmp = split m#/#, $_; - my $p = ''; - while (my $x = shift @tmp) { - $p .= "/$x"; - $common{$p} ||= 0; - $common{$p}++; - } - } - foreach (sort {length $b <=> length $a} keys %common) { - if ($common{$_} == @$paths) { - return $_; - } - } - return ''; -} - -# grafts set here are 'stronger' in that they're based on actual tree -# matches, and won't be deleted from merge-base checking in write_grafts() -sub graft_tree_joins { - my $grafts = shift; - map_tree_joins() if (@_branch_from && !%tree_map); - return unless %tree_map; - - git_svn_each(sub { - my $i = shift; - defined(my $pid = open my $fh, '-|') or croak $!; - if (!$pid) { - exec qw/git-rev-list --pretty=raw/, - "refs/remotes/$i" or croak $!; - } - while (<$fh>) { - next unless /^commit ($sha1)$/o; - my $c = $1; - my ($t) = (<$fh> =~ /^tree ($sha1)$/o); - next unless $tree_map{$t}; - - my $l; - do { - $l = readline $fh; - } until ($l =~ /^committer (?:.+) (\d+) ([\-\+]?\d+)$/); - - my ($s, $tz) = ($1, $2); - if ($tz =~ s/^\+//) { - $s += tz_to_s_offset($tz); - } elsif ($tz =~ s/^\-//) { - $s -= tz_to_s_offset($tz); - } - - my ($url_a, $r_a, $uuid_a) = cmt_metadata($c); - - foreach my $p (@{$tree_map{$t}}) { - next if $p eq $c; - my $mb = eval { - safe_qx('git-merge-base', $c, $p) - }; - next unless ($@ || $?); - if (defined $r_a) { - # see if SVN says it's a relative - my ($url_b, $r_b, $uuid_b) = - cmt_metadata($p); - next if (defined $url_b && - defined $url_a && - ($url_a eq $url_b) && - ($uuid_a eq $uuid_b)); - if ($uuid_a eq $uuid_b) { - if ($r_b < $r_a) { - $grafts->{$c}->{$p} = 2; - next; - } elsif ($r_b > $r_a) { - $grafts->{$p}->{$c} = 2; - next; - } - } - } - my $ct = get_commit_time($p); - if ($ct < $s) { - $grafts->{$c}->{$p} = 2; - } elsif ($ct > $s) { - $grafts->{$p}->{$c} = 2; - } - # what should we do when $ct == $s ? - } - } - close $fh or croak $?; - }); -} - -# this isn't funky-filename safe, but good enough for now... -sub graft_file_copy_cmd { - my ($grafts, $l_map, $u) = @_; - my $paths = $l_map->{$u}; - my $pfx = common_prefix([keys %$paths]); - $SVN_URL ||= $u.$pfx; - my $pid = open my $fh, '-|'; - defined $pid or croak $!; - unless ($pid) { - my @exec = qw/svn log -v/; - push @exec, "-r$_revision" if defined $_revision; - exec @exec, $u.$pfx or croak $!; - } - my ($r, $mp) = (undef, undef); - while (<$fh>) { - chomp; - if (/^\-{72}$/) { - $mp = $r = undef; - } elsif (/^r(\d+) \| /) { - $r = $1 unless defined $r; - } elsif (/^Changed paths:/) { - $mp = 1; - } elsif ($mp && m#^ [AR] /(\S.*?) \(from /(\S+?):(\d+)\)$#) { - my ($p1, $p0, $r0) = ($1, $2, $3); - my $c = find_graft_path_commit($paths, $p1, $r); - next unless $c; - find_graft_path_parents($grafts, $paths, $c, $p0, $r0); - } - } -} - -sub graft_file_copy_lib { - my ($grafts, $l_map, $u) = @_; - my $tree_paths = $l_map->{$u}; - my $pfx = common_prefix([keys %$tree_paths]); - my ($repo, $path) = repo_path_split($u.$pfx); - $SVN_LOG ||= libsvn_connect($repo); - $SVN ||= libsvn_connect($repo); - - my ($base, $head) = libsvn_parse_revision(); - my $inc = 1000; - my ($min, $max) = ($base, $head < $base+$inc ? $head : $base+$inc); - my $eh = $SVN::Error::handler; - $SVN::Error::handler = \&libsvn_skip_unknown_revs; - while (1) { - my $pool = SVN::Pool->new; - libsvn_get_log($SVN_LOG, "/$path", $min, $max, 0, 1, 1, - sub { - libsvn_graft_file_copies($grafts, $tree_paths, - $path, @_); - }, $pool); - $pool->clear; - last if ($max >= $head); - $min = $max + 1; - $max += $inc; - $max = $head if ($max > $head); - } - $SVN::Error::handler = $eh; -} - -sub process_merge_msg_matches { - my ($grafts, $l_map, $u, $p, $c, @matches) = @_; - my (@strong, @weak); - foreach (@matches) { - # merging with ourselves is not interesting - next if $_ eq $p; - if ($l_map->{$u}->{$_}) { - push @strong, $_; - } else { - push @weak, $_; - } - } - foreach my $w (@weak) { - last if @strong; - # no exact match, use branch name as regexp. - my $re = qr/\Q$w\E/i; - foreach (keys %{$l_map->{$u}}) { - if (/$re/) { - push @strong, $l_map->{$u}->{$_}; - last; - } - } - last if @strong; - $w = basename($w); - $re = qr/\Q$w\E/i; - foreach (keys %{$l_map->{$u}}) { - if (/$re/) { - push @strong, $l_map->{$u}->{$_}; - last; - } - } - } - my ($rev) = ($c->{m} =~ /^git-svn-id:\s(?:\S+?)\@(\d+) - \s(?:[a-f\d\-]+)$/xsm); - unless (defined $rev) { - ($rev) = ($c->{m} =~/^git-svn-id:\s(\d+) - \@(?:[a-f\d\-]+)/xsm); - return unless defined $rev; - } - foreach my $m (@strong) { - my ($r0, $s0) = find_rev_before($rev, $m, 1); - $grafts->{$c->{c}}->{$s0} = 1 if defined $s0; - } -} - -sub graft_merge_msg { - my ($grafts, $l_map, $u, $p, @re) = @_; - - my $x = $l_map->{$u}->{$p}; - my $rl = rev_list_raw($x); - while (my $c = next_rev_list_entry($rl)) { - foreach my $re (@re) { - my (@br) = ($c->{m} =~ /$re/g); - next unless @br; - process_merge_msg_matches($grafts,$l_map,$u,$p,$c,@br); - } - } -} - -sub read_uuid { - return if $SVN_UUID; - if ($_use_lib) { - my $pool = SVN::Pool->new; - $SVN_UUID = $SVN->get_uuid($pool); - $pool->clear; - } else { - my $info = shift || svn_info('.'); - $SVN_UUID = $info->{'Repository UUID'} or - croak "Repository UUID unreadable\n"; - } -} - -sub quiet_run { - my $pid = fork; - defined $pid or croak $!; - if (!$pid) { - open my $null, '>', '/dev/null' or croak $!; - open STDERR, '>&', $null or croak $!; - open STDOUT, '>&', $null or croak $!; - exec @_ or croak $!; - } - waitpid $pid, 0; - return $?; -} - -sub repo_path_split { - my $full_url = shift; - $full_url =~ s#/+$##; - - foreach (@repo_path_split_cache) { - if ($full_url =~ s#$_##) { - my $u = $1; - $full_url =~ s#^/+##; - return ($u, $full_url); - } - } - - my ($url, $path) = ($full_url =~ m!^([a-z\+]+://[^/]*)(.*)$!i); - $path =~ s#^/+##; - my @paths = split(m#/+#, $path); - - if ($_use_lib) { - while (1) { - $SVN = libsvn_connect($url); - last if (defined $SVN && - defined eval { $SVN->get_latest_revnum }); - my $n = shift @paths || last; - $url .= "/$n"; - } - } else { - while (quiet_run(qw/svn ls --non-interactive/, $url)) { - my $n = shift @paths || last; - $url .= "/$n"; - } - } - push @repo_path_split_cache, qr/^(\Q$url\E)/; - $path = join('/',@paths); - return ($url, $path); -} - -sub setup_git_svn { - defined $SVN_URL or croak "SVN repository location required\n"; - unless (-d $GIT_DIR) { - croak "GIT_DIR=$GIT_DIR does not exist!\n"; - } - mkpath([$GIT_SVN_DIR]); - mkpath(["$GIT_SVN_DIR/info"]); - open my $fh, '>>',$REVDB or croak $!; - close $fh; - s_to_file($SVN_URL,"$GIT_SVN_DIR/info/url"); - -} - -sub assert_svn_wc_clean { - return if $_use_lib; - my ($svn_rev) = @_; - croak "$svn_rev is not an integer!\n" unless ($svn_rev =~ /^\d+$/); - my $lcr = svn_info('.')->{'Last Changed Rev'}; - if ($svn_rev != $lcr) { - print STDERR "Checking for copy-tree ... "; - my @diff = grep(/^Index: /,(safe_qx(qw(svn diff), - "-r$lcr:$svn_rev"))); - if (@diff) { - croak "Nope! Expected r$svn_rev, got r$lcr\n"; - } else { - print STDERR "OK!\n"; - } - } - my @status = grep(!/^Performing status on external/,(`svn status`)); - @status = grep(!/^\s*$/,@status); - if (scalar @status) { - print STDERR "Tree ($SVN_WC) is not clean:\n"; - print STDERR $_ foreach @status; - croak; - } -} - -sub get_tree_from_treeish { - my ($treeish) = @_; - croak "Not a sha1: $treeish\n" unless $treeish =~ /^$sha1$/o; - chomp(my $type = `git-cat-file -t $treeish`); - my $expected; - while ($type eq 'tag') { - chomp(($treeish, $type) = `git-cat-file tag $treeish`); - } - if ($type eq 'commit') { - $expected = (grep /^tree /,`git-cat-file commit $treeish`)[0]; - ($expected) = ($expected =~ /^tree ($sha1)$/); - die "Unable to get tree from $treeish\n" unless $expected; - } elsif ($type eq 'tree') { - $expected = $treeish; - } else { - die "$treeish is a $type, expected tree, tag or commit\n"; - } - return $expected; -} - -sub assert_tree { - return if $_use_lib; - my ($treeish) = @_; - my $expected = get_tree_from_treeish($treeish); - - my $tmpindex = $GIT_SVN_INDEX.'.assert-tmp'; - if (-e $tmpindex) { - unlink $tmpindex or croak $!; - } - my $old_index = set_index($tmpindex); - index_changes(1); - chomp(my $tree = `git-write-tree`); - restore_index($old_index); - if ($tree ne $expected) { - croak "Tree mismatch, Got: $tree, Expected: $expected\n"; - } - unlink $tmpindex; -} - -sub parse_diff_tree { - my $diff_fh = shift; - local $/ = "\0"; - my $state = 'meta'; - my @mods; - while (<$diff_fh>) { - chomp $_; # this gets rid of the trailing "\0" - if ($state eq 'meta' && /^:(\d{6})\s(\d{6})\s - $sha1\s($sha1)\s([MTCRAD])\d*$/xo) { - push @mods, { mode_a => $1, mode_b => $2, - sha1_b => $3, chg => $4 }; - if ($4 =~ /^(?:C|R)$/) { - $state = 'file_a'; - } else { - $state = 'file_b'; - } - } elsif ($state eq 'file_a') { - my $x = $mods[$#mods] or croak "Empty array\n"; - if ($x->{chg} !~ /^(?:C|R)$/) { - croak "Error parsing $_, $x->{chg}\n"; - } - $x->{file_a} = $_; - $state = 'file_b'; - } elsif ($state eq 'file_b') { - my $x = $mods[$#mods] or croak "Empty array\n"; - if (exists $x->{file_a} && $x->{chg} !~ /^(?:C|R)$/) { - croak "Error parsing $_, $x->{chg}\n"; - } - if (!exists $x->{file_a} && $x->{chg} =~ /^(?:C|R)$/) { - croak "Error parsing $_, $x->{chg}\n"; - } - $x->{file_b} = $_; - $state = 'meta'; - } else { - croak "Error parsing $_\n"; - } - } - close $diff_fh or croak $?; - - return \@mods; -} - -sub svn_check_prop_executable { - my $m = shift; - return if -l $m->{file_b}; - if ($m->{mode_b} =~ /755$/) { - chmod((0755 &~ umask),$m->{file_b}) or croak $!; - if ($m->{mode_a} !~ /755$/) { - sys(qw(svn propset svn:executable 1), $m->{file_b}); - } - -x $m->{file_b} or croak "$m->{file_b} is not executable!\n"; - } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) { - sys(qw(svn propdel svn:executable), $m->{file_b}); - chmod((0644 &~ umask),$m->{file_b}) or croak $!; - -x $m->{file_b} and croak "$m->{file_b} is executable!\n"; - } -} - -sub svn_ensure_parent_path { - my $dir_b = dirname(shift); - svn_ensure_parent_path($dir_b) if ($dir_b ne File::Spec->curdir); - mkpath([$dir_b]) unless (-d $dir_b); - sys(qw(svn add -N), $dir_b) unless (-d "$dir_b/.svn"); -} - -sub precommit_check { - my $mods = shift; - my (%rm_file, %rmdir_check, %added_check); - - my %o = ( D => 0, R => 1, C => 2, A => 3, M => 3, T => 3 ); - foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) { - if ($m->{chg} eq 'R') { - if (-d $m->{file_b}) { - err_dir_to_file("$m->{file_a} => $m->{file_b}"); - } - # dir/$file => dir/file/$file - my $dirname = dirname($m->{file_b}); - while ($dirname ne File::Spec->curdir) { - if ($dirname ne $m->{file_a}) { - $dirname = dirname($dirname); - next; - } - err_file_to_dir("$m->{file_a} => $m->{file_b}"); - } - # baz/zzz => baz (baz is a file) - $dirname = dirname($m->{file_a}); - while ($dirname ne File::Spec->curdir) { - if ($dirname ne $m->{file_b}) { - $dirname = dirname($dirname); - next; - } - err_dir_to_file("$m->{file_a} => $m->{file_b}"); - } - } - if ($m->{chg} =~ /^(D|R)$/) { - my $t = $1 eq 'D' ? 'file_b' : 'file_a'; - $rm_file{ $m->{$t} } = 1; - my $dirname = dirname( $m->{$t} ); - my $basename = basename( $m->{$t} ); - $rmdir_check{$dirname}->{$basename} = 1; - } elsif ($m->{chg} =~ /^(?:A|C)$/) { - if (-d $m->{file_b}) { - err_dir_to_file($m->{file_b}); - } - my $dirname = dirname( $m->{file_b} ); - my $basename = basename( $m->{file_b} ); - $added_check{$dirname}->{$basename} = 1; - while ($dirname ne File::Spec->curdir) { - if ($rm_file{$dirname}) { - err_file_to_dir($m->{file_b}); - } - $dirname = dirname $dirname; - } - } - } - return (\%rmdir_check, \%added_check); - - sub err_dir_to_file { - my $file = shift; - print STDERR "Node change from directory to file ", - "is not supported by Subversion: ",$file,"\n"; - exit 1; - } - sub err_file_to_dir { - my $file = shift; - print STDERR "Node change from file to directory ", - "is not supported by Subversion: ",$file,"\n"; - exit 1; - } -} - - -sub get_diff { - my ($from, $treeish) = @_; - assert_tree($from); - print "diff-tree $from $treeish\n"; - my $pid = open my $diff_fh, '-|'; - defined $pid or croak $!; - if ($pid == 0) { - my @diff_tree = qw(git-diff-tree -z -r); - if ($_cp_similarity) { - push @diff_tree, "-C$_cp_similarity"; - } else { - push @diff_tree, '-C'; - } - push @diff_tree, '--find-copies-harder' if $_find_copies_harder; - push @diff_tree, "-l$_l" if defined $_l; - exec(@diff_tree, $from, $treeish) or croak $!; - } - return parse_diff_tree($diff_fh); -} - -sub svn_checkout_tree { - my ($from, $treeish) = @_; - my $mods = get_diff($from->{commit}, $treeish); - return $mods unless (scalar @$mods); - my ($rm, $add) = precommit_check($mods); - - my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 ); - foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) { - if ($m->{chg} eq 'C') { - svn_ensure_parent_path( $m->{file_b} ); - sys(qw(svn cp), $m->{file_a}, $m->{file_b}); - apply_mod_line_blob($m); - svn_check_prop_executable($m); - } elsif ($m->{chg} eq 'D') { - sys(qw(svn rm --force), $m->{file_b}); - } elsif ($m->{chg} eq 'R') { - svn_ensure_parent_path( $m->{file_b} ); - sys(qw(svn mv --force), $m->{file_a}, $m->{file_b}); - apply_mod_line_blob($m); - svn_check_prop_executable($m); - } elsif ($m->{chg} eq 'M') { - apply_mod_line_blob($m); - svn_check_prop_executable($m); - } elsif ($m->{chg} eq 'T') { - sys(qw(svn rm --force),$m->{file_b}); - apply_mod_line_blob($m); - sys(qw(svn add), $m->{file_b}); - svn_check_prop_executable($m); - } elsif ($m->{chg} eq 'A') { - svn_ensure_parent_path( $m->{file_b} ); - apply_mod_line_blob($m); - sys(qw(svn add), $m->{file_b}); - svn_check_prop_executable($m); - } else { - croak "Invalid chg: $m->{chg}\n"; - } - } - - assert_tree($treeish); - if ($_rmdir) { # remove empty directories - handle_rmdir($rm, $add); - } - assert_tree($treeish); - return $mods; -} - -sub libsvn_checkout_tree { - my ($from, $treeish, $ed) = @_; - my $mods = get_diff($from, $treeish); - return $mods unless (scalar @$mods); - my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 ); - foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) { - my $f = $m->{chg}; - if (defined $o{$f}) { - $ed->$f($m, $_q); - } else { - croak "Invalid change type: $f\n"; - } - } - $ed->rmdirs($_q) if $_rmdir; - return $mods; -} - -# svn ls doesn't work with respect to the current working tree, but what's -# in the repository. There's not even an option for it... *sigh* -# (added files don't show up and removed files remain in the ls listing) -sub svn_ls_current { - my ($dir, $rm, $add) = @_; - chomp(my @ls = safe_qx('svn','ls',$dir)); - my @ret = (); - foreach (@ls) { - s#/$##; # trailing slashes are evil - push @ret, $_ unless $rm->{$dir}->{$_}; - } - if (exists $add->{$dir}) { - push @ret, keys %{$add->{$dir}}; - } - return \@ret; -} - -sub handle_rmdir { - my ($rm, $add) = @_; - - foreach my $dir (sort {length $b <=> length $a} keys %$rm) { - my $ls = svn_ls_current($dir, $rm, $add); - next if (scalar @$ls); - sys(qw(svn rm --force),$dir); - - my $dn = dirname $dir; - $rm->{ $dn }->{ basename $dir } = 1; - $ls = svn_ls_current($dn, $rm, $add); - while (scalar @$ls == 0 && $dn ne File::Spec->curdir) { - sys(qw(svn rm --force),$dn); - $dir = basename $dn; - $dn = dirname $dn; - $rm->{ $dn }->{ $dir } = 1; - $ls = svn_ls_current($dn, $rm, $add); - } - } -} - -sub get_commit_message { - my ($commit, $commit_msg) = (@_); - my %log_msg = ( msg => '' ); - open my $msg, '>', $commit_msg or croak $!; - - chomp(my $type = `git-cat-file -t $commit`); - if ($type eq 'commit') { - my $pid = open my $msg_fh, '-|'; - defined $pid or croak $!; - - if ($pid == 0) { - exec(qw(git-cat-file commit), $commit) or croak $!; - } - my $in_msg = 0; - while (<$msg_fh>) { - if (!$in_msg) { - $in_msg = 1 if (/^\s*$/); - } elsif (/^git-svn-id: /) { - # skip this, we regenerate the correct one - # on re-fetch anyways - } else { - print $msg $_ or croak $!; - } - } - close $msg_fh or croak $?; - } - close $msg or croak $!; - - if ($_edit || ($type eq 'tree')) { - my $editor = $ENV{VISUAL} || $ENV{EDITOR} || 'vi'; - system($editor, $commit_msg); - } - - # file_to_s removes all trailing newlines, so just use chomp() here: - open $msg, '<', $commit_msg or croak $!; - { local $/; chomp($log_msg{msg} = <$msg>); } - close $msg or croak $!; - - return \%log_msg; -} - -sub set_svn_commit_env { - if (defined $LC_ALL) { - $ENV{LC_ALL} = $LC_ALL; - } else { - delete $ENV{LC_ALL}; - } -} - -sub svn_commit_tree { - my ($last, $commit) = @_; - my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$"; - my $log_msg = get_commit_message($commit, $commit_msg); - my ($oneline) = ($log_msg->{msg} =~ /([^\n\r]+)/); - print "Committing $commit: $oneline\n"; - - set_svn_commit_env(); - my @ci_output = safe_qx(qw(svn commit -F),$commit_msg); - $ENV{LC_ALL} = 'C'; - unlink $commit_msg; - my ($committed) = ($ci_output[$#ci_output] =~ /(\d+)/); - if (!defined $committed) { - my $out = join("\n",@ci_output); - print STDERR "W: Trouble parsing \`svn commit' output:\n\n", - $out, "\n\nAssuming English locale..."; - ($committed) = ($out =~ /^Committed revision \d+\./sm); - defined $committed or die " FAILED!\n", - "Commit output failed to parse committed revision!\n", - print STDERR " OK\n"; - } - - my @svn_up = qw(svn up); - push @svn_up, '--ignore-externals' unless $_no_ignore_ext; - if ($_optimize_commits && ($committed == ($last->{revision} + 1))) { - push @svn_up, "-r$committed"; - sys(@svn_up); - my $info = svn_info('.'); - my $date = $info->{'Last Changed Date'} or die "Missing date\n"; - if ($info->{'Last Changed Rev'} != $committed) { - croak "$info->{'Last Changed Rev'} != $committed\n" - } - my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~ - /(\d{4})\-(\d\d)\-(\d\d)\s - (\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x) - or croak "Failed to parse date: $date\n"; - $log_msg->{date} = "$tz $Y-$m-$d $H:$M:$S"; - $log_msg->{author} = $info->{'Last Changed Author'}; - $log_msg->{revision} = $committed; - $log_msg->{msg} .= "\n"; - $log_msg->{parents} = [ $last->{commit} ]; - $log_msg->{commit} = git_commit($log_msg, $commit); - return $log_msg; - } - # resync immediately - push @svn_up, "-r$last->{revision}"; - sys(@svn_up); - return fetch("$committed=$commit"); -} - -sub rev_list_raw { - my (@args) = @_; - my $pid = open my $fh, '-|'; - defined $pid or croak $!; - if (!$pid) { - exec(qw/git-rev-list --pretty=raw/, @args) or croak $!; - } - return { fh => $fh, t => { } }; -} - -sub next_rev_list_entry { - my $rl = shift; - my $fh = $rl->{fh}; - my $x = $rl->{t}; - while (<$fh>) { - if (/^commit ($sha1)$/o) { - if ($x->{c}) { - $rl->{t} = { c => $1 }; - return $x; - } else { - $x->{c} = $1; - } - } elsif (/^parent ($sha1)$/o) { - $x->{p}->{$1} = 1; - } elsif (s/^ //) { - $x->{m} ||= ''; - $x->{m} .= $_; - } - } - return ($x != $rl->{t}) ? $x : undef; -} - -# read the entire log into a temporary file (which is removed ASAP) -# and store the file handle + parser state -sub svn_log_raw { - my (@log_args) = @_; - my $log_fh = IO::File->new_tmpfile or croak $!; - my $pid = fork; - defined $pid or croak $!; - if (!$pid) { - open STDOUT, '>&', $log_fh or croak $!; - exec (qw(svn log), @log_args) or croak $! - } - waitpid $pid, 0; - croak $? if $?; - seek $log_fh, 0, 0 or croak $!; - return { state => 'sep', fh => $log_fh }; -} - -sub next_log_entry { - my $log = shift; # retval of svn_log_raw() - my $ret = undef; - my $fh = $log->{fh}; - - while (<$fh>) { - chomp; - if (/^\-{72}$/) { - if ($log->{state} eq 'msg') { - if ($ret->{lines}) { - $ret->{msg} .= $_."\n"; - unless(--$ret->{lines}) { - $log->{state} = 'sep'; - } - } else { - croak "Log parse error at: $_\n", - $ret->{revision}, - "\n"; - } - next; - } - if ($log->{state} ne 'sep') { - croak "Log parse error at: $_\n", - "state: $log->{state}\n", - $ret->{revision}, - "\n"; - } - $log->{state} = 'rev'; - - # if we have an empty log message, put something there: - if ($ret) { - $ret->{msg} ||= "\n"; - delete $ret->{lines}; - return $ret; - } - next; - } - if ($log->{state} eq 'rev' && s/^r(\d+)\s*\|\s*//) { - my $rev = $1; - my ($author, $date, $lines) = split(/\s*\|\s*/, $_, 3); - ($lines) = ($lines =~ /(\d+)/); - my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~ - /(\d{4})\-(\d\d)\-(\d\d)\s - (\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x) - or croak "Failed to parse date: $date\n"; - $ret = { revision => $rev, - date => "$tz $Y-$m-$d $H:$M:$S", - author => $author, - lines => $lines, - msg => '' }; - if (defined $_authors && ! defined $users{$author}) { - die "Author: $author not defined in ", - "$_authors file\n"; - } - $log->{state} = 'msg_start'; - next; - } - # skip the first blank line of the message: - if ($log->{state} eq 'msg_start' && /^$/) { - $log->{state} = 'msg'; - } elsif ($log->{state} eq 'msg') { - if ($ret->{lines}) { - $ret->{msg} .= $_."\n"; - unless (--$ret->{lines}) { - $log->{state} = 'sep'; - } - } else { - croak "Log parse error at: $_\n", - $ret->{revision},"\n"; - } - } - } - return $ret; -} - -sub svn_info { - my $url = shift || $SVN_URL; - - my $pid = open my $info_fh, '-|'; - defined $pid or croak $!; - - if ($pid == 0) { - exec(qw(svn info),$url) or croak $!; - } - - my $ret = {}; - # only single-lines seem to exist in svn info output - while (<$info_fh>) { - chomp $_; - if (m#^([^:]+)\s*:\s*(\S.*)$#) { - $ret->{$1} = $2; - push @{$ret->{-order}}, $1; - } - } - close $info_fh or croak $?; - return $ret; -} - -sub sys { system(@_) == 0 or croak $? } - -sub eol_cp { - my ($from, $to) = @_; - my $es = svn_propget_base('svn:eol-style', $to); - open my $rfd, '<', $from or croak $!; - binmode $rfd or croak $!; - open my $wfd, '>', $to or croak $!; - binmode $wfd or croak $!; - eol_cp_fd($rfd, $wfd, $es); - close $rfd or croak $!; - close $wfd or croak $!; -} - -sub eol_cp_fd { - my ($rfd, $wfd, $es) = @_; - my $eol = defined $es ? $EOL{$es} : undef; - my $buf; - use bytes; - while (1) { - my ($r, $w, $t); - defined($r = sysread($rfd, $buf, 4096)) or croak $!; - return unless $r; - if ($eol) { - if ($buf =~ /\015$/) { - my $c; - defined($r = sysread($rfd,$c,1)) or croak $!; - $buf .= $c if $r > 0; - } - $buf =~ s/(?:\015\012|\015|\012)/$eol/gs; - $r = length($buf); - } - for ($w = 0; $w < $r; $w += $t) { - $t = syswrite($wfd, $buf, $r - $w, $w) or croak $!; - } - } - no bytes; -} - -sub do_update_index { - my ($z_cmd, $cmd, $no_text_base) = @_; - - my $z = open my $p, '-|'; - defined $z or croak $!; - unless ($z) { exec @$z_cmd or croak $! } - - my $pid = open my $ui, '|-'; - defined $pid or croak $!; - unless ($pid) { - exec('git-update-index',"--$cmd",'-z','--stdin') or croak $!; - } - local $/ = "\0"; - while (my $x = <$p>) { - chomp $x; - if (!$no_text_base && lstat $x && ! -l _ && - svn_propget_base('svn:keywords', $x)) { - my $mode = -x _ ? 0755 : 0644; - my ($v,$d,$f) = File::Spec->splitpath($x); - my $tb = File::Spec->catfile($d, '.svn', 'tmp', - 'text-base',"$f.svn-base"); - $tb =~ s#^/##; - unless (-f $tb) { - $tb = File::Spec->catfile($d, '.svn', - 'text-base',"$f.svn-base"); - $tb =~ s#^/##; - } - unlink $x or croak $!; - eol_cp($tb, $x); - chmod(($mode &~ umask), $x) or croak $!; - } - print $ui $x,"\0"; - } - close $ui or croak $?; -} - -sub index_changes { - return if $_use_lib; - - if (!-f "$GIT_SVN_DIR/info/exclude") { - open my $fd, '>>', "$GIT_SVN_DIR/info/exclude" or croak $!; - print $fd '.svn',"\n"; - close $fd or croak $!; - } - my $no_text_base = shift; - do_update_index([qw/git-diff-files --name-only -z/], - 'remove', - $no_text_base); - do_update_index([qw/git-ls-files -z --others/, - "--exclude-from=$GIT_SVN_DIR/info/exclude"], - 'add', - $no_text_base); -} - -sub s_to_file { - my ($str, $file, $mode) = @_; - open my $fd,'>',$file or croak $!; - print $fd $str,"\n" or croak $!; - close $fd or croak $!; - chmod ($mode &~ umask, $file) if (defined $mode); -} - -sub file_to_s { - my $file = shift; - open my $fd,'<',$file or croak "$!: file: $file\n"; - local $/; - my $ret = <$fd>; - close $fd or croak $!; - $ret =~ s/\s*$//s; - return $ret; -} - -sub assert_revision_unknown { - my $r = shift; - if (my $c = revdb_get($REVDB, $r)) { - croak "$r = $c already exists! Why are we refetching it?"; - } -} - -sub trees_eq { - my ($x, $y) = @_; - my @x = safe_qx('git-cat-file','commit',$x); - my @y = safe_qx('git-cat-file','commit',$y); - if (($y[0] ne $x[0]) || $x[0] !~ /^tree $sha1\n$/ - || $y[0] !~ /^tree $sha1\n$/) { - print STDERR "Trees not equal: $y[0] != $x[0]\n"; - return 0 - } - return 1; -} - -sub git_commit { - my ($log_msg, @parents) = @_; - assert_revision_unknown($log_msg->{revision}); - map_tree_joins() if (@_branch_from && !%tree_map); - - my (@tmp_parents, @exec_parents, %seen_parent); - if (my $lparents = $log_msg->{parents}) { - @tmp_parents = @$lparents - } - # commit parents can be conditionally bound to a particular - # svn revision via: "svn_revno=commit_sha1", filter them out here: - foreach my $p (@parents) { - next unless defined $p; - if ($p =~ /^(\d+)=($sha1_short)$/o) { - if ($1 == $log_msg->{revision}) { - push @tmp_parents, $2; - } - } else { - push @tmp_parents, $p if $p =~ /$sha1_short/o; - } - } - my $tree = $log_msg->{tree}; - if (!defined $tree) { - my $index = set_index($GIT_SVN_INDEX); - index_changes(); - chomp($tree = `git-write-tree`); - croak $? if $?; - restore_index($index); - } - - # just in case we clobber the existing ref, we still want that ref - # as our parent: - if (my $cur = eval { file_to_s("$GIT_DIR/refs/remotes/$GIT_SVN") }) { - push @tmp_parents, $cur; - } - - if (exists $tree_map{$tree}) { - foreach my $p (@{$tree_map{$tree}}) { - my $skip; - foreach (@tmp_parents) { - # see if a common parent is found - my $mb = eval { - safe_qx('git-merge-base', $_, $p) - }; - next if ($@ || $?); - $skip = 1; - last; - } - next if $skip; - my ($url_p, $r_p, $uuid_p) = cmt_metadata($p); - next if (($SVN_UUID eq $uuid_p) && - ($log_msg->{revision} > $r_p)); - next if (defined $url_p && defined $SVN_URL && - ($SVN_UUID eq $uuid_p) && - ($url_p eq $SVN_URL)); - push @tmp_parents, $p; - } - } - foreach (@tmp_parents) { - next if $seen_parent{$_}; - $seen_parent{$_} = 1; - push @exec_parents, $_; - # MAXPARENT is defined to 16 in commit-tree.c: - last if @exec_parents > 16; - } - - set_commit_env($log_msg); - my @exec = ('git-commit-tree', $tree); - push @exec, '-p', $_ foreach @exec_parents; - defined(my $pid = open3(my $msg_fh, my $out_fh, '>&STDERR', @exec)) - or croak $!; - print $msg_fh $log_msg->{msg} or croak $!; - unless ($_no_metadata) { - print $msg_fh "\ngit-svn-id: $SVN_URL\@$log_msg->{revision}", - " $SVN_UUID\n" or croak $!; - } - $msg_fh->flush == 0 or croak $!; - close $msg_fh or croak $!; - chomp(my $commit = do { local $/; <$out_fh> }); - close $out_fh or croak $!; - waitpid $pid, 0; - croak $? if $?; - if ($commit !~ /^$sha1$/o) { - die "Failed to commit, invalid sha1: $commit\n"; - } - sys('git-update-ref',"refs/remotes/$GIT_SVN",$commit); - revdb_set($REVDB, $log_msg->{revision}, $commit); - - # this output is read via pipe, do not change: - print "r$log_msg->{revision} = $commit\n"; - check_repack(); - return $commit; -} - -sub check_repack { - if ($_repack && (--$_repack_nr == 0)) { - $_repack_nr = $_repack; - sys("git repack $_repack_flags"); - } -} - -sub set_commit_env { - my ($log_msg) = @_; - my $author = $log_msg->{author}; - if (!defined $author || length $author == 0) { - $author = '(no author)'; - } - my ($name,$email) = defined $users{$author} ? @{$users{$author}} - : ($author,"$author\@$SVN_UUID"); - $ENV{GIT_AUTHOR_NAME} = $ENV{GIT_COMMITTER_NAME} = $name; - $ENV{GIT_AUTHOR_EMAIL} = $ENV{GIT_COMMITTER_EMAIL} = $email; - $ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_msg->{date}; -} - -sub apply_mod_line_blob { - my $m = shift; - if ($m->{mode_b} =~ /^120/) { - blob_to_symlink($m->{sha1_b}, $m->{file_b}); - } else { - blob_to_file($m->{sha1_b}, $m->{file_b}); - } -} - -sub blob_to_symlink { - my ($blob, $link) = @_; - defined $link or croak "\$link not defined!\n"; - croak "Not a sha1: $blob\n" unless $blob =~ /^$sha1$/o; - if (-l $link || -f _) { - unlink $link or croak $!; - } - - my $dest = `git-cat-file blob $blob`; # no newline, so no chomp - symlink $dest, $link or croak $!; -} - -sub blob_to_file { - my ($blob, $file) = @_; - defined $file or croak "\$file not defined!\n"; - croak "Not a sha1: $blob\n" unless $blob =~ /^$sha1$/o; - if (-l $file || -f _) { - unlink $file or croak $!; - } - - open my $blob_fh, '>', $file or croak "$!: $file\n"; - my $pid = fork; - defined $pid or croak $!; - - if ($pid == 0) { - open STDOUT, '>&', $blob_fh or croak $!; - exec('git-cat-file','blob',$blob) or croak $!; - } - waitpid $pid, 0; - croak $? if $?; - - close $blob_fh or croak $!; -} - -sub safe_qx { - my $pid = open my $child, '-|'; - defined $pid or croak $!; - if ($pid == 0) { - exec(@_) or croak $!; - } - my @ret = (<$child>); - close $child or croak $?; - die $? if $?; # just in case close didn't error out - return wantarray ? @ret : join('',@ret); -} - -sub svn_compat_check { - if ($_follow_parent) { - print STDERR 'E: --follow-parent functionality is only ', - "available when SVN libraries are used\n"; - exit 1; - } - my @co_help = safe_qx(qw(svn co -h)); - unless (grep /ignore-externals/,@co_help) { - print STDERR "W: Installed svn version does not support ", - "--ignore-externals\n"; - $_no_ignore_ext = 1; - } - if (grep /usage: checkout URL\[\@REV\]/,@co_help) { - $_svn_co_url_revs = 1; - } - if (grep /\[TARGET\[\@REV\]\.\.\.\]/, `svn propget -h`) { - $_svn_pg_peg_revs = 1; - } - - # I really, really hope nobody hits this... - unless (grep /stop-on-copy/, (safe_qx(qw(svn log -h)))) { - print STDERR <<''; -W: The installed svn version does not support the --stop-on-copy flag in - the log command. - Lets hope the directory you're tracking is not a branch or tag - and was never moved within the repository... - - $_no_stop_copy = 1; - } -} - -# *sigh*, new versions of svn won't honor -r without URL@, -# (and they won't honor URL@ without -r, too!) -sub svn_cmd_checkout { - my ($url, $rev, $dir) = @_; - my @cmd = ('svn','co', "-r$rev"); - push @cmd, '--ignore-externals' unless $_no_ignore_ext; - $url .= "\@$rev" if $_svn_co_url_revs; - sys(@cmd, $url, $dir); -} - -sub check_upgrade_needed { - if (!-r $REVDB) { - -d $GIT_SVN_DIR or mkpath([$GIT_SVN_DIR]); - open my $fh, '>>',$REVDB or croak $!; - close $fh; - } - my $old = eval { - my $pid = open my $child, '-|'; - defined $pid or croak $!; - if ($pid == 0) { - close STDERR; - exec('git-rev-parse',"$GIT_SVN-HEAD") or croak $!; - } - my @ret = (<$child>); - close $child or croak $?; - die $? if $?; # just in case close didn't error out - return wantarray ? @ret : join('',@ret); - }; - return unless $old; - my $head = eval { safe_qx('git-rev-parse',"refs/remotes/$GIT_SVN") }; - if ($@ || !$head) { - print STDERR "Please run: $0 rebuild --upgrade\n"; - exit 1; - } -} - -# fills %tree_map with a reverse mapping of trees to commits. Useful -# for finding parents to commit on. -sub map_tree_joins { - my %seen; - foreach my $br (@_branch_from) { - my $pid = open my $pipe, '-|'; - defined $pid or croak $!; - if ($pid == 0) { - exec(qw(git-rev-list --topo-order --pretty=raw), $br) - or croak $!; - } - while (<$pipe>) { - if (/^commit ($sha1)$/o) { - my $commit = $1; - - # if we've seen a commit, - # we've seen its parents - last if $seen{$commit}; - my ($tree) = (<$pipe> =~ /^tree ($sha1)$/o); - unless (defined $tree) { - die "Failed to parse commit $commit\n"; - } - push @{$tree_map{$tree}}, $commit; - $seen{$commit} = 1; - } - } - close $pipe; # we could be breaking the pipe early - } -} - -sub load_all_refs { - if (@_branch_from) { - print STDERR '--branch|-b parameters are ignored when ', - "--branch-all-refs|-B is passed\n"; - } - - # don't worry about rev-list on non-commit objects/tags, - # it shouldn't blow up if a ref is a blob or tree... - chomp(@_branch_from = `git-rev-parse --symbolic --all`); -} - -# ' = real-name ' mapping based on git-svnimport: -sub load_authors { - open my $authors, '<', $_authors or die "Can't open $_authors $!\n"; - while (<$authors>) { - chomp; - next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/; - my ($user, $name, $email) = ($1, $2, $3); - $users{$user} = [$name, $email]; - } - close $authors or croak $!; -} - -sub rload_authors { - open my $authors, '<', $_authors or die "Can't open $_authors $!\n"; - while (<$authors>) { - chomp; - next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/; - my ($user, $name, $email) = ($1, $2, $3); - $rusers{"$name <$email>"} = $user; - } - close $authors or croak $!; -} - -sub svn_propget_base { - my ($p, $f) = @_; - $f .= '@BASE' if $_svn_pg_peg_revs; - return safe_qx(qw/svn propget/, $p, $f); -} - -sub git_svn_each { - my $sub = shift; - foreach (`git-rev-parse --symbolic --all`) { - next unless s#^refs/remotes/##; - chomp $_; - next unless -f "$GIT_DIR/svn/$_/info/url"; - &$sub($_); - } -} - -sub migrate_revdb { - git_svn_each(sub { - my $id = shift; - defined(my $pid = fork) or croak $!; - if (!$pid) { - $GIT_SVN = $ENV{GIT_SVN_ID} = $id; - init_vars(); - exit 0 if -r $REVDB; - print "Upgrading svn => git mapping...\n"; - -d $GIT_SVN_DIR or mkpath([$GIT_SVN_DIR]); - open my $fh, '>>',$REVDB or croak $!; - close $fh; - rebuild(); - print "Done upgrading. You may now delete the ", - "deprecated $GIT_SVN_DIR/revs directory\n"; - exit 0; - } - waitpid $pid, 0; - croak $? if $?; - }); -} - -sub migration_check { - migrate_revdb() unless (-e $REVDB); - return if (-d "$GIT_DIR/svn" || !-d $GIT_DIR); - print "Upgrading repository...\n"; - unless (-d "$GIT_DIR/svn") { - mkdir "$GIT_DIR/svn" or croak $!; - } - print "Data from a previous version of git-svn exists, but\n\t", - "$GIT_SVN_DIR\n\t(required for this version ", - "($VERSION) of git-svn) does not.\n"; - - foreach my $x (`git-rev-parse --symbolic --all`) { - next unless $x =~ s#^refs/remotes/##; - chomp $x; - next unless -f "$GIT_DIR/$x/info/url"; - my $u = eval { file_to_s("$GIT_DIR/$x/info/url") }; - next unless $u; - my $dn = dirname("$GIT_DIR/svn/$x"); - mkpath([$dn]) unless -d $dn; - rename "$GIT_DIR/$x", "$GIT_DIR/svn/$x" or croak "$!: $x"; - } - migrate_revdb() if (-d $GIT_SVN_DIR && !-w $REVDB); - print "Done upgrading.\n"; -} - -sub find_rev_before { - my ($r, $id, $eq_ok) = @_; - my $f = "$GIT_DIR/svn/$id/.rev_db"; - return (undef,undef) unless -r $f; - --$r unless $eq_ok; - while ($r > 0) { - if (my $c = revdb_get($f, $r)) { - return ($r, $c); - } - --$r; - } - return (undef, undef); -} - -sub init_vars { - $GIT_SVN ||= $ENV{GIT_SVN_ID} || 'git-svn'; - $GIT_SVN_DIR = "$GIT_DIR/svn/$GIT_SVN"; - $REVDB = "$GIT_SVN_DIR/.rev_db"; - $GIT_SVN_INDEX = "$GIT_SVN_DIR/index"; - $SVN_URL = undef; - $SVN_WC = "$GIT_SVN_DIR/tree"; - %tree_map = (); -} - -# convert GetOpt::Long specs for use by git-repo-config -sub read_repo_config { - return unless -d $GIT_DIR; - my $opts = shift; - foreach my $o (keys %$opts) { - my $v = $opts->{$o}; - my ($key) = ($o =~ /^([a-z\-]+)/); - $key =~ s/-//g; - my $arg = 'git-repo-config'; - $arg .= ' --int' if ($o =~ /[:=]i$/); - $arg .= ' --bool' if ($o !~ /[:=][sfi]$/); - if (ref $v eq 'ARRAY') { - chomp(my @tmp = `$arg --get-all svn.$key`); - @$v = @tmp if @tmp; - } else { - chomp(my $tmp = `$arg --get svn.$key`); - if ($tmp && !($arg =~ / --bool / && $tmp eq 'false')) { - $$v = $tmp; - } - } - } -} - -sub set_default_vals { - if (defined $_repack) { - $_repack = 1000 if ($_repack <= 0); - $_repack_nr = $_repack; - $_repack_flags ||= '-d'; - } -} - -sub read_grafts { - my $gr_file = shift; - my ($grafts, $comments) = ({}, {}); - if (open my $fh, '<', $gr_file) { - my @tmp; - while (<$fh>) { - if (/^($sha1)\s+/) { - my $c = $1; - if (@tmp) { - @{$comments->{$c}} = @tmp; - @tmp = (); - } - foreach my $p (split /\s+/, $_) { - $grafts->{$c}->{$p} = 1; - } - } else { - push @tmp, $_; - } - } - close $fh or croak $!; - @{$comments->{'END'}} = @tmp if @tmp; - } - return ($grafts, $comments); -} - -sub write_grafts { - my ($grafts, $comments, $gr_file) = @_; - - open my $fh, '>', $gr_file or croak $!; - foreach my $c (sort keys %$grafts) { - if ($comments->{$c}) { - print $fh $_ foreach @{$comments->{$c}}; - } - my $p = $grafts->{$c}; - my %x; # real parents - delete $p->{$c}; # commits are not self-reproducing... - my $pid = open my $ch, '-|'; - defined $pid or croak $!; - if (!$pid) { - exec(qw/git-cat-file commit/, $c) or croak $!; - } - while (<$ch>) { - if (/^parent ($sha1)/) { - $x{$1} = $p->{$1} = 1; - } else { - last unless /^\S/; - } - } - close $ch; # breaking the pipe - - # if real parents are the only ones in the grafts, drop it - next if join(' ',sort keys %$p) eq join(' ',sort keys %x); - - my (@ip, @jp, $mb); - my %del = %x; - @ip = @jp = keys %$p; - foreach my $i (@ip) { - next if $del{$i} || $p->{$i} == 2; - foreach my $j (@jp) { - next if $i eq $j || $del{$j} || $p->{$j} == 2; - $mb = eval { safe_qx('git-merge-base',$i,$j) }; - next unless $mb; - chomp $mb; - next if $x{$mb}; - if ($mb eq $j) { - delete $p->{$i}; - $del{$i} = 1; - } elsif ($mb eq $i) { - delete $p->{$j}; - $del{$j} = 1; - } - } - } - - # if real parents are the only ones in the grafts, drop it - next if join(' ',sort keys %$p) eq join(' ',sort keys %x); - - print $fh $c, ' ', join(' ', sort keys %$p),"\n"; - } - if ($comments->{'END'}) { - print $fh $_ foreach @{$comments->{'END'}}; - } - close $fh or croak $!; -} - -sub read_url_paths_all { - my ($l_map, $pfx, $p) = @_; - my @dir; - foreach (<$p/*>) { - if (-r "$_/info/url") { - $pfx .= '/' if $pfx && $pfx !~ m!/$!; - my $id = $pfx . basename $_; - my $url = file_to_s("$_/info/url"); - my ($u, $p) = repo_path_split($url); - $l_map->{$u}->{$p} = $id; - } elsif (-d $_) { - push @dir, $_; - } - } - foreach (@dir) { - my $x = $_; - $x =~ s!^\Q$GIT_DIR\E/svn/!!o; - read_url_paths_all($l_map, $x, $_); - } -} - -# this one only gets ids that have been imported, not new ones -sub read_url_paths { - my $l_map = {}; - git_svn_each(sub { my $x = shift; - my $url = file_to_s("$GIT_DIR/svn/$x/info/url"); - my ($u, $p) = repo_path_split($url); - $l_map->{$u}->{$p} = $x; - }); - return $l_map; -} - -sub extract_metadata { - my $id = shift or return (undef, undef, undef); - my ($url, $rev, $uuid) = ($id =~ /^git-svn-id:\s(\S+?)\@(\d+) - \s([a-f\d\-]+)$/x); - if (!$rev || !$uuid || !$url) { - # some of the original repositories I made had - # indentifiers like this: - ($rev, $uuid) = ($id =~/^git-svn-id:\s(\d+)\@([a-f\d\-]+)/); - } - return ($url, $rev, $uuid); -} - -sub cmt_metadata { - return extract_metadata((grep(/^git-svn-id: /, - safe_qx(qw/git-cat-file commit/, shift)))[-1]); -} - -sub get_commit_time { - my $cmt = shift; - defined(my $pid = open my $fh, '-|') or croak $!; - if (!$pid) { - exec qw/git-rev-list --pretty=raw -n1/, $cmt or croak $!; - } - while (<$fh>) { - /^committer\s(?:.+) (\d+) ([\-\+]?\d+)$/ or next; - my ($s, $tz) = ($1, $2); - if ($tz =~ s/^\+//) { - $s += tz_to_s_offset($tz); - } elsif ($tz =~ s/^\-//) { - $s -= tz_to_s_offset($tz); - } - close $fh; - return $s; - } - die "Can't get commit time for commit: $cmt\n"; -} - -sub tz_to_s_offset { - my ($tz) = @_; - $tz =~ s/(\d\d)$//; - return ($1 * 60) + ($tz * 3600); -} - -sub setup_pager { # translated to Perl from pager.c - return unless (-t *STDOUT); - my $pager = $ENV{PAGER}; - if (!defined $pager) { - $pager = 'less'; - } elsif (length $pager == 0 || $pager eq 'cat') { - return; - } - pipe my $rfd, my $wfd or return; - defined(my $pid = fork) or croak $!; - if (!$pid) { - open STDOUT, '>&', $wfd or croak $!; - return; - } - open STDIN, '<&', $rfd or croak $!; - $ENV{LESS} ||= '-S'; - exec $pager or croak "Can't run pager: $!\n";; -} - -sub get_author_info { - my ($dest, $author, $t, $tz) = @_; - $author =~ s/(?:^\s*|\s*$)//g; - $dest->{a_raw} = $author; - my $_a; - if ($_authors) { - $_a = $rusers{$author} || undef; - } - if (!$_a) { - ($_a) = ($author =~ /<([^>]+)\@[^>]+>$/); - } - $dest->{t} = $t; - $dest->{tz} = $tz; - $dest->{a} = $_a; - # Date::Parse isn't in the standard Perl distro :( - if ($tz =~ s/^\+//) { - $t += tz_to_s_offset($tz); - } elsif ($tz =~ s/^\-//) { - $t -= tz_to_s_offset($tz); - } - $dest->{t_utc} = $t; -} - -sub process_commit { - my ($c, $r_min, $r_max, $defer) = @_; - if (defined $r_min && defined $r_max) { - if ($r_min == $c->{r} && $r_min == $r_max) { - show_commit($c); - return 0; - } - return 1 if $r_min == $r_max; - if ($r_min < $r_max) { - # we need to reverse the print order - return 0 if (defined $_limit && --$_limit < 0); - push @$defer, $c; - return 1; - } - if ($r_min != $r_max) { - return 1 if ($r_min < $c->{r}); - return 1 if ($r_max > $c->{r}); - } - } - return 0 if (defined $_limit && --$_limit < 0); - show_commit($c); - return 1; -} - -sub show_commit { - my $c = shift; - if ($_oneline) { - my $x = "\n"; - if (my $l = $c->{l}) { - while ($l->[0] =~ /^\s*$/) { shift @$l } - $x = $l->[0]; - } - $_l_fmt ||= 'A' . length($c->{r}); - print 'r',pack($_l_fmt, $c->{r}),' | '; - print "$c->{c} | " if $_show_commit; - print $x; - } else { - show_commit_normal($c); - } -} - -sub show_commit_normal { - my ($c) = @_; - print '-' x72, "\nr$c->{r} | "; - print "$c->{c} | " if $_show_commit; - print "$c->{a} | ", strftime("%Y-%m-%d %H:%M:%S %z (%a, %d %b %Y)", - localtime($c->{t_utc})), ' | '; - my $nr_line = 0; - - if (my $l = $c->{l}) { - while ($l->[$#$l] eq "\n" && $l->[($#$l - 1)] eq "\n") { - pop @$l; - } - $nr_line = scalar @$l; - if (!$nr_line) { - print "1 line\n\n\n"; - } else { - if ($nr_line == 1) { - $nr_line = '1 line'; - } else { - $nr_line .= ' lines'; - } - print $nr_line, "\n\n"; - print $_ foreach @$l; - } - } else { - print "1 line\n\n"; - - } - foreach my $x (qw/raw diff/) { - if ($c->{$x}) { - print "\n"; - print $_ foreach @{$c->{$x}} - } - } -} - -sub libsvn_load { - return unless $_use_lib; - $_use_lib = eval { - require SVN::Core; - if ($SVN::Core::VERSION lt '1.1.0') { - die "Need SVN::Core 1.1.0 or better ", - "(got $SVN::Core::VERSION) ", - "Falling back to command-line svn\n"; - } - require SVN::Ra; - require SVN::Delta; - push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor'; - my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file. - $SVN::Node::dir.$SVN::Node::unknown. - $SVN::Node::none.$SVN::Node::file. - $SVN::Node::dir.$SVN::Node::unknown; - 1; - }; -} - -sub libsvn_connect { - my ($url) = @_; - my $auth = SVN::Core::auth_open([SVN::Client::get_simple_provider(), - SVN::Client::get_ssl_server_trust_file_provider(), - SVN::Client::get_username_provider()]); - my $s = eval { SVN::Ra->new(url => $url, auth => $auth) }; - return $s; -} - -sub libsvn_get_file { - my ($gui, $f, $rev) = @_; - my $p = $f; - return unless ($p =~ s#^\Q$SVN_PATH\E/##); - - my ($hash, $pid, $in, $out); - my $pool = SVN::Pool->new; - defined($pid = open3($in, $out, '>&STDERR', - qw/git-hash-object -w --stdin/)) or croak $!; - # redirect STDOUT for SVN 1.1.x compatibility - open my $stdout, '>&', \*STDOUT or croak $!; - open STDOUT, '>&', $in or croak $!; - my ($r, $props) = $SVN->get_file($f, $rev, \*STDOUT, $pool); - $in->flush == 0 or croak $!; - open STDOUT, '>&', $stdout or croak $!; - close $in or croak $!; - close $stdout or croak $!; - $pool->clear; - chomp($hash = do { local $/; <$out> }); - close $out or croak $!; - waitpid $pid, 0; - $hash =~ /^$sha1$/o or die "not a sha1: $hash\n"; - - my $mode = exists $props->{'svn:executable'} ? '100755' : '100644'; - if (exists $props->{'svn:special'}) { - $mode = '120000'; - my $link = `git-cat-file blob $hash`; - $link =~ s/^link // or die "svn:special file with contents: <", - $link, "> is not understood\n"; - defined($pid = open3($in, $out, '>&STDERR', - qw/git-hash-object -w --stdin/)) or croak $!; - print $in $link; - $in->flush == 0 or croak $!; - close $in or croak $!; - chomp($hash = do { local $/; <$out> }); - close $out or croak $!; - waitpid $pid, 0; - $hash =~ /^$sha1$/o or die "not a sha1: $hash\n"; - } - print $gui $mode,' ',$hash,"\t",$p,"\0" or croak $!; -} - -sub libsvn_log_entry { - my ($rev, $author, $date, $msg, $parents) = @_; - my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T - (\d\d)\:(\d\d)\:(\d\d).\d+Z$/x) - or die "Unable to parse date: $date\n"; - if (defined $_authors && ! defined $users{$author}) { - die "Author: $author not defined in $_authors file\n"; - } - return { revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S", - author => $author, msg => $msg."\n", parents => $parents || [] } -} - -sub process_rm { - my ($gui, $last_commit, $f) = @_; - $f =~ s#^\Q$SVN_PATH\E/?## or return; - # remove entire directories. - if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) { - defined(my $pid = open my $ls, '-|') or croak $!; - if (!$pid) { - exec(qw/git-ls-tree -r --name-only -z/, - $last_commit,'--',$f) or croak $!; - } - local $/ = "\0"; - while (<$ls>) { - print $gui '0 ',0 x 40,"\t",$_ or croak $!; - } - close $ls or croak $?; - } else { - print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!; - } -} - -sub libsvn_fetch { - my ($last_commit, $paths, $rev, $author, $date, $msg) = @_; - open my $gui, '| git-update-index -z --index-info' or croak $!; - my @amr; - foreach my $f (keys %$paths) { - my $m = $paths->{$f}->action(); - $f =~ s#^/+##; - if ($m =~ /^[DR]$/) { - print "\t$m\t$f\n" unless $_q; - process_rm($gui, $last_commit, $f); - next if $m eq 'D'; - # 'R' can be file replacements, too, right? - } - my $pool = SVN::Pool->new; - my $t = $SVN->check_path($f, $rev, $pool); - if ($t == $SVN::Node::file) { - if ($m =~ /^[AMR]$/) { - push @amr, [ $m, $f ]; - } else { - die "Unrecognized action: $m, ($f r$rev)\n"; - } - } - $pool->clear; - } - foreach (@amr) { - print "\t$_->[0]\t$_->[1]\n" unless $_q; - libsvn_get_file($gui, $_->[1], $rev) - } - close $gui or croak $?; - return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]); -} - -sub svn_grab_base_rev { - defined(my $pid = open my $fh, '-|') or croak $!; - if (!$pid) { - open my $null, '>', '/dev/null' or croak $!; - open STDERR, '>&', $null or croak $!; - exec qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0" - or croak $!; - } - chomp(my $c = do { local $/; <$fh> }); - close $fh; - if (defined $c && length $c) { - my ($url, $rev, $uuid) = cmt_metadata($c); - return ($rev, $c) if defined $rev; - } - if ($_no_metadata) { - my $offset = -41; # from tail - my $rl; - open my $fh, '<', $REVDB or - die "--no-metadata specified and $REVDB not readable\n"; - seek $fh, $offset, 2; - $rl = readline $fh; - defined $rl or return (undef, undef); - chomp $rl; - while ($c ne $rl && tell $fh != 0) { - $offset -= 41; - seek $fh, $offset, 2; - $rl = readline $fh; - defined $rl or return (undef, undef); - chomp $rl; - } - my $rev = tell $fh; - croak $! if ($rev < -1); - $rev = ($rev - 41) / 41; - close $fh or croak $!; - return ($rev, $c); - } - return (undef, undef); -} - -sub libsvn_parse_revision { - my $base = shift; - my $head = $SVN->get_latest_revnum(); - if (!defined $_revision || $_revision eq 'BASE:HEAD') { - return ($base + 1, $head) if (defined $base); - return (0, $head); - } - return ($1, $2) if ($_revision =~ /^(\d+):(\d+)$/); - return ($_revision, $_revision) if ($_revision =~ /^\d+$/); - if ($_revision =~ /^BASE:(\d+)$/) { - return ($base + 1, $1) if (defined $base); - return (0, $head); - } - return ($1, $head) if ($_revision =~ /^(\d+):HEAD$/); - die "revision argument: $_revision not understood by git-svn\n", - "Try using the command-line svn client instead\n"; -} - -sub libsvn_traverse { - my ($gui, $pfx, $path, $rev) = @_; - my $cwd = "$pfx/$path"; - my $pool = SVN::Pool->new; - $cwd =~ s#^/+##g; - my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool); - foreach my $d (keys %$dirent) { - my $t = $dirent->{$d}->kind; - if ($t == $SVN::Node::dir) { - libsvn_traverse($gui, $cwd, $d, $rev); - } elsif ($t == $SVN::Node::file) { - print "\tA\t$cwd/$d\n" unless $_q; - libsvn_get_file($gui, "$cwd/$d", $rev); - } - } - $pool->clear; -} - -sub libsvn_traverse_ignore { - my ($fh, $path, $r) = @_; - $path =~ s#^/+##g; - my $pool = SVN::Pool->new; - my ($dirent, undef, $props) = $SVN->get_dir($path, $r, $pool); - my $p = $path; - $p =~ s#^\Q$SVN_PATH\E/?##; - print $fh length $p ? "\n# $p\n" : "\n# /\n"; - if (my $s = $props->{'svn:ignore'}) { - $s =~ s/[\r\n]+/\n/g; - chomp $s; - if (length $p == 0) { - $s =~ s#\n#\n/$p#g; - print $fh "/$s\n"; - } else { - $s =~ s#\n#\n/$p/#g; - print $fh "/$p/$s\n"; - } - } - foreach (sort keys %$dirent) { - next if $dirent->{$_}->kind != $SVN::Node::dir; - libsvn_traverse_ignore($fh, "$path/$_", $r); - } - $pool->clear; -} - -sub revisions_eq { - my ($path, $r0, $r1) = @_; - return 1 if $r0 == $r1; - my $nr = 0; - if ($_use_lib) { - # should be OK to use Pool here (r1 - r0) should be small - my $pool = SVN::Pool->new; - libsvn_get_log($SVN, "/$path", $r0, $r1, - 0, 1, 1, sub {$nr++}, $pool); - $pool->clear; - } else { - my ($url, undef) = repo_path_split($SVN_URL); - my $svn_log = svn_log_raw("$url/$path","-r$r0:$r1"); - while (next_log_entry($svn_log)) { $nr++ } - close $svn_log->{fh}; - } - return 0 if ($nr > 1); - return 1; -} - -sub libsvn_find_parent_branch { - my ($paths, $rev, $author, $date, $msg) = @_; - my $svn_path = '/'.$SVN_PATH; - - # look for a parent from another branch: - my $i = $paths->{$svn_path} or return; - my $branch_from = $i->copyfrom_path or return; - my $r = $i->copyfrom_rev; - print STDERR "Found possible branch point: ", - "$branch_from => $svn_path, $r\n"; - $branch_from =~ s#^/##; - my $l_map = {}; - read_url_paths_all($l_map, '', "$GIT_DIR/svn"); - my $url = $SVN->{url}; - defined $l_map->{$url} or return; - my $id = $l_map->{$url}->{$branch_from}; - if (!defined $id && $_follow_parent) { - print STDERR "Following parent: $branch_from\@$r\n"; - # auto create a new branch and follow it - $id = basename($branch_from); - $id .= '@'.$r if -r "$GIT_DIR/svn/$id"; - while (-r "$GIT_DIR/svn/$id") { - # just grow a tail if we're not unique enough :x - $id .= '-'; - } - } - return unless defined $id; - - my ($r0, $parent) = find_rev_before($r,$id,1); - if ($_follow_parent && (!defined $r0 || !defined $parent)) { - defined(my $pid = fork) or croak $!; - if (!$pid) { - $GIT_SVN = $ENV{GIT_SVN_ID} = $id; - init_vars(); - $SVN_URL = "$url/$branch_from"; - $SVN_LOG = $SVN = undef; - setup_git_svn(); - # we can't assume SVN_URL exists at r+1: - $_revision = "0:$r"; - fetch_lib(); - exit 0; - } - waitpid $pid, 0; - croak $? if $?; - ($r0, $parent) = find_rev_before($r,$id,1); - } - return unless (defined $r0 && defined $parent); - if (revisions_eq($branch_from, $r0, $r)) { - unlink $GIT_SVN_INDEX; - print STDERR "Found branch parent: ($GIT_SVN) $parent\n"; - sys(qw/git-read-tree/, $parent); - return libsvn_fetch($parent, $paths, $rev, - $author, $date, $msg); - } - print STDERR "Nope, branch point not imported or unknown\n"; - return undef; -} - -sub libsvn_get_log { - my ($ra, @args) = @_; - if ($SVN::Core::VERSION le '1.2.0') { - splice(@args, 3, 1); - } - $ra->get_log(@args); -} - -sub libsvn_new_tree { - if (my $log_entry = libsvn_find_parent_branch(@_)) { - return $log_entry; - } - my ($paths, $rev, $author, $date, $msg) = @_; - open my $gui, '| git-update-index -z --index-info' or croak $!; - my $pool = SVN::Pool->new; - libsvn_traverse($gui, '', $SVN_PATH, $rev, $pool); - $pool->clear; - close $gui or croak $?; - return libsvn_log_entry($rev, $author, $date, $msg); -} - -sub find_graft_path_commit { - my ($tree_paths, $p1, $r1) = @_; - foreach my $x (keys %$tree_paths) { - next unless ($p1 =~ /^\Q$x\E/); - my $i = $tree_paths->{$x}; - my ($r0, $parent) = find_rev_before($r1,$i,1); - return $parent if (defined $r0 && $r0 == $r1); - print STDERR "r$r1 of $i not imported\n"; - next; - } - return undef; -} - -sub find_graft_path_parents { - my ($grafts, $tree_paths, $c, $p0, $r0) = @_; - foreach my $x (keys %$tree_paths) { - next unless ($p0 =~ /^\Q$x\E/); - my $i = $tree_paths->{$x}; - my ($r, $parent) = find_rev_before($r0, $i, 1); - if (defined $r && defined $parent && revisions_eq($x,$r,$r0)) { - my ($url_b, undef, $uuid_b) = cmt_metadata($c); - my ($url_a, undef, $uuid_a) = cmt_metadata($parent); - next if ($url_a && $url_b && $url_a eq $url_b && - $uuid_b eq $uuid_a); - $grafts->{$c}->{$parent} = 1; - } - } -} - -sub libsvn_graft_file_copies { - my ($grafts, $tree_paths, $path, $paths, $rev) = @_; - foreach (keys %$paths) { - my $i = $paths->{$_}; - my ($m, $p0, $r0) = ($i->action, $i->copyfrom_path, - $i->copyfrom_rev); - next unless (defined $p0 && defined $r0); - - my $p1 = $_; - $p1 =~ s#^/##; - $p0 =~ s#^/##; - my $c = find_graft_path_commit($tree_paths, $p1, $rev); - next unless $c; - find_graft_path_parents($grafts, $tree_paths, $c, $p0, $r0); - } -} - -sub set_index { - my $old = $ENV{GIT_INDEX_FILE}; - $ENV{GIT_INDEX_FILE} = shift; - return $old; -} - -sub restore_index { - my ($old) = @_; - if (defined $old) { - $ENV{GIT_INDEX_FILE} = $old; - } else { - delete $ENV{GIT_INDEX_FILE}; - } -} - -sub libsvn_commit_cb { - my ($rev, $date, $committer, $c, $msg, $r_last, $cmt_last) = @_; - if ($_optimize_commits && $rev == ($r_last + 1)) { - my $log = libsvn_log_entry($rev,$committer,$date,$msg); - $log->{tree} = get_tree_from_treeish($c); - my $cmt = git_commit($log, $cmt_last, $c); - my @diff = safe_qx('git-diff-tree', $cmt, $c); - if (@diff) { - print STDERR "Trees differ: $cmt $c\n", - join('',@diff),"\n"; - exit 1; - } - } else { - fetch("$rev=$c"); - } -} - -sub libsvn_ls_fullurl { - my $fullurl = shift; - my ($repo, $path) = repo_path_split($fullurl); - $SVN ||= libsvn_connect($repo); - my @ret; - my $pool = SVN::Pool->new; - my ($dirent, undef, undef) = $SVN->get_dir($path, - $SVN->get_latest_revnum, $pool); - foreach my $d (keys %$dirent) { - if ($dirent->{$d}->kind == $SVN::Node::dir) { - push @ret, "$d/"; # add '/' for compat with cli svn - } - } - $pool->clear; - return @ret; -} - - -sub libsvn_skip_unknown_revs { - my $err = shift; - my $errno = $err->apr_err(); - # Maybe the branch we're tracking didn't - # exist when the repo started, so it's - # not an error if it doesn't, just continue - # - # Wonderfully consistent library, eh? - # 160013 - svn:// and file:// - # 175002 - http(s):// - # More codes may be discovered later... - if ($errno == 175002 || $errno == 160013) { - return; - } - croak "Error from SVN, ($errno): ", $err->expanded_message,"\n"; -}; - -# Tie::File seems to be prone to offset errors if revisions get sparse, -# it's not that fast, either. Tie::File is also not in Perl 5.6. So -# one of my favorite modules is out :< Next up would be one of the DBM -# modules, but I'm not sure which is most portable... So I'll just -# go with something that's plain-text, but still capable of -# being randomly accessed. So here's my ultra-simple fixed-width -# database. All records are 40 characters + "\n", so it's easy to seek -# to a revision: (41 * rev) is the byte offset. -# A record of 40 0s denotes an empty revision. -# And yes, it's still pretty fast (faster than Tie::File). -sub revdb_set { - my ($file, $rev, $commit) = @_; - length $commit == 40 or croak "arg3 must be a full SHA1 hexsum\n"; - open my $fh, '+<', $file or croak $!; - my $offset = $rev * 41; - # assume that append is the common case: - seek $fh, 0, 2 or croak $!; - my $pos = tell $fh; - if ($pos < $offset) { - print $fh (('0' x 40),"\n") x (($offset - $pos) / 41); - } - seek $fh, $offset, 0 or croak $!; - print $fh $commit,"\n"; - close $fh or croak $!; -} - -sub revdb_get { - my ($file, $rev) = @_; - my $ret; - my $offset = $rev * 41; - open my $fh, '<', $file or croak $!; - seek $fh, $offset, 0; - if (tell $fh == $offset) { - $ret = readline $fh; - if (defined $ret) { - chomp $ret; - $ret = undef if ($ret =~ /^0{40}$/); - } - } - close $fh or croak $!; - return $ret; -} - -sub copy_remote_ref { - my $origin = $_cp_remote ? $_cp_remote : 'origin'; - my $ref = "refs/remotes/$GIT_SVN"; - if (safe_qx('git-ls-remote', $origin, $ref)) { - sys(qw/git fetch/, $origin, "$ref:$ref"); - } else { - die "Unable to find remote reference: ", - "refs/remotes/$GIT_SVN on $origin\n"; - } -} - -package SVN::Git::Editor; -use vars qw/@ISA/; -use strict; -use warnings; -use Carp qw/croak/; -use IO::File; - -sub new { - my $class = shift; - my $git_svn = shift; - my $self = SVN::Delta::Editor->new(@_); - bless $self, $class; - foreach (qw/svn_path c r ra /) { - die "$_ required!\n" unless (defined $git_svn->{$_}); - $self->{$_} = $git_svn->{$_}; - } - $self->{pool} = SVN::Pool->new; - $self->{bat} = { '' => $self->open_root($self->{r}, $self->{pool}) }; - $self->{rm} = { }; - require Digest::MD5; - return $self; -} - -sub split_path { - return ($_[0] =~ m#^(.*?)/?([^/]+)$#); -} - -sub repo_path { - (defined $_[1] && length $_[1]) ? "$_[0]->{svn_path}/$_[1]" - : $_[0]->{svn_path} -} - -sub url_path { - my ($self, $path) = @_; - $self->{ra}->{url} . '/' . $self->repo_path($path); -} - -sub rmdirs { - my ($self, $q) = @_; - my $rm = $self->{rm}; - delete $rm->{''}; # we never delete the url we're tracking - return unless %$rm; - - foreach (keys %$rm) { - my @d = split m#/#, $_; - my $c = shift @d; - $rm->{$c} = 1; - while (@d) { - $c .= '/' . shift @d; - $rm->{$c} = 1; - } - } - delete $rm->{$self->{svn_path}}; - delete $rm->{''}; # we never delete the url we're tracking - return unless %$rm; - - defined(my $pid = open my $fh,'-|') or croak $!; - if (!$pid) { - exec qw/git-ls-tree --name-only -r -z/, $self->{c} or croak $!; - } - local $/ = "\0"; - my @svn_path = split m#/#, $self->{svn_path}; - while (<$fh>) { - chomp; - my @dn = (@svn_path, (split m#/#, $_)); - while (pop @dn) { - delete $rm->{join '/', @dn}; - } - unless (%$rm) { - close $fh; - return; - } - } - close $fh; - - my ($r, $p, $bat) = ($self->{r}, $self->{pool}, $self->{bat}); - foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) { - $self->close_directory($bat->{$d}, $p); - my ($dn) = ($d =~ m#^(.*?)/?(?:[^/]+)$#); - print "\tD+\t/$d/\n" unless $q; - $self->SUPER::delete_entry($d, $r, $bat->{$dn}, $p); - delete $bat->{$d}; - } -} - -sub open_or_add_dir { - my ($self, $full_path, $baton) = @_; - my $p = SVN::Pool->new; - my $t = $self->{ra}->check_path($full_path, $self->{r}, $p); - $p->clear; - if ($t == $SVN::Node::none) { - return $self->add_directory($full_path, $baton, - undef, -1, $self->{pool}); - } elsif ($t == $SVN::Node::dir) { - return $self->open_directory($full_path, $baton, - $self->{r}, $self->{pool}); - } - print STDERR "$full_path already exists in repository at ", - "r$self->{r} and it is not a directory (", - ($t == $SVN::Node::file ? 'file' : 'unknown'),"/$t)\n"; - exit 1; -} - -sub ensure_path { - my ($self, $path) = @_; - my $bat = $self->{bat}; - $path = $self->repo_path($path); - return $bat->{''} unless (length $path); - my @p = split m#/+#, $path; - my $c = shift @p; - $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{''}); - while (@p) { - my $c0 = $c; - $c .= '/' . shift @p; - $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{$c0}); - } - return $bat->{$c}; -} - -sub A { - my ($self, $m, $q) = @_; - my ($dir, $file) = split_path($m->{file_b}); - my $pbat = $self->ensure_path($dir); - my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, - undef, -1); - print "\tA\t$m->{file_b}\n" unless $q; - $self->chg_file($fbat, $m); - $self->close_file($fbat,undef,$self->{pool}); -} - -sub C { - my ($self, $m, $q) = @_; - my ($dir, $file) = split_path($m->{file_b}); - my $pbat = $self->ensure_path($dir); - my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, - $self->url_path($m->{file_a}), $self->{r}); - print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $q; - $self->chg_file($fbat, $m); - $self->close_file($fbat,undef,$self->{pool}); -} - -sub delete_entry { - my ($self, $path, $pbat) = @_; - my $rpath = $self->repo_path($path); - my ($dir, $file) = split_path($rpath); - $self->{rm}->{$dir} = 1; - $self->SUPER::delete_entry($rpath, $self->{r}, $pbat, $self->{pool}); -} - -sub R { - my ($self, $m, $q) = @_; - my ($dir, $file) = split_path($m->{file_b}); - my $pbat = $self->ensure_path($dir); - my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, - $self->url_path($m->{file_a}), $self->{r}); - print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $q; - $self->chg_file($fbat, $m); - $self->close_file($fbat,undef,$self->{pool}); - - ($dir, $file) = split_path($m->{file_a}); - $pbat = $self->ensure_path($dir); - $self->delete_entry($m->{file_a}, $pbat); -} - -sub M { - my ($self, $m, $q) = @_; - my ($dir, $file) = split_path($m->{file_b}); - my $pbat = $self->ensure_path($dir); - my $fbat = $self->open_file($self->repo_path($m->{file_b}), - $pbat,$self->{r},$self->{pool}); - print "\t$m->{chg}\t$m->{file_b}\n" unless $q; - $self->chg_file($fbat, $m); - $self->close_file($fbat,undef,$self->{pool}); -} - -sub T { shift->M(@_) } - -sub change_file_prop { - my ($self, $fbat, $pname, $pval) = @_; - $self->SUPER::change_file_prop($fbat, $pname, $pval, $self->{pool}); -} - -sub chg_file { - my ($self, $fbat, $m) = @_; - if ($m->{mode_b} =~ /755$/ && $m->{mode_a} !~ /755$/) { - $self->change_file_prop($fbat,'svn:executable','*'); - } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) { - $self->change_file_prop($fbat,'svn:executable',undef); - } - my $fh = IO::File->new_tmpfile or croak $!; - if ($m->{mode_b} =~ /^120/) { - print $fh 'link ' or croak $!; - $self->change_file_prop($fbat,'svn:special','*'); - } elsif ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) { - $self->change_file_prop($fbat,'svn:special',undef); - } - defined(my $pid = fork) or croak $!; - if (!$pid) { - open STDOUT, '>&', $fh or croak $!; - exec qw/git-cat-file blob/, $m->{sha1_b} or croak $!; - } - waitpid $pid, 0; - croak $? if $?; - $fh->flush == 0 or croak $!; - seek $fh, 0, 0 or croak $!; - - my $md5 = Digest::MD5->new; - $md5->addfile($fh) or croak $!; - seek $fh, 0, 0 or croak $!; - - my $exp = $md5->hexdigest; - my $atd = $self->apply_textdelta($fbat, undef, $self->{pool}); - my $got = SVN::TxDelta::send_stream($fh, @$atd, $self->{pool}); - die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp); - - close $fh or croak $!; -} - -sub D { - my ($self, $m, $q) = @_; - my ($dir, $file) = split_path($m->{file_b}); - my $pbat = $self->ensure_path($dir); - print "\tD\t$m->{file_b}\n" unless $q; - $self->delete_entry($m->{file_b}, $pbat); -} - -sub close_edit { - my ($self) = @_; - my ($p,$bat) = ($self->{pool}, $self->{bat}); - foreach (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$bat) { - $self->close_directory($bat->{$_}, $p); - } - $self->SUPER::close_edit($p); - $p->clear; -} - -sub abort_edit { - my ($self) = @_; - $self->SUPER::abort_edit($self->{pool}); - $self->{pool}->clear; -} - -__END__ - -Data structures: - -$svn_log hashref (as returned by svn_log_raw) -{ - fh => file handle of the log file, - state => state of the log file parser (sep/msg/rev/msg_start...) -} - -$log_msg hashref as returned by next_log_entry($svn_log) -{ - msg => 'whitespace-formatted log entry -', # trailing newline is preserved - revision => '8', # integer - date => '2004-02-24T17:01:44.108345Z', # commit date - author => 'committer name' -}; - - -@mods = array of diff-index line hashes, each element represents one line - of diff-index output - -diff-index line ($m hash) -{ - mode_a => first column of diff-index output, no leading ':', - mode_b => second column of diff-index output, - sha1_b => sha1sum of the final blob, - chg => change type [MCRADT], - file_a => original file name of a file (iff chg is 'C' or 'R') - file_b => new/current file name of a file (any chg) -} -; - -# retval of read_url_paths{,_all}(); -$l_map = { - # repository root url - 'https://svn.musicpd.org' => { - # repository path # GIT_SVN_ID - 'mpd/trunk' => 'trunk', - 'mpd/tags/0.11.5' => 'tags/0.11.5', - }, -} - -Notes: - I don't trust the each() function on unless I created %hash myself - because the internal iterator may not have started at base. diff --git a/contrib/git-svn/git-svn.txt b/contrib/git-svn/git-svn.txt deleted file mode 100644 index f7d3de48f0..0000000000 --- a/contrib/git-svn/git-svn.txt +++ /dev/null @@ -1,319 +0,0 @@ -git-svn(1) -========== - -NAME ----- -git-svn - bidirectional operation between a single Subversion branch and git - -SYNOPSIS --------- -'git-svn' [options] [arguments] - -DESCRIPTION ------------ -git-svn is a simple conduit for changesets between a single Subversion -branch and git. - -git-svn is not to be confused with git-svnimport. The were designed -with very different goals in mind. - -git-svn is designed for an individual developer who wants a -bidirectional flow of changesets between a single branch in Subversion -and an arbitrary number of branches in git. git-svnimport is designed -for read-only operation on repositories that match a particular layout -(albeit the recommended one by SVN developers). - -For importing svn, git-svnimport is potentially more powerful when -operating on repositories organized under the recommended -trunk/branch/tags structure, and should be faster, too. - -git-svn mostly ignores the very limited view of branching that -Subversion has. This allows git-svn to be much easier to use, -especially on repositories that are not organized in a manner that -git-svnimport is designed for. - -COMMANDS --------- -init:: - Creates an empty git repository with additional metadata - directories for git-svn. The Subversion URL must be specified - as a command-line argument. - -fetch:: - Fetch unfetched revisions from the Subversion URL we are - tracking. refs/remotes/git-svn will be updated to the - latest revision. - - Note: You should never attempt to modify the remotes/git-svn - branch outside of git-svn. Instead, create a branch from - remotes/git-svn and work on that branch. Use the 'commit' - command (see below) to write git commits back to - remotes/git-svn. - - See 'Additional Fetch Arguments' if you are interested in - manually joining branches on commit. - -commit:: - Commit specified commit or tree objects to SVN. This relies on - your imported fetch data being up-to-date. This makes - absolutely no attempts to do patching when committing to SVN, it - simply overwrites files with those specified in the tree or - commit. All merging is assumed to have taken place - independently of git-svn functions. - -rebuild:: - Not a part of daily usage, but this is a useful command if - you've just cloned a repository (using git-clone) that was - tracked with git-svn. Unfortunately, git-clone does not clone - git-svn metadata and the svn working tree that git-svn uses for - its operations. This rebuilds the metadata so git-svn can - resume fetch operations. A Subversion URL may be optionally - specified at the command-line if the directory/repository you're - tracking has moved or changed protocols. - -show-ignore:: - Recursively finds and lists the svn:ignore property on - directories. The output is suitable for appending to - the $GIT_DIR/info/exclude file. - -OPTIONS -------- --r :: ---revision :: - Only used with the 'fetch' command. - - Takes any valid -r svn would accept and passes it - directly to svn. -r: ranges and "{" DATE "}" syntax - is also supported. This is passed directly to svn, see svn - documentation for more details. - - This can allow you to make partial mirrors when running fetch. - --:: ---stdin:: - Only used with the 'commit' command. - - Read a list of commits from stdin and commit them in reverse - order. Only the leading sha1 is read from each line, so - git-rev-list --pretty=oneline output can be used. - ---rmdir:: - Only used with the 'commit' command. - - Remove directories from the SVN tree if there are no files left - behind. SVN can version empty directories, and they are not - removed by default if there are no files left in them. git - cannot version empty directories. Enabling this flag will make - the commit to SVN act like git. - - repo-config key: svn.rmdir - --e:: ---edit:: - Only used with the 'commit' command. - - Edit the commit message before committing to SVN. This is off by - default for objects that are commits, and forced on when committing - tree objects. - - repo-config key: svn.edit - --l:: ---find-copies-harder:: - Both of these are only used with the 'commit' command. - - They are both passed directly to git-diff-tree see - git-diff-tree(1) for more information. - - repo-config key: svn.l - repo-config key: svn.findcopiesharder - --A:: ---authors-file=:: - - Syntax is compatible with the files used by git-svnimport and - git-cvsimport: - ------------------------------------------------------------------------- -loginname = Joe User ------------------------------------------------------------------------- - - If this option is specified and git-svn encounters an SVN - committer name that does not exist in the authors-file, git-svn - will abort operation. The user will then have to add the - appropriate entry. Re-running the previous git-svn command - after the authors-file is modified should continue operation. - - repo-config key: svn.authors-file - -ADVANCED OPTIONS ----------------- --b:: ---branch :: - Used with 'fetch' or 'commit'. - - This can be used to join arbitrary git branches to remotes/git-svn - on new commits where the tree object is equivalent. - - When used with different GIT_SVN_ID values, tags and branches in - SVN can be tracked this way, as can some merges where the heads - end up having completely equivalent content. This can even be - used to track branches across multiple SVN _repositories_. - - This option may be specified multiple times, once for each - branch. - - repo-config key: svn.branch - --i:: ---id :: - This sets GIT_SVN_ID (instead of using the environment). See - the section on "Tracking Multiple Repositories or Branches" for - more information on using GIT_SVN_ID. - -COMPATIBILITY OPTIONS ---------------------- ---upgrade:: - Only used with the 'rebuild' command. - - Run this if you used an old version of git-svn that used - "git-svn-HEAD" instead of "remotes/git-svn" as the branch - for tracking the remote. - ---no-ignore-externals:: - Only used with the 'fetch' and 'rebuild' command. - - By default, git-svn passes --ignore-externals to svn to avoid - fetching svn:external trees into git. Pass this flag to enable - externals tracking directly via git. - - Versions of svn that do not support --ignore-externals are - automatically detected and this flag will be automatically - enabled for them. - - Otherwise, do not enable this flag unless you know what you're - doing. - - repo-config key: svn.noignoreexternals - -Basic Examples -~~~~~~~~~~~~~~ - -Tracking and contributing to an Subversion managed-project: - ------------------------------------------------------------------------- -# Initialize a tree (like git init-db): - git-svn init http://svn.foo.org/project/trunk -# Fetch remote revisions: - git-svn fetch -# Create your own branch to hack on: - git checkout -b my-branch remotes/git-svn -# Commit only the git commits you want to SVN: - git-svn commit [ ...] -# Commit all the git commits from my-branch that don't exist in SVN: - git-svn commit remotes/git-svn..my-branch -# Something is committed to SVN, pull the latest into your branch: - git-svn fetch && git pull . remotes/git-svn -# Append svn:ignore settings to the default git exclude file: - git-svn show-ignore >> .git/info/exclude ------------------------------------------------------------------------- - -DESIGN PHILOSOPHY ------------------ -Merge tracking in Subversion is lacking and doing branched development -with Subversion is cumbersome as a result. git-svn completely forgoes -any automated merge/branch tracking on the Subversion side and leaves it -entirely up to the user on the git side. It's simply not worth it to do -a useful translation when the the original signal is weak. - -TRACKING MULTIPLE REPOSITORIES OR BRANCHES ------------------------------------------- -This is for advanced users, most users should ignore this section. - -Because git-svn does not care about relationships between different -branches or directories in a Subversion repository, git-svn has a simple -hack to allow it to track an arbitrary number of related _or_ unrelated -SVN repositories via one git repository. Simply set the GIT_SVN_ID -environment variable to a name other other than "git-svn" (the default) -and git-svn will ignore the contents of the $GIT_DIR/git-svn directory -and instead do all of its work in $GIT_DIR/$GIT_SVN_ID for that -invocation. The interface branch will be remotes/$GIT_SVN_ID, instead of -remotes/git-svn. Any remotes/$GIT_SVN_ID branch should never be modified -by the user outside of git-svn commands. - -ADDITIONAL FETCH ARGUMENTS --------------------------- -This is for advanced users, most users should ignore this section. - -Unfetched SVN revisions may be imported as children of existing commits -by specifying additional arguments to 'fetch'. Additional parents may -optionally be specified in the form of sha1 hex sums at the -command-line. Unfetched SVN revisions may also be tied to particular -git commits with the following syntax: - - svn_revision_number=git_commit_sha1 - -This allows you to tie unfetched SVN revision 375 to your current HEAD:: - - `git-svn fetch 375=$(git-rev-parse HEAD)` - -Advanced Example: Tracking a Reorganized Repository -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you're tracking a directory that has moved, or otherwise been -branched or tagged off of another directory in the repository and you -care about the full history of the project, then you can read this -section. - -This is how Yann Dirson tracked the trunk of the ufoai directory when -the /trunk directory of his repository was moved to /ufoai/trunk and -he needed to continue tracking /ufoai/trunk where /trunk left off. - ------------------------------------------------------------------------- - # This log message shows when the repository was reorganized: - r166 | ydirson | 2006-03-02 01:36:55 +0100 (Thu, 02 Mar 2006) | 1 line - Changed paths: - D /trunk - A /ufoai/trunk (from /trunk:165) - - # First we start tracking the old revisions: - GIT_SVN_ID=git-oldsvn git-svn init \ - https://svn.sourceforge.net/svnroot/ufoai/trunk - GIT_SVN_ID=git-oldsvn git-svn fetch -r1:165 - - # And now, we continue tracking the new revisions: - GIT_SVN_ID=git-newsvn git-svn init \ - https://svn.sourceforge.net/svnroot/ufoai/ufoai/trunk - GIT_SVN_ID=git-newsvn git-svn fetch \ - 166=`git-rev-parse refs/remotes/git-oldsvn` ------------------------------------------------------------------------- - -BUGS ----- -If somebody commits a conflicting changeset to SVN at a bad moment -(right before you commit) causing a conflict and your commit to fail, -your svn working tree ($GIT_DIR/git-svn/tree) may be dirtied. The -easiest thing to do is probably just to rm -rf $GIT_DIR/git-svn/tree and -run 'rebuild'. - -We ignore all SVN properties except svn:executable. Too difficult to -map them since we rely heavily on git write-tree being _exactly_ the -same on both the SVN and git working trees and I prefer not to clutter -working trees with metadata files. - -svn:keywords can't be ignored in Subversion (at least I don't know of -a way to ignore them). - -Renamed and copied directories are not detected by git and hence not -tracked when committing to SVN. I do not plan on adding support for -this as it's quite difficult and time-consuming to get working for all -the possible corner cases (git doesn't do it, either). Renamed and -copied files are fully supported if they're similar enough for git to -detect them. - -Author ------- -Written by Eric Wong . - -Documentation -------------- -Written by Eric Wong . diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh deleted file mode 100644 index d7f972a0c8..0000000000 --- a/contrib/git-svn/t/lib-git-svn.sh +++ /dev/null @@ -1,45 +0,0 @@ -PATH=$PWD/../:$PATH -if test -d ../../../t -then - cd ../../../t -else - echo "Must be run in contrib/git-svn/t" >&2 - exit 1 -fi - -. ./test-lib.sh - -GIT_DIR=$PWD/.git -GIT_SVN_DIR=$GIT_DIR/svn/git-svn -SVN_TREE=$GIT_SVN_DIR/svn-tree - -svnadmin >/dev/null 2>&1 -if test $? != 1 -then - test_expect_success 'skipping contrib/git-svn test' : - test_done - exit -fi - -svn >/dev/null 2>&1 -if test $? != 1 -then - test_expect_success 'skipping contrib/git-svn test' : - test_done - exit -fi - -svnrepo=$PWD/svnrepo - -set -e - -if svnadmin create --help | grep fs-type >/dev/null -then - svnadmin create --fs-type fsfs "$svnrepo" -else - svnadmin create "$svnrepo" -fi - -svnrepo="file://$svnrepo/test-git-svn" - - diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/contrib/git-svn/t/t0000-contrib-git-svn.sh deleted file mode 100644 index b482bb64c0..0000000000 --- a/contrib/git-svn/t/t0000-contrib-git-svn.sh +++ /dev/null @@ -1,232 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2006 Eric Wong -# - -test_description='git-svn tests' -GIT_SVN_LC_ALL=$LC_ALL - -case "$LC_ALL" in -*.UTF-8) - have_utf8=t - ;; -*) - have_utf8= - ;; -esac - -. ./lib-git-svn.sh - -mkdir import -cd import - -echo foo > foo -if test -z "$NO_SYMLINK" -then - ln -s foo foo.link -fi -mkdir -p dir/a/b/c/d/e -echo 'deep dir' > dir/a/b/c/d/e/file -mkdir -p bar -echo 'zzz' > bar/zzz -echo '#!/bin/sh' > exec.sh -chmod +x exec.sh -svn import -m 'import for git-svn' . "$svnrepo" >/dev/null - -cd .. -rm -rf import - -test_expect_success \ - 'initialize git-svn' \ - "git-svn init $svnrepo" - -test_expect_success \ - 'import an SVN revision into git' \ - 'git-svn fetch' - -test_expect_success "checkout from svn" "svn co $svnrepo $SVN_TREE" - -name='try a deep --rmdir with a commit' -git checkout -f -b mybranch remotes/git-svn -mv dir/a/b/c/d/e/file dir/file -cp dir/file file -git update-index --add --remove dir/a/b/c/d/e/file dir/file file -git commit -m "$name" - -test_expect_success "$name" \ - "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch && - svn up $SVN_TREE && - test -d $SVN_TREE/dir && test ! -d $SVN_TREE/dir/a" - - -name='detect node change from file to directory #1' -mkdir dir/new_file -mv dir/file dir/new_file/file -mv dir/new_file dir/file -git update-index --remove dir/file -git update-index --add dir/file/file -git commit -m "$name" - -test_expect_failure "$name" \ - 'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch' \ - || true - - -name='detect node change from directory to file #1' -rm -rf dir $GIT_DIR/index -git checkout -f -b mybranch2 remotes/git-svn -mv bar/zzz zzz -rm -rf bar -mv zzz bar -git update-index --remove -- bar/zzz -git update-index --add -- bar -git commit -m "$name" - -test_expect_failure "$name" \ - 'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch2' \ - || true - - -name='detect node change from file to directory #2' -rm -f $GIT_DIR/index -git checkout -f -b mybranch3 remotes/git-svn -rm bar/zzz -git-update-index --remove bar/zzz -mkdir bar/zzz -echo yyy > bar/zzz/yyy -git-update-index --add bar/zzz/yyy -git commit -m "$name" - -test_expect_failure "$name" \ - 'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch3' \ - || true - - -name='detect node change from directory to file #2' -rm -f $GIT_DIR/index -git checkout -f -b mybranch4 remotes/git-svn -rm -rf dir -git update-index --remove -- dir/file -touch dir -echo asdf > dir -git update-index --add -- dir -git commit -m "$name" - -test_expect_failure "$name" \ - 'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch4' \ - || true - - -name='remove executable bit from a file' -rm -f $GIT_DIR/index -git checkout -f -b mybranch5 remotes/git-svn -chmod -x exec.sh -git update-index exec.sh -git commit -m "$name" - -test_expect_success "$name" \ - "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && - svn up $SVN_TREE && - test ! -x $SVN_TREE/exec.sh" - - -name='add executable bit back file' -chmod +x exec.sh -git update-index exec.sh -git commit -m "$name" - -test_expect_success "$name" \ - "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && - svn up $SVN_TREE && - test -x $SVN_TREE/exec.sh" - - - -if test -z "$NO_SYMLINK" -then - name='executable file becomes a symlink to bar/zzz (file)' - rm exec.sh - ln -s bar/zzz exec.sh - git update-index exec.sh - git commit -m "$name" - - test_expect_success "$name" \ - "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && - svn up $SVN_TREE && - test -L $SVN_TREE/exec.sh" - - name='new symlink is added to a file that was also just made executable' - chmod +x bar/zzz - ln -s bar/zzz exec-2.sh - git update-index --add bar/zzz exec-2.sh - git commit -m "$name" - - test_expect_success "$name" \ - "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && - svn up $SVN_TREE && - test -x $SVN_TREE/bar/zzz && - test -L $SVN_TREE/exec-2.sh" - - name='modify a symlink to become a file' - git help > help || true - rm exec-2.sh - cp help exec-2.sh - git update-index exec-2.sh - git commit -m "$name" - - test_expect_success "$name" \ - "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 && - svn up $SVN_TREE && - test -f $SVN_TREE/exec-2.sh && - test ! -L $SVN_TREE/exec-2.sh && - diff -u help $SVN_TREE/exec-2.sh" -fi - - -if test "$have_utf8" = t -then - name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL" - echo '# hello' >> exec-2.sh - git update-index exec-2.sh - git commit -m 'éï∏' - export LC_ALL="$GIT_SVN_LC_ALL" - test_expect_success "$name" "git-svn commit HEAD" - unset LC_ALL -else - echo "UTF-8 locale not set, test skipped ($GIT_SVN_LC_ALL)" -fi - -name='test fetch functionality (svn => git) with alternate GIT_SVN_ID' -GIT_SVN_ID=alt -export GIT_SVN_ID -test_expect_success "$name" \ - "git-svn init $svnrepo && git-svn fetch && - git-rev-list --pretty=raw remotes/git-svn | grep ^tree | uniq > a && - git-rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b && - diff -u a b" - -if test -n "$NO_SYMLINK" -then - test_done - exit 0 -fi - -name='check imported tree checksums expected tree checksums' -rm -f expected -if test "$have_utf8" = t -then - echo tree f735671b89a7eb30cab1d8597de35bd4271ab813 > expected -fi -cat >> expected <<\EOF -tree 4b9af72bb861eaed053854ec502cf7df72618f0f -tree 031b8d557afc6fea52894eaebb45bec52f1ba6d1 -tree 0b094cbff17168f24c302e297f55bfac65eb8bd3 -tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e -tree 56a30b966619b863674f5978696f4a3594f2fca9 -tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e -tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4 -EOF -test_expect_success "$name" "diff -u a expected" - -test_done - diff --git a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh b/contrib/git-svn/t/t0001-contrib-git-svn-props.sh deleted file mode 100644 index a5a235f100..0000000000 --- a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2006 Eric Wong -# - -test_description='git-svn property tests' -. ./lib-git-svn.sh - -mkdir import - -a_crlf= -a_lf= -a_cr= -a_ne_crlf= -a_ne_lf= -a_ne_cr= -a_empty= -a_empty_lf= -a_empty_cr= -a_empty_crlf= - -cd import - cat >> kw.c <<\EOF -/* Somebody prematurely put a keyword into this file */ -/* $Id$ */ -EOF - - printf "Hello\r\nWorld\r\n" > crlf - a_crlf=`git-hash-object -w crlf` - printf "Hello\rWorld\r" > cr - a_cr=`git-hash-object -w cr` - printf "Hello\nWorld\n" > lf - a_lf=`git-hash-object -w lf` - - printf "Hello\r\nWorld" > ne_crlf - a_ne_crlf=`git-hash-object -w ne_crlf` - printf "Hello\nWorld" > ne_lf - a_ne_lf=`git-hash-object -w ne_lf` - printf "Hello\rWorld" > ne_cr - a_ne_cr=`git-hash-object -w ne_cr` - - touch empty - a_empty=`git-hash-object -w empty` - printf "\n" > empty_lf - a_empty_lf=`git-hash-object -w empty_lf` - printf "\r" > empty_cr - a_empty_cr=`git-hash-object -w empty_cr` - printf "\r\n" > empty_crlf - a_empty_crlf=`git-hash-object -w empty_crlf` - - svn import -m 'import for git-svn' . "$svnrepo" >/dev/null -cd .. - -rm -rf import -test_expect_success 'checkout working copy from svn' "svn co $svnrepo test_wc" -test_expect_success 'setup some commits to svn' \ - 'cd test_wc && - echo Greetings >> kw.c && - svn commit -m "Not yet an Id" && - svn up && - echo Hello world >> kw.c && - svn commit -m "Modified file, but still not yet an Id" && - svn up && - svn propset svn:keywords Id kw.c && - svn commit -m "Propset Id" && - svn up && - cd ..' - -test_expect_success 'initialize git-svn' "git-svn init $svnrepo" -test_expect_success 'fetch revisions from svn' 'git-svn fetch' - -name='test svn:keywords ignoring' -test_expect_success "$name" \ - 'git checkout -b mybranch remotes/git-svn && - echo Hi again >> kw.c && - git commit -a -m "test keywoards ignoring" && - git-svn commit remotes/git-svn..mybranch && - git pull . remotes/git-svn' - -expect='/* $Id$ */' -got="`sed -ne 2p kw.c`" -test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'" - -test_expect_success "propset CR on crlf files" \ - 'cd test_wc && - svn propset svn:eol-style CR empty && - svn propset svn:eol-style CR crlf && - svn propset svn:eol-style CR ne_crlf && - svn commit -m "propset CR on crlf files" && - svn up && - cd ..' - -test_expect_success 'fetch and pull latest from svn and checkout a new wc' \ - "git-svn fetch && - git pull . remotes/git-svn && - svn co $svnrepo new_wc" - -for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf -do - test_expect_success "Comparing $i" "cmp $i new_wc/$i" -done - - -cd test_wc - printf '$Id$\rHello\rWorld\r' > cr - printf '$Id$\rHello\rWorld' > ne_cr - a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git-hash-object --stdin` - a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git-hash-object --stdin` - test_expect_success 'Set CRLF on cr files' \ - 'svn propset svn:eol-style CRLF cr && - svn propset svn:eol-style CRLF ne_cr && - svn propset svn:keywords Id cr && - svn propset svn:keywords Id ne_cr && - svn commit -m "propset CRLF on cr files" && - svn up' -cd .. -test_expect_success 'fetch and pull latest from svn' \ - 'git-svn fetch && git pull . remotes/git-svn' - -b_cr="`git-hash-object cr`" -b_ne_cr="`git-hash-object ne_cr`" - -test_expect_success 'CRLF + $Id$' "test '$a_cr' = '$b_cr'" -test_expect_success 'CRLF + $Id$ (no newline)' "test '$a_ne_cr' = '$b_ne_cr'" - -test_done diff --git a/contrib/git-svn/t/t0002-deep-rmdir.sh b/contrib/git-svn/t/t0002-deep-rmdir.sh deleted file mode 100644 index d693d183c8..0000000000 --- a/contrib/git-svn/t/t0002-deep-rmdir.sh +++ /dev/null @@ -1,29 +0,0 @@ -test_description='git-svn rmdir' -. ./lib-git-svn.sh - -test_expect_success 'initialize repo' " - mkdir import && - cd import && - mkdir -p deeply/nested/directory/number/1 && - mkdir -p deeply/nested/directory/number/2 && - echo foo > deeply/nested/directory/number/1/file && - echo foo > deeply/nested/directory/number/2/another && - svn import -m 'import for git-svn' . $svnrepo && - cd .. - " - -test_expect_success 'mirror via git-svn' " - git-svn init $svnrepo && - git-svn fetch && - git checkout -f -b test-rmdir remotes/git-svn - " - -test_expect_success 'Try a commit on rmdir' " - git rm -f deeply/nested/directory/number/2/another && - git commit -a -m 'remove another' && - git-svn commit --rmdir HEAD && - svn ls -R $svnrepo | grep ^deeply/nested/directory/number/1 - " - - -test_done diff --git a/contrib/git-svn/t/t0003-graft-branches.sh b/contrib/git-svn/t/t0003-graft-branches.sh deleted file mode 100644 index cc62d4ece8..0000000000 --- a/contrib/git-svn/t/t0003-graft-branches.sh +++ /dev/null @@ -1,63 +0,0 @@ -test_description='git-svn graft-branches' -. ./lib-git-svn.sh - -test_expect_success 'initialize repo' " - mkdir import && - cd import && - mkdir -p trunk branches tags && - echo hello > trunk/readme && - svn import -m 'import for git-svn' . $svnrepo && - cd .. && - svn cp -m 'tag a' $svnrepo/trunk $svnrepo/tags/a && - svn cp -m 'branch a' $svnrepo/trunk $svnrepo/branches/a && - svn co $svnrepo wc && - cd wc && - echo feedme >> branches/a/readme && - svn commit -m hungry && - svn up && - cd trunk && - svn merge -r3:4 $svnrepo/branches/a && - svn commit -m 'merge with a' && - cd ../.. && - svn log -v $svnrepo && - git-svn init -i trunk $svnrepo/trunk && - git-svn init -i a $svnrepo/branches/a && - git-svn init -i tags/a $svnrepo/tags/a && - git-svn fetch -i tags/a && - git-svn fetch -i a && - git-svn fetch -i trunk - " - -r1=`git-rev-list remotes/trunk | tail -n1` -r2=`git-rev-list remotes/tags/a | tail -n1` -r3=`git-rev-list remotes/a | tail -n1` -r4=`git-rev-list remotes/a | head -n1` -r5=`git-rev-list remotes/trunk | head -n1` - -test_expect_success 'test graft-branches regexes and copies' " - test -n "$r1" && - test -n "$r2" && - test -n "$r3" && - test -n "$r4" && - test -n "$r5" && - git-svn graft-branches && - grep '^$r2 $r1' $GIT_DIR/info/grafts && - grep '^$r3 $r1' $GIT_DIR/info/grafts && - grep '^$r5 ' $GIT_DIR/info/grafts | grep '$r4' | grep '$r1' - " - -test_debug 'gitk --all & sleep 1' - -test_expect_success 'test graft-branches with tree-joins' " - rm $GIT_DIR/info/grafts && - git-svn graft-branches --no-default-regex --no-graft-copy -B && - grep '^$r3 ' $GIT_DIR/info/grafts | grep '$r1' | grep '$r2' && - grep '^$r2 $r1' $GIT_DIR/info/grafts && - grep '^$r5 ' $GIT_DIR/info/grafts | grep '$r1' | grep '$r4' - " - -# the result of this is kinda funky, we have a strange history and -# this is just a test :) -test_debug 'gitk --all &' - -test_done diff --git a/contrib/git-svn/t/t0004-follow-parent.sh b/contrib/git-svn/t/t0004-follow-parent.sh deleted file mode 100644 index 01488ff78a..0000000000 --- a/contrib/git-svn/t/t0004-follow-parent.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2006 Eric Wong -# - -test_description='git-svn --follow-parent fetching' -. ./lib-git-svn.sh - -if test -n "$GIT_SVN_NO_LIB" && test "$GIT_SVN_NO_LIB" -ne 0 -then - echo 'Skipping: --follow-parent needs SVN libraries' - test_done - exit 0 -fi - -test_expect_success 'initialize repo' " - mkdir import && - cd import && - mkdir -p trunk && - echo hello > trunk/readme && - svn import -m 'initial' . $svnrepo && - cd .. && - svn co $svnrepo wc && - cd wc && - echo world >> trunk/readme && - svn commit -m 'another commit' && - svn up && - svn mv -m 'rename to thunk' trunk thunk && - svn up && - echo goodbye >> thunk/readme && - svn commit -m 'bye now' && - cd .. - " - -test_expect_success 'init and fetch --follow-parent a moved directory' " - git-svn init -i thunk $svnrepo/thunk && - git-svn fetch --follow-parent -i thunk && - git-rev-parse --verify refs/remotes/trunk && - test '$?' -eq '0' - " - -test_debug 'gitk --all &' - -test_done diff --git a/contrib/git-svn/t/t0005-commit-diff.sh b/contrib/git-svn/t/t0005-commit-diff.sh deleted file mode 100644 index f994b72f80..0000000000 --- a/contrib/git-svn/t/t0005-commit-diff.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2006 Eric Wong -test_description='git-svn commit-diff' -. ./lib-git-svn.sh - -if test -n "$GIT_SVN_NO_LIB" && test "$GIT_SVN_NO_LIB" -ne 0 -then - echo 'Skipping: commit-diff needs SVN libraries' - test_done - exit 0 -fi - -test_expect_success 'initialize repo' " - mkdir import && - cd import && - echo hello > readme && - svn import -m 'initial' . $svnrepo && - cd .. && - echo hello > readme && - git update-index --add readme && - git commit -a -m 'initial' && - echo world >> readme && - git commit -a -m 'another' - " - -head=`git rev-parse --verify HEAD^0` -prev=`git rev-parse --verify HEAD^1` - -# the internals of the commit-diff command are the same as the regular -# commit, so only a basic test of functionality is needed since we've -# already tested commit extensively elsewhere - -test_expect_success 'test the commit-diff command' " - test -n '$prev' && test -n '$head' && - git-svn commit-diff '$prev' '$head' '$svnrepo' && - svn co $svnrepo wc && - cmp readme wc/readme - " - -test_done -- cgit v1.2.3 From 82e5a82fd73edb80a841f5fab1660e14b9b8f3ad Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Mon, 10 Jul 2006 01:50:18 -0400 Subject: Fix more typos, primarily in the code The only visible change is that git-blame doesn't understand "--compability" anymore, but it does accept "--compatibility" instead, which is already documented. Signed-off-by: Pavel Roskin Signed-off-by: Junio C Hamano --- contrib/colordiff/colordiff.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/colordiff/colordiff.perl b/contrib/colordiff/colordiff.perl index 5789cfb265..9566a765ef 100755 --- a/contrib/colordiff/colordiff.perl +++ b/contrib/colordiff/colordiff.perl @@ -110,7 +110,7 @@ foreach $config_file (@config_files) { } } -# colordiff specfic options here. Need to pre-declare if using variables +# colordiff specific options here. Need to pre-declare if using variables GetOptions( "no-banner" => sub { $show_banner = 0 }, "plain-text=s" => \&set_color, -- cgit v1.2.3 From 1b3a667461c7b85068d38eb50fa60529959855fb Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 13 Jul 2006 22:22:13 +0200 Subject: Wrap long lines in docstrings in contrib/emacs/git.el Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index ebd00ef9c4..83a845dd94 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -59,14 +59,16 @@ (defcustom git-committer-name nil "User name to use for commits. -The default is to fall back to the repository config, then to `add-log-full-name' and then to `user-full-name'." +The default is to fall back to the repository config, +then to `add-log-full-name' and then to `user-full-name'." :group 'git :type '(choice (const :tag "Default" nil) (string :tag "Name"))) (defcustom git-committer-email nil "Email address to use for commits. -The default is to fall back to the git repository config, then to `add-log-mailing-address' and then to `user-mail-address'." +The default is to fall back to the git repository config, +then to `add-log-mailing-address' and then to `user-mail-address'." :group 'git :type '(choice (const :tag "Default" nil) (string :tag "Email"))) @@ -86,6 +88,7 @@ The default is to fall back to the git repository config, then to `add-log-maili :group 'git :type 'string) + (defface git-status-face '((((class color) (background light)) (:foreground "purple"))) "Git mode face used to highlight added and modified files." @@ -149,7 +152,8 @@ The default is to fall back to the git repository config, then to `add-log-maili (apply #'call-process "git" nil buffer nil args))) (defun git-call-process-env-string (env &rest args) - "Wrapper for call-process that sets environment strings, and returns the process output as a string." + "Wrapper for call-process that sets environment strings, +and returns the process output as a string." (with-temp-buffer (and (eq 0 (apply #' git-call-process-env t env args)) (buffer-string)))) -- cgit v1.2.3 From 5716e794bf12ad9740da41d06c94e2771fe9e0ee Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 13 Jul 2006 22:22:14 +0200 Subject: Display help for Git mode after pressing `h' or `?' in *git-status* Add bindings for "h" and "?" in git-status-mode to display help about the mode, including keymap via (describe-function 'git-status-mode), like in PCL-CVS. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 83a845dd94..34c995046d 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -943,6 +943,8 @@ and returns the process output as a string." (let ((map (make-keymap)) (diff-map (make-sparse-keymap))) (suppress-keymap map) + (define-key map "?" 'git-help) + (define-key map "h" 'git-help) (define-key map " " 'git-next-file) (define-key map "a" 'git-add-file) (define-key map "c" 'git-commit-file) @@ -1012,5 +1014,10 @@ Commands: (goto-char (point-min))) (message "%s is not a git working tree." dir))) +(defun git-help () + "Display help for Git mode." + (interactive) + (describe-function 'git-status-mode)) + (provide 'git) ;;; git.el ends here -- cgit v1.2.3 From 51a6e56fb7f63e110dc0c3c1fddd07ca9e22ced6 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 22 Jul 2006 15:39:02 +0200 Subject: git.el: Run git-rerere on commits if the rr-cache directory exists. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 34c995046d..7371d4b463 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -584,6 +584,8 @@ and returns the process output as a string." (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) (with-current-buffer buffer (erase-buffer)) (git-set-files-state files 'uptodate) + (when (file-directory-p ".git/rr-cache") + (git-run-command nil nil "rerere")) (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit)) -- cgit v1.2.3 From 9f56a7fda940caa7d4a1b06947481b5157510557 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 22 Jul 2006 15:39:32 +0200 Subject: git.el: Prepend a slash to the file name when adding to .gitignore. This way the ignore command will really only ignore the marked files and not files with the same name in subdirectories. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 7371d4b463..5837471375 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -258,7 +258,7 @@ and returns the process output as a string." (set-buffer (find-file-noselect ignore-name)) (goto-char (point-max)) (unless (zerop (current-column)) (insert "\n")) - (insert name "\n") + (insert "/" name "\n") (sort-lines nil (point-min) (point-max)) (save-buffer)) (when created -- cgit v1.2.3 From 73389f12bf0ef39820685a322527fab549676f33 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 22 Jul 2006 15:39:53 +0200 Subject: git.el: Try to reuse an existing buffer when running git-status. By default, running git-status again will now reuse an existing buffer that displays the same directory. The old behavior of always creating a new buffer can be obtained by customizing the git-reuse-status-buffer option. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 5837471375..92cb2b90fe 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -83,6 +83,12 @@ then to `add-log-mailing-address' and then to `user-mail-address'." :group 'git :type 'boolean) +(defcustom git-reuse-status-buffer t + "Whether `git-status' should try to reuse an existing buffer +if there is already one that displays the same directory." + :group 'git + :type 'boolean) + (defcustom git-per-dir-ignore-file ".gitignore" "Name of the per-directory ignore file." :group 'git @@ -1003,12 +1009,28 @@ Commands: (set (make-local-variable 'list-buffers-directory) default-directory) (run-hooks 'git-status-mode-hook))) +(defun git-find-status-buffer (dir) + "Find the git status buffer handling a specified directory." + (let ((list (buffer-list)) + (fulldir (expand-file-name dir)) + found) + (while (and list (not found)) + (let ((buffer (car list))) + (with-current-buffer buffer + (when (and list-buffers-directory + (string-equal fulldir (expand-file-name list-buffers-directory)) + (string-match "\\*git-status\\*$" (buffer-name buffer))) + (setq found buffer)))) + (setq list (cdr list))) + found)) + (defun git-status (dir) "Entry point into git-status mode." (interactive "DSelect directory: ") (setq dir (git-get-top-dir dir)) (if (file-directory-p (concat (file-name-as-directory dir) ".git")) - (let ((buffer (create-file-buffer (expand-file-name "*git-status*" dir)))) + (let ((buffer (or (and git-reuse-status-buffer (git-find-status-buffer dir)) + (create-file-buffer (expand-file-name "*git-status*" dir))))) (switch-to-buffer buffer) (cd dir) (git-status-mode) -- cgit v1.2.3 From 5df52584fa6cce1789009ad8a11f9eee92d5168a Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 22 Jul 2006 15:40:13 +0200 Subject: git.el: Put the git customize group in the 'tools' parent group. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 92cb2b90fe..68de9be0c7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -55,7 +55,8 @@ ;;;; ------------------------------------------------------------ (defgroup git nil - "Git user interface") + "A user interface for the git versioning system." + :group 'tools) (defcustom git-committer-name nil "User name to use for commits. -- cgit v1.2.3 From 076a10c7282a08f783a28c1b64d0e114a3fe3d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Wed, 16 Aug 2006 23:12:26 +0300 Subject: Be nicer if git executable is not installed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch avoids problems if vc-git.el is installed and activated, but the git executable is not available, for example http://list-archive.xemacs.org/xemacs-beta/200608/msg00062.html Signed-off-by: Ville Skyttä Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index 3f6ed699f0..4a8f79092d 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -54,7 +54,7 @@ (let* ((dir (file-name-directory file)) (name (file-relative-name file dir))) (when dir (cd dir)) - (and (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name)) + (and (ignore-errors (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name))) (let ((str (buffer-string))) (and (> (length str) (length name)) (string= (substring str 0 (1+ (length name))) (concat name "\0")))))))) -- cgit v1.2.3 From 5bab25fd91b839bbbebed2b394deabc13dea99db Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Fri, 25 Aug 2006 03:06:50 +0200 Subject: gitview.txt: improve asciidoc markup Signed-off-by: Jonas Fonseca Signed-off-by: Junio C Hamano --- contrib/gitview/gitview.txt | 56 ++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 23 deletions(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview.txt b/contrib/gitview/gitview.txt index 6924df286e..77c29de305 100644 --- a/contrib/gitview/gitview.txt +++ b/contrib/gitview/gitview.txt @@ -7,40 +7,50 @@ gitview - A GTK based repository browser for git SYNOPSIS -------- -'gitview' [options] [args] +'gitview' [options] [args] DESCRIPTION --------- -Dependencies +Dependencies: * Python 2.4 * PyGTK 2.8 or later * PyCairo 1.0 or later OPTIONS ------- - --without-diff - If the user doesn't want to list the commit diffs in the main window. This may speed up the repository browsing. - - - All the valid option for git-rev-list(1) - Key Bindings: - F4: - To maximize the window - F5: - To reread references. - F11: - Full screen - F12: - Leave full screen +------- +--without-diff:: + + If the user doesn't want to list the commit diffs in the main window. + This may speed up the repository browsing. + +:: + + All the valid option for gitlink:git-rev-list[1]. + +Key Bindings +------------ +F4:: + To maximize the window + +F5:: + To reread references. + +F11:: + Full screen + +F12:: + Leave full screen EXAMPLES ------- - gitview v2.6.12.. include/scsi drivers/scsi - Show as the changes since version v2.6.12 that changed any file in the include/scsi - or drivers/scsi subdirectories +-------- + +gitview v2.6.12.. include/scsi drivers/scsi:: + + Show as the changes since version v2.6.12 that changed any file in the + include/scsi or drivers/scsi subdirectories - gitview --since=2.weeks.ago - Show the changes during the last two weeks +gitview --since=2.weeks.ago:: + Show the changes during the last two weeks -- cgit v1.2.3 From cdad8bbe92a447233133be8f9f3015c1bd7e10eb Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 11 Sep 2006 19:22:49 -0400 Subject: contrib/vim: add syntax highlighting file for commits Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/vim/README | 8 ++++++++ contrib/vim/syntax/gitcommit.vim | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 contrib/vim/README create mode 100644 contrib/vim/syntax/gitcommit.vim (limited to 'contrib') diff --git a/contrib/vim/README b/contrib/vim/README new file mode 100644 index 0000000000..9e7881fea9 --- /dev/null +++ b/contrib/vim/README @@ -0,0 +1,8 @@ +To syntax highlight git's commit messages, you need to: + 1. Copy syntax/gitcommit.vim to vim's syntax directory: + $ mkdir -p $HOME/.vim/syntax + $ cp syntax/gitcommit.vim $HOME/.vim/syntax + 2. Auto-detect the editing of git commit files: + $ cat >>$HOME/.vimrc <<'EOF' + autocmd BufNewFile,BufRead COMMIT_EDITMSG set filetype=gitcommit + EOF diff --git a/contrib/vim/syntax/gitcommit.vim b/contrib/vim/syntax/gitcommit.vim new file mode 100644 index 0000000000..a9de09fa2f --- /dev/null +++ b/contrib/vim/syntax/gitcommit.vim @@ -0,0 +1,18 @@ +syn region gitLine start=/^#/ end=/$/ +syn region gitCommit start=/^# Updated but not checked in:$/ end=/^#$/ contains=gitHead,gitCommitFile +syn region gitHead contained start=/^# (.*)/ end=/^#$/ +syn region gitChanged start=/^# Changed but not updated:/ end=/^#$/ contains=gitHead,gitChangedFile +syn region gitUntracked start=/^# Untracked files:/ end=/^#$/ contains=gitHead,gitUntrackedFile + +syn match gitCommitFile contained /^#\t.*/hs=s+2 +syn match gitChangedFile contained /^#\t.*/hs=s+2 +syn match gitUntrackedFile contained /^#\t.*/hs=s+2 + +hi def link gitLine Comment +hi def link gitCommit Comment +hi def link gitChanged Comment +hi def link gitHead Comment +hi def link gitUntracked Comment +hi def link gitCommitFile Type +hi def link gitChangedFile Constant +hi def link gitUntrackedFile Constant -- cgit v1.2.3 From 690d8824c852b6a59deb03f65e330297c587752e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 28 Sep 2006 02:31:25 -0700 Subject: Contributed bash completion support for core Git tools. This is a set of bash completion routines for many of the popular core Git tools. I wrote these routines from scratch after reading the git-compl and git-compl-lib routines available from the gitcompletion package at http://gitweb.hawaga.org.uk/ and found those to be lacking in functionality for some commands. Consequently there may be some similarities but many differences. Since these are completion routines only for tools shipped with core Git and since bash is a popular shell on many of the native core Git platforms (Linux, Mac OS X, Solaris, BSD) including these routines as part of the stock package would probably be convienent for many users. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 324 +++++++++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100755 contrib/completion/git-completion.bash (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash new file mode 100755 index 0000000000..d9cb17d0b2 --- /dev/null +++ b/contrib/completion/git-completion.bash @@ -0,0 +1,324 @@ +# +# bash completion support for core Git. +# +# Copyright (C) 2006 Shawn Pearce +# Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/). +# +# The contained completion routines provide support for completing: +# +# *) local and remote branch names +# *) local and remote tag names +# *) .git/remotes file names +# *) git 'subcommands' +# *) tree paths within 'ref:path/to/file' expressions +# +# To use these routines: +# +# 1) Copy this file to somewhere (e.g. ~/.git-completion.sh). +# 2) Added the following line to your .bashrc: +# source ~/.git-completion.sh +# + +__git_refs () +{ + local cmd i is_hash=y + if [ -d "$1" ]; then + cmd=git-peek-remote + else + cmd=git-ls-remote + fi + for i in $($cmd "$1" 2>/dev/null); do + case "$is_hash,$i" in + y,*) is_hash=n ;; + n,*^{}) is_hash=y ;; + n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;; + n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;; + n,*) is_hash=y; echo "$i" ;; + esac + done +} + +__git_refs2 () +{ + local cmd i is_hash=y + if [ -d "$1" ]; then + cmd=git-peek-remote + else + cmd=git-ls-remote + fi + for i in $($cmd "$1" 2>/dev/null); do + case "$is_hash,$i" in + y,*) is_hash=n ;; + n,*^{}) is_hash=y ;; + n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}:${i#refs/tags/}" ;; + n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}:${i#refs/heads/}" ;; + n,*) is_hash=y; echo "$i:$i" ;; + esac + done +} + +__git_remotes () +{ + local i REVERTGLOB=$(shopt -p nullglob) + shopt -s nullglob + for i in .git/remotes/*; do + echo ${i#.git/remotes/} + done + $REVERTGLOB +} + +__git_complete_file () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + ?*:*) + local pfx ls ref="$(echo "$cur" | sed 's,:.*$,,')" + cur="$(echo "$cur" | sed 's,^.*:,,')" + case "$cur" in + ?*/*) + pfx="$(echo "$cur" | sed 's,/[^/]*$,,')" + cur="$(echo "$cur" | sed 's,^.*/,,')" + ls="$ref:$pfx" + pfx="$pfx/" + ;; + *) + ls="$ref" + ;; + esac + COMPREPLY=($(compgen -P "$pfx" \ + -W "$(git-ls-tree "$ls" \ + | sed '/^100... blob /s,^.* ,, + /^040000 tree /{ + s,^.* ,, + s,$,/, + } + s/^.* //')" \ + -- "$cur")) + ;; + *) + COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur")) + ;; + esac +} + +_git_branch () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs .)" -- "$cur")) +} + +_git_cat_file () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "${COMP_WORDS[0]},$COMP_CWORD" in + git-cat-file*,1) + COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur")) + ;; + git,2) + COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur")) + ;; + *) + __git_complete_file + ;; + esac +} + +_git_checkout () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + COMPREPLY=($(compgen -W "-l -b $(__git_refs .)" -- "$cur")) +} + +_git_diff () +{ + __git_complete_file +} + +_git_diff_tree () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + COMPREPLY=($(compgen -W "-r -p -M $(__git_refs .)" -- "$cur")) +} + +_git_fetch () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + + case "${COMP_WORDS[0]},$COMP_CWORD" in + git-fetch*,1) + COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + ;; + git,2) + COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + ;; + *) + case "$cur" in + *:*) + cur=$(echo "$cur" | sed 's/^.*://') + COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur")) + ;; + *) + local remote + case "${COMP_WORDS[0]}" in + git-fetch) remote="${COMP_WORDS[1]}" ;; + git) remote="${COMP_WORDS[2]}" ;; + esac + COMPREPLY=($(compgen -W "$(__git_refs2 "$remote")" -- "$cur")) + ;; + esac + ;; + esac +} + +_git_ls_remote () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) +} + +_git_ls_tree () +{ + __git_complete_file +} + +_git_log () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + *..*) + local pfx=$(echo "$cur" | sed 's/\.\..*$/../') + cur=$(echo "$cur" | sed 's/^.*\.\.//') + COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs .)" -- "$cur")) + ;; + *) + COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur")) + ;; + esac +} + +_git_merge_base () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur")) +} + +_git_pull () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + + case "${COMP_WORDS[0]},$COMP_CWORD" in + git-pull*,1) + COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + ;; + git,2) + COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + ;; + *) + local remote + case "${COMP_WORDS[0]}" in + git-pull) remote="${COMP_WORDS[1]}" ;; + git) remote="${COMP_WORDS[2]}" ;; + esac + COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur")) + ;; + esac +} + +_git_push () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + + case "${COMP_WORDS[0]},$COMP_CWORD" in + git-push*,1) + COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + ;; + git,2) + COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + ;; + *) + case "$cur" in + *:*) + local remote + case "${COMP_WORDS[0]}" in + git-push) remote="${COMP_WORDS[1]}" ;; + git) remote="${COMP_WORDS[2]}" ;; + esac + cur=$(echo "$cur" | sed 's/^.*://') + COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur")) + ;; + *) + COMPREPLY=($(compgen -W "$(__git_refs2 .)" -- "$cur")) + ;; + esac + ;; + esac +} + +_git_show () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur")) +} + +_git () +{ + if [ $COMP_CWORD = 1 ]; then + COMPREPLY=($(compgen \ + -W "--version $(git help -a|egrep '^ ')" \ + -- "${COMP_WORDS[COMP_CWORD]}")) + else + case "${COMP_WORDS[1]}" in + branch) _git_branch ;; + cat-file) _git_cat_file ;; + checkout) _git_checkout ;; + diff) _git_diff ;; + diff-tree) _git_diff_tree ;; + fetch) _git_fetch ;; + log) _git_log ;; + ls-remote) _git_ls_remote ;; + ls-tree) _git_ls_tree ;; + pull) _git_pull ;; + push) _git_push ;; + show) _git_show ;; + show-branch) _git_log ;; + whatchanged) _git_log ;; + *) COMPREPLY=() ;; + esac + fi +} + +_gitk () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + COMPREPLY=($(compgen -W "--all $(__git_refs .)" -- "$cur")) +} + +complete -o default -o nospace -F _git git +complete -o default -F _gitk gitk +complete -o default -F _git_branch git-branch +complete -o default -o nospace -F _git_cat_file git-cat-file +complete -o default -F _git_checkout git-checkout +complete -o default -o nospace -F _git_diff git-diff +complete -o default -F _git_diff_tree git-diff-tree +complete -o default -o nospace -F _git_fetch git-fetch +complete -o default -o nospace -F _git_log git-log +complete -o default -F _git_ls_remote git-ls-remote +complete -o default -o nospace -F _git_ls_tree git-ls-tree +complete -o default -F _git_merge_base git-merge-base +complete -o default -o nospace -F _git_pull git-pull +complete -o default -o nospace -F _git_push git-push +complete -o default -F _git_show git-show +complete -o default -o nospace -F _git_log git-whatchanged + +# The following are necessary only for Cygwin, and only are needed +# when the user has tab-completed the executable name and consequently +# included the '.exe' suffix. +# +complete -o default -o nospace -F _git_cat_file git-cat-file.exe +complete -o default -o nospace -F _git_diff git-diff.exe +complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe +complete -o default -o nospace -F _git_log git-log.exe +complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe +complete -o default -F _git_merge_base git-merge-base.exe +complete -o default -o nospace -F _git_push git-push.exe +complete -o default -o nospace -F _git_log git-whatchanged.exe -- cgit v1.2.3 From c530c5aa31f44adafd1f4ecb05223024162e689c Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 5 Oct 2006 11:29:57 +0200 Subject: git.el: Fixed inverted "renamed from/to" message. The deleted file should be labeled "renamed to" and the added file "renamed from", not the other way around (duh!) Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 68de9be0c7..5354cd67b3 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -422,8 +422,8 @@ and returns the process output as a string." (propertize (concat " (" (if (eq state 'copy) "copied from " - (if (eq (git-fileinfo->state info) 'added) "renamed to " - "renamed from ")) + (if (eq (git-fileinfo->state info) 'added) "renamed from " + "renamed to ")) (git-escape-file-name (git-fileinfo->orig-name info)) ")") 'face 'git-status-face) ""))) -- cgit v1.2.3 From 13f8e0b24b546c0da942ab8ebe334d6e55fe1ea6 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 5 Oct 2006 11:30:44 +0200 Subject: vc-git.el: Switch to using git-blame instead of git-annotate. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index 4a8f79092d..4189c4ced0 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -119,10 +119,10 @@ (defun vc-git-annotate-command (file buf &optional rev) ; FIXME: rev is ignored (let ((name (file-relative-name file))) - (call-process "git" nil buf nil "annotate" name))) + (call-process "git" nil buf nil "blame" name))) (defun vc-git-annotate-time () - (and (re-search-forward "[0-9a-f]+\t(.*\t\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\)\t[0-9]+)" nil t) + (and (re-search-forward "[0-9a-f]+ (.* \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\) +[0-9]+)" nil t) (vc-annotate-convert-time (apply #'encode-time (mapcar (lambda (match) (string-to-number (match-string match))) '(6 5 4 3 2 1 7)))))) -- cgit v1.2.3 From 474a90fef9ebcdedee041b2def4b9a98b94cd146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Hasselstr=C3=B6m?= Date: Sun, 22 Oct 2006 20:46:36 +0200 Subject: git-vc: better installation instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide some more detailed installation instructions, for the elisp-challenged among us. Signed-off-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index 4189c4ced0..80e767533a 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -23,7 +23,10 @@ ;; system. ;; ;; To install: put this file on the load-path and add GIT to the list -;; of supported backends in `vc-handled-backends'. +;; of supported backends in `vc-handled-backends'; the following line, +;; placed in your ~/.emacs, will accomplish this: +;; +;; (add-to-list 'vc-handled-backends 'GIT) ;; ;; TODO ;; - changelog generation -- cgit v1.2.3 From b4aee09e610567529dc619d7324dc2fe85a11db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Hasselstr=C3=B6m?= Date: Sun, 22 Oct 2006 17:02:42 -0700 Subject: ignore-errors requires cl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vc-git complains that it can't find the definition of ignore-errors unless I (require 'cl). So I guess the correct place to do that is in the file itself. Signed-off-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index 80e767533a..8b6361922f 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -33,6 +33,8 @@ ;; - working with revisions other than HEAD ;; +(eval-when-compile (require 'cl)) + (defvar git-commits-coding-system 'utf-8 "Default coding system for git commits.") -- cgit v1.2.3 From 367dce2a5b80da055a0d776fe830e6f19b81bd1d Mon Sep 17 00:00:00 2001 From: Dennis Stosberg Date: Sat, 28 Oct 2006 14:12:20 +0200 Subject: Bash completion support for aliases - Add aliases to the list of available git commands. - Make completion work for aliased commands. Signed-off-by: Dennis Stosberg Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d9cb17d0b2..b074f4fe57 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -101,6 +101,23 @@ __git_complete_file () esac } +__git_aliases () +{ + git repo-config --list | grep '^alias\.' \ + | sed -e 's/^alias\.//' -e 's/=.*$//' +} + +__git_aliased_command () +{ + local cmdline=$(git repo-config alias.$1) + for word in $cmdline; do + if [ "${word##-*}" ]; then + echo $word + return + fi + done +} + _git_branch () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -264,10 +281,18 @@ _git () { if [ $COMP_CWORD = 1 ]; then COMPREPLY=($(compgen \ - -W "--version $(git help -a|egrep '^ ')" \ + -W "--version $(git help -a|egrep '^ ') \ + $(__git_aliases)" \ -- "${COMP_WORDS[COMP_CWORD]}")) else - case "${COMP_WORDS[1]}" in + local command="${COMP_WORDS[1]}" + local expansion=$(__git_aliased_command "$command") + + if [ "$expansion" ]; then + command="$expansion" + fi + + case "$command" in branch) _git_branch ;; cat-file) _git_cat_file ;; checkout) _git_checkout ;; -- cgit v1.2.3 From 8a078c3f72002af9bf79dc884fe321e3e48930fc Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 3 Nov 2006 17:41:23 +0100 Subject: git.el: Added functions for moving to the next/prev unmerged file. This is useful when doing a merge that changes many files with only a few conflicts here and there. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 5354cd67b3..e283df2fdb 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -670,6 +670,32 @@ and returns the process output as a string." (unless git-status (error "Not in git-status buffer.")) (ewoc-goto-prev git-status n)) +(defun git-next-unmerged-file (&optional n) + "Move the selection down N unmerged files." + (interactive "p") + (unless git-status (error "Not in git-status buffer.")) + (let* ((last (ewoc-locate git-status)) + (node (ewoc-next git-status last))) + (while (and node (> n 0)) + (when (eq 'unmerged (git-fileinfo->state (ewoc-data node))) + (setq n (1- n)) + (setq last node)) + (setq node (ewoc-next git-status node))) + (ewoc-goto-node git-status last))) + +(defun git-prev-unmerged-file (&optional n) + "Move the selection up N unmerged files." + (interactive "p") + (unless git-status (error "Not in git-status buffer.")) + (let* ((last (ewoc-locate git-status)) + (node (ewoc-prev git-status last))) + (while (and node (> n 0)) + (when (eq 'unmerged (git-fileinfo->state (ewoc-data node))) + (setq n (1- n)) + (setq last node)) + (setq node (ewoc-prev git-status node))) + (ewoc-goto-node git-status last))) + (defun git-add-file () "Add marked file(s) to the index cache." (interactive) @@ -967,7 +993,9 @@ and returns the process output as a string." (define-key map "m" 'git-mark-file) (define-key map "M" 'git-mark-all) (define-key map "n" 'git-next-file) + (define-key map "N" 'git-next-unmerged-file) (define-key map "p" 'git-prev-file) + (define-key map "P" 'git-prev-unmerged-file) (define-key map "q" 'git-status-quit) (define-key map "r" 'git-remove-file) (define-key map "R" 'git-resolve-file) -- cgit v1.2.3 From b8ee51815ad0520b0340c2a594dee1f8de9c7c8a Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 3 Nov 2006 17:41:46 +0100 Subject: git.el: Added a function to open the current file in another window. Bound to 'o' by default, compatible with pcl-cvs and buffer-mode. Suggested by Han-Wen Nienhuys. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index e283df2fdb..08d6404bcb 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -910,6 +910,15 @@ and returns the process output as a string." (when (eq 'unmerged (git-fileinfo->state info)) (smerge-mode)))) +(defun git-find-file-other-window () + "Visit the current file in its own buffer in another window." + (interactive) + (unless git-status (error "Not in git-status buffer.")) + (let ((info (ewoc-data (ewoc-locate git-status)))) + (find-file-other-window (git-fileinfo->name info)) + (when (eq 'unmerged (git-fileinfo->state info)) + (smerge-mode)))) + (defun git-find-file-imerge () "Visit the current file in interactive merge mode." (interactive) @@ -994,6 +1003,7 @@ and returns the process output as a string." (define-key map "M" 'git-mark-all) (define-key map "n" 'git-next-file) (define-key map "N" 'git-next-unmerged-file) + (define-key map "o" 'git-find-file-other-window) (define-key map "p" 'git-prev-file) (define-key map "P" 'git-prev-unmerged-file) (define-key map "q" 'git-status-quit) -- cgit v1.2.3 From 2ac2b19601cd8db8660a8d55647079f1ff4885b1 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 3 Nov 2006 17:42:17 +0100 Subject: git.el: Move point after the log message header when entering log-edit mode. Suggested by Han-Wen Nienhuys. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 08d6404bcb..6f3b46df9f 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -899,7 +899,8 @@ and returns the process output as a string." (2 font-lock-function-name-face)) (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$") (1 font-lock-comment-face))))) - (log-edit #'git-do-commit nil #'git-log-edit-files buffer)))) + (log-edit #'git-do-commit nil #'git-log-edit-files buffer) + (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) (defun git-find-file () "Visit the current file in its own buffer." -- cgit v1.2.3 From 2379d61fa6355bc7a8cc8b5dce87a7d4a9505c76 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 3 Nov 2006 17:42:43 +0100 Subject: git.el: Include MERGE_MSG in the log-edit buffer even when not committing a merge. This lets us take advantage of the fact that git-cherry-pick now saves the message in MERGE_MSG too. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 6f3b46df9f..972c402ea0 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -589,6 +589,7 @@ and returns the process output as a string." (let ((commit (git-commit-tree buffer tree head))) (git-update-ref "HEAD" commit head) (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) + (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) (git-set-files-state files 'uptodate) (when (file-directory-p ".git/rr-cache") @@ -888,7 +889,7 @@ and returns the process output as a string." 'face 'git-header-face) (propertize git-log-msg-separator 'face 'git-separator-face) "\n") - (cond ((and merge-heads (file-readable-p ".git/MERGE_MSG")) + (cond ((file-readable-p ".git/MERGE_MSG") (insert-file-contents ".git/MERGE_MSG")) (sign-off (insert (format "\n\nSigned-off-by: %s <%s>\n" -- cgit v1.2.3 From dfb960920d4953066cb0cdf3cb6dd8a5a8d9cf14 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 4 Nov 2006 13:57:18 -0500 Subject: Added completion support for git-branch.exe. On Cygwin a user might complete the new git-branch builtin as git-branch.exe, at which point bash requires a new completion registration for the command. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b074f4fe57..b7b8a43de5 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -339,6 +339,7 @@ complete -o default -o nospace -F _git_log git-whatchanged # when the user has tab-completed the executable name and consequently # included the '.exe' suffix. # +complete -o default -F _git_branch git-branch.exe complete -o default -o nospace -F _git_cat_file git-cat-file.exe complete -o default -o nospace -F _git_diff git-diff.exe complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe -- cgit v1.2.3 From 67e78c3b8aeb44681b2d4ab2cf97145b7cc9a7a2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 4 Nov 2006 13:57:44 -0500 Subject: Added bash completion support for git-reset. Completion for the --hard/--soft/--mixed modes of operation as well as a ref name for can be very useful and save some fingers. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b7b8a43de5..a3fbb9032c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -271,6 +271,13 @@ _git_push () esac } +_git_reset () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + local opt="--mixed --hard --soft" + COMPREPLY=($(compgen -W "$opt $(__git_refs .)" -- "$cur")) +} + _git_show () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -304,6 +311,7 @@ _git () ls-tree) _git_ls_tree ;; pull) _git_pull ;; push) _git_push ;; + reset) _git_reset ;; show) _git_show ;; show-branch) _git_log ;; whatchanged) _git_log ;; @@ -332,6 +340,7 @@ complete -o default -o nospace -F _git_ls_tree git-ls-tree complete -o default -F _git_merge_base git-merge-base complete -o default -o nospace -F _git_pull git-pull complete -o default -o nospace -F _git_push git-push +complete -o default -F _git_reset git-reset complete -o default -F _git_show git-show complete -o default -o nospace -F _git_log git-whatchanged -- cgit v1.2.3 From 144d33dec97405889e302c2391300ee04aad7714 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 5 Nov 2006 06:20:02 -0500 Subject: Added missing completions for show-branch and merge-base. The show-branch and merge-base commands were partially supported when it came to bash completions as they were only specified in one form another. Now we specify them in both forms. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a3fbb9032c..fdfbf959b7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -309,6 +309,7 @@ _git () log) _git_log ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; + merge-base) _git_merge_base ;; pull) _git_pull ;; push) _git_push ;; reset) _git_reset ;; @@ -342,12 +343,14 @@ complete -o default -o nospace -F _git_pull git-pull complete -o default -o nospace -F _git_push git-push complete -o default -F _git_reset git-reset complete -o default -F _git_show git-show +complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged # The following are necessary only for Cygwin, and only are needed # when the user has tab-completed the executable name and consequently # included the '.exe' suffix. # +complete -o default -o nospace -F _git git.exe complete -o default -F _git_branch git-branch.exe complete -o default -o nospace -F _git_cat_file git-cat-file.exe complete -o default -o nospace -F _git_diff git-diff.exe @@ -356,4 +359,5 @@ complete -o default -o nospace -F _git_log git-log.exe complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe complete -o default -F _git_merge_base git-merge-base.exe complete -o default -o nospace -F _git_push git-push.exe +complete -o default -o nospace -F _git_log git-show-branch.exe complete -o default -o nospace -F _git_log git-whatchanged.exe -- cgit v1.2.3 From 76c3eb51ede4619116ef980aa34d087c97c25cbc Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 5 Nov 2006 06:20:25 -0500 Subject: Only load .exe suffix'd completions on Cygwin. The only platform which actually needs to define .exe suffixes as part of its completion set is Cygwin. So don't define them on any other platform. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index fdfbf959b7..926638d5ff 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -350,6 +350,7 @@ complete -o default -o nospace -F _git_log git-whatchanged # when the user has tab-completed the executable name and consequently # included the '.exe' suffix. # +if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then complete -o default -o nospace -F _git git.exe complete -o default -F _git_branch git-branch.exe complete -o default -o nospace -F _git_cat_file git-cat-file.exe @@ -361,3 +362,4 @@ complete -o default -F _git_merge_base git-merge-base.exe complete -o default -o nospace -F _git_push git-push.exe complete -o default -o nospace -F _git_log git-show-branch.exe complete -o default -o nospace -F _git_log git-whatchanged.exe +fi -- cgit v1.2.3 From 56fc25f21e280c4f3816e989b34d08d7d7cc59fc Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 5 Nov 2006 06:21:03 -0500 Subject: Bash completion support for remotes in .git/config. Now that Git natively supports remote specifications within the config file such as: [remote "origin"] url = ... we should provide bash completion support "out of the box" for these remotes, just like we do for the .git/remotes directory. Also cleaned up the __git_aliases expansion to use the same form of querying and filtering repo-config as this saves two fork/execs in the middle of a user prompted completion. Finally also forced the variable 'word' to be local within __git_aliased_command. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 926638d5ff..5f1be46ba5 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -59,12 +59,21 @@ __git_refs2 () __git_remotes () { - local i REVERTGLOB=$(shopt -p nullglob) + local i ngoff IFS=$'\n' + shopt -q nullglob || ngoff=1 shopt -s nullglob for i in .git/remotes/*; do echo ${i#.git/remotes/} done - $REVERTGLOB + [ "$ngoff" ] && shopt -u nullglob + for i in $(git repo-config --list); do + case "$i" in + remote.*.url=*) + i="${i#remote.}" + echo "${i/.url=*/}" + ;; + esac + done } __git_complete_file () @@ -103,13 +112,20 @@ __git_complete_file () __git_aliases () { - git repo-config --list | grep '^alias\.' \ - | sed -e 's/^alias\.//' -e 's/=.*$//' + local i IFS=$'\n' + for i in $(git repo-config --list); do + case "$i" in + alias.*) + i="${i#alias.}" + echo "${i/=*/}" + ;; + esac + done } __git_aliased_command () { - local cmdline=$(git repo-config alias.$1) + local word cmdline=$(git repo-config --get "alias.$1") for word in $cmdline; do if [ "${word##-*}" ]; then echo $word -- cgit v1.2.3 From 873537fadc9bdc35726d1c69c46926c7f5c49dd2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 5 Nov 2006 06:21:57 -0500 Subject: Take --git-dir into consideration during bash completion. If the user has setup a command line of "git --git-dir=baz" then anything we complete must be performed within the scope of "baz" and not the current working directory. This is useful with commands such as "git --git-dir=git.git log m" to complete out "master" and view the log for the master branch of the git.git repository. As a nice side effect this also works for aliases within the target repository, just as git would honor them. Unfortunately because we still examine arguments by absolute position in most of the more complex commands (e.g. git push) using --git-dir with those commands will probably still cause completion to fail. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 123 +++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 53 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 5f1be46ba5..f258f2f03e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -19,15 +19,20 @@ # source ~/.git-completion.sh # +__gitdir () +{ + echo "${__git_dir:-$(git rev-parse --git-dir 2>/dev/null)}" +} + __git_refs () { - local cmd i is_hash=y - if [ -d "$1" ]; then + local cmd i is_hash=y dir="${1:-$(__gitdir)}" + if [ -d "$dir" ]; then cmd=git-peek-remote else cmd=git-ls-remote fi - for i in $($cmd "$1" 2>/dev/null); do + for i in $($cmd "$dir" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -40,13 +45,13 @@ __git_refs () __git_refs2 () { - local cmd i is_hash=y - if [ -d "$1" ]; then + local cmd i is_hash=y dir="${1:-$(__gitdir)}" + if [ -d "$dir" ]; then cmd=git-peek-remote else cmd=git-ls-remote fi - for i in $($cmd "$1" 2>/dev/null); do + for i in $($cmd "$dir" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -59,14 +64,14 @@ __git_refs2 () __git_remotes () { - local i ngoff IFS=$'\n' + local i ngoff IFS=$'\n' d="$(__gitdir)" shopt -q nullglob || ngoff=1 shopt -s nullglob - for i in .git/remotes/*; do - echo ${i#.git/remotes/} + for i in "$d/remotes"/*; do + echo ${i#$d/remotes/} done [ "$ngoff" ] && shopt -u nullglob - for i in $(git repo-config --list); do + for i in $(git --git-dir="$d" repo-config --list); do case "$i" in remote.*.url=*) i="${i#remote.}" @@ -95,7 +100,7 @@ __git_complete_file () ;; esac COMPREPLY=($(compgen -P "$pfx" \ - -W "$(git-ls-tree "$ls" \ + -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \ | sed '/^100... blob /s,^.* ,, /^040000 tree /{ s,^.* ,, @@ -105,7 +110,7 @@ __git_complete_file () -- "$cur")) ;; *) - COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur")) + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) ;; esac } @@ -113,7 +118,7 @@ __git_complete_file () __git_aliases () { local i IFS=$'\n' - for i in $(git repo-config --list); do + for i in $(git --git-dir="$(__gitdir)" repo-config --list); do case "$i" in alias.*) i="${i#alias.}" @@ -125,7 +130,8 @@ __git_aliases () __git_aliased_command () { - local word cmdline=$(git repo-config --get "alias.$1") + local word cmdline=$(git --git-dir="$(__gitdir)" \ + repo-config --get "alias.$1") for word in $cmdline; do if [ "${word##-*}" ]; then echo $word @@ -137,7 +143,7 @@ __git_aliased_command () _git_branch () { local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs .)" -- "$cur")) + COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs)" -- "$cur")) } _git_cat_file () @@ -159,7 +165,7 @@ _git_cat_file () _git_checkout () { local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "-l -b $(__git_refs .)" -- "$cur")) + COMPREPLY=($(compgen -W "-l -b $(__git_refs)" -- "$cur")) } _git_diff () @@ -170,7 +176,7 @@ _git_diff () _git_diff_tree () { local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "-r -p -M $(__git_refs .)" -- "$cur")) + COMPREPLY=($(compgen -W "-r -p -M $(__git_refs)" -- "$cur")) } _git_fetch () @@ -188,7 +194,7 @@ _git_fetch () case "$cur" in *:*) cur=$(echo "$cur" | sed 's/^.*://') - COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur")) + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) ;; *) local remote @@ -221,10 +227,10 @@ _git_log () *..*) local pfx=$(echo "$cur" | sed 's/\.\..*$/../') cur=$(echo "$cur" | sed 's/^.*\.\.//') - COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs .)" -- "$cur")) + COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur")) ;; *) - COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur")) + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) ;; esac } @@ -232,7 +238,7 @@ _git_log () _git_merge_base () { local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur")) + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } _git_pull () @@ -280,7 +286,7 @@ _git_push () COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur")) ;; *) - COMPREPLY=($(compgen -W "$(__git_refs2 .)" -- "$cur")) + COMPREPLY=($(compgen -W "$(__git_refs2)" -- "$cur")) ;; esac ;; @@ -291,56 +297,67 @@ _git_reset () { local cur="${COMP_WORDS[COMP_CWORD]}" local opt="--mixed --hard --soft" - COMPREPLY=($(compgen -W "$opt $(__git_refs .)" -- "$cur")) + COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur")) } _git_show () { local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "$(__git_refs .)" -- "$cur")) + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } _git () { - if [ $COMP_CWORD = 1 ]; then + local i c=1 command __git_dir + + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + --git-dir=*) __git_dir="${i#--git-dir=}" ;; + --bare) __git_dir="." ;; + --version|--help|-p|--paginate) ;; + *) command="$i"; break ;; + esac + c=$((++c)) + done + + if [ $c -eq $COMP_CWORD -a -z "$command" ]; then COMPREPLY=($(compgen \ - -W "--version $(git help -a|egrep '^ ') \ + -W "--git-dir= --version \ + $(git help -a|egrep '^ ') \ $(__git_aliases)" \ -- "${COMP_WORDS[COMP_CWORD]}")) - else - local command="${COMP_WORDS[1]}" - local expansion=$(__git_aliased_command "$command") + return; + fi - if [ "$expansion" ]; then - command="$expansion" - fi + local expansion=$(__git_aliased_command "$command") + [ "$expansion" ] && command="$expansion" - case "$command" in - branch) _git_branch ;; - cat-file) _git_cat_file ;; - checkout) _git_checkout ;; - diff) _git_diff ;; - diff-tree) _git_diff_tree ;; - fetch) _git_fetch ;; - log) _git_log ;; - ls-remote) _git_ls_remote ;; - ls-tree) _git_ls_tree ;; - merge-base) _git_merge_base ;; - pull) _git_pull ;; - push) _git_push ;; - reset) _git_reset ;; - show) _git_show ;; - show-branch) _git_log ;; - whatchanged) _git_log ;; - *) COMPREPLY=() ;; - esac - fi + case "$command" in + branch) _git_branch ;; + cat-file) _git_cat_file ;; + checkout) _git_checkout ;; + diff) _git_diff ;; + diff-tree) _git_diff_tree ;; + fetch) _git_fetch ;; + log) _git_log ;; + ls-remote) _git_ls_remote ;; + ls-tree) _git_ls_tree ;; + merge-base) _git_merge_base ;; + pull) _git_pull ;; + push) _git_push ;; + reset) _git_reset ;; + show) _git_show ;; + show-branch) _git_log ;; + whatchanged) _git_log ;; + *) COMPREPLY=() ;; + esac } _gitk () { local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "--all $(__git_refs .)" -- "$cur")) + COMPREPLY=($(compgen -W "--all $(__git_refs)" -- "$cur")) } complete -o default -o nospace -F _git git -- cgit v1.2.3 From e5d5b21fdf0ec0aebbfaca814a15d15a718544a2 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 5 Nov 2006 06:24:56 -0500 Subject: Support bash completion on symmetric difference operator. Now that log, whatchanged, rev-list, etc. support the symmetric difference operator '...' we should provide bash completion for it just like we do for '..'. While we are at it we can remove two sed invocations during the interactive prompt and replace them with internal bash operations. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f258f2f03e..e4a32b61b2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -222,11 +222,16 @@ _git_ls_tree () _git_log () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local pfx cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in + *...*) + pfx="${cur%...*}..." + cur="${cur#*...}" + COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur")) + ;; *..*) - local pfx=$(echo "$cur" | sed 's/\.\..*$/../') - cur=$(echo "$cur" | sed 's/^.*\.\.//') + pfx="${cur%..*}.." + cur="${cur#*..}" COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur")) ;; *) -- cgit v1.2.3 From a79c6551a315b26f2461dd9734defce88014b77b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 5 Nov 2006 06:25:25 -0500 Subject: Remove more sed invocations from within bash completion. This change removes between 1 and 4 sed invocations per completion entered by the user. In the case of cat-file the 4 invocations per completion can take a while on Cygwin; running these replacements directly within bash saves some time for the end user. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e4a32b61b2..a43a177160 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -83,15 +83,15 @@ __git_remotes () __git_complete_file () { - local cur="${COMP_WORDS[COMP_CWORD]}" + local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in ?*:*) - local pfx ls ref="$(echo "$cur" | sed 's,:.*$,,')" - cur="$(echo "$cur" | sed 's,^.*:,,')" + ref="${cur%%:*}" + cur="${cur#*:}" case "$cur" in ?*/*) - pfx="$(echo "$cur" | sed 's,/[^/]*$,,')" - cur="$(echo "$cur" | sed 's,^.*/,,')" + pfx="${cur%/*}" + cur="${cur##*/}" ls="$ref:$pfx" pfx="$pfx/" ;; @@ -193,7 +193,7 @@ _git_fetch () *) case "$cur" in *:*) - cur=$(echo "$cur" | sed 's/^.*://') + cur="${cur#*:}" COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) ;; *) @@ -287,7 +287,7 @@ _git_push () git-push) remote="${COMP_WORDS[1]}" ;; git) remote="${COMP_WORDS[2]}" ;; esac - cur=$(echo "$cur" | sed 's/^.*://') + cur="${cur#*:}" COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur")) ;; *) -- cgit v1.2.3 From d8e812502f1c72f5ef542de7eb05874e27f2b086 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 19 Nov 2006 17:28:51 +0100 Subject: shortlog: read mailmap from ./.mailmap again While at it, remove the linux specific mailmap into contrib/mailmap.linux. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/mailmap.linux | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 contrib/mailmap.linux (limited to 'contrib') diff --git a/contrib/mailmap.linux b/contrib/mailmap.linux new file mode 100644 index 0000000000..83927c94ee --- /dev/null +++ b/contrib/mailmap.linux @@ -0,0 +1,40 @@ +# +# Even with git, we don't always have name translations. +# So have an email->real name table to translate the +# (hopefully few) missing names +# +Adrian Bunk +Andreas Herrmann +Andrew Morton +Andrew Vasquez +Christoph Hellwig +Corey Minyard +David Woodhouse +Domen Puncer +Douglas Gilbert +Ed L Cashin +Evgeniy Polyakov +Felix Moeller +Frank Zago +Greg Kroah-Hartman +James Bottomley +James Bottomley +Jeff Garzik +Jens Axboe +Kay Sievers +Mitesh shah +Morten Welinder +Morten Welinder +Morten Welinder +Morten Welinder +Nguyen Anh Quynh +Paolo 'Blaisorblade' Giarrusso +Peter A Jonsson +Ralf Wildenhues +Rudolf Marek +Rui Saraiva +Sachin P Sant +Santtu Hyrkk,Av(B +Simon Kelley +Tejun Heo +Tony Luck -- cgit v1.2.3 From 7595e2ee6ef9b35ebc8dc45543723e1d89765ce3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 25 Nov 2006 00:07:54 -0800 Subject: git-shortlog: make common repository prefix configurable with .mailmap The code had "/pub/scm/linux/kernel/git/" hardcoded which was too specific to the kernel project. With this, a line in the .mailmap file: # repo-abbrev: /pub/scm/linux/kernel/git/ can be used to cause the substring to be abbreviated to /.../ on the title line of the commit message. Signed-off-by: Junio C Hamano --- contrib/mailmap.linux | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/mailmap.linux b/contrib/mailmap.linux index 83927c94ee..e4907f80f1 100644 --- a/contrib/mailmap.linux +++ b/contrib/mailmap.linux @@ -3,6 +3,8 @@ # So have an email->real name table to translate the # (hopefully few) missing names # +# repo-abbrev: /pub/scm/linux/kernel/git/ +# Adrian Bunk Andreas Herrmann Andrew Morton -- cgit v1.2.3 From 4ad91321ee95598c2488ab5e39afec13575d4e3f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 03:40:47 -0500 Subject: Teach git-completion.bash how to complete git-merge. Now that git-merge is high-level Porcelain users are going to expect to be able to use it from the command line, in which case we really should also be able to complete ref names as parameters. I'm also including completion support for the merge strategies that are supported by git-merge.sh, should the user wish to use a different strategy than their default. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a43a177160..28bd0e3ba7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -81,6 +81,16 @@ __git_remotes () done } +__git_merge_strategies () +{ + sed -n "/^all_strategies='/{ + s/^all_strategies='// + s/'// + p + q + }" "$(git --exec-path)/git-merge" +} + __git_complete_file () { local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}" @@ -240,6 +250,24 @@ _git_log () esac } +_git_merge () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + COMPREPLY=($(compgen -W " + --no-commit --no-summary --squash + " -- "$cur")) + return + esac + if [ $COMP_CWORD -gt 1 -a X-s = "X${COMP_WORDS[COMP_CWORD-1]}" ] + then + COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur")) + else + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + fi +} + _git_merge_base () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -348,6 +376,7 @@ _git () log) _git_log ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; + merge) _git_merge;; merge-base) _git_merge_base ;; pull) _git_pull ;; push) _git_push ;; @@ -376,6 +405,7 @@ complete -o default -o nospace -F _git_fetch git-fetch complete -o default -o nospace -F _git_log git-log complete -o default -F _git_ls_remote git-ls-remote complete -o default -o nospace -F _git_ls_tree git-ls-tree +complete -o default -F _git_merge git-merge complete -o default -F _git_merge_base git-merge-base complete -o default -o nospace -F _git_pull git-pull complete -o default -o nospace -F _git_push git-push -- cgit v1.2.3 From f2bb9f88805f370b9de1c116f42e3ceb30321c80 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 03:41:01 -0500 Subject: Hide plumbing/transport commands from bash completion. Users generally are not going to need to invoke plumbing-level commands from within one line shell commands. If they are invoking these commands then it is likely that they are glueing them together into a shell script to perform an action, in which case bash completion for these commands is of relatively little use. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 62 +++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 28bd0e3ba7..b55431fbf2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -125,6 +125,58 @@ __git_complete_file () esac } +__git_commands () +{ + local i IFS=" "$'\n' + for i in $(git help -a|egrep '^ ') + do + case $i in + check-ref-format) : plumbing;; + commit-tree) : plumbing;; + convert-objects) : plumbing;; + cvsserver) : daemon;; + daemon) : daemon;; + fetch-pack) : plumbing;; + hash-object) : plumbing;; + http-*) : transport;; + index-pack) : plumbing;; + local-fetch) : plumbing;; + mailinfo) : plumbing;; + mailsplit) : plumbing;; + merge-*) : plumbing;; + mktree) : plumbing;; + mktag) : plumbing;; + pack-objects) : plumbing;; + pack-redundant) : plumbing;; + pack-refs) : plumbing;; + parse-remote) : plumbing;; + patch-id) : plumbing;; + peek-remote) : plumbing;; + read-tree) : plumbing;; + receive-pack) : plumbing;; + rerere) : plumbing;; + rev-list) : plumbing;; + rev-parse) : plumbing;; + runstatus) : plumbing;; + sh-setup) : internal;; + shell) : daemon;; + send-pack) : plumbing;; + show-index) : plumbing;; + ssh-*) : transport;; + stripspace) : plumbing;; + symbolic-ref) : plumbing;; + unpack-file) : plumbing;; + unpack-objects) : plumbing;; + update-ref) : plumbing;; + update-server-info) : daemon;; + upload-archive) : plumbing;; + upload-pack) : plumbing;; + write-tree) : plumbing;; + *) echo $i;; + esac + done +} + __git_aliases () { local i IFS=$'\n' @@ -355,11 +407,11 @@ _git () done if [ $c -eq $COMP_CWORD -a -z "$command" ]; then - COMPREPLY=($(compgen \ - -W "--git-dir= --version \ - $(git help -a|egrep '^ ') \ - $(__git_aliases)" \ - -- "${COMP_WORDS[COMP_CWORD]}")) + COMPREPLY=($(compgen -W " + --git-dir= --version --exec-path + $(__git_commands) + $(__git_aliases) + " -- "${COMP_WORDS[COMP_CWORD]}")) return; fi -- cgit v1.2.3 From d33909bf6e78e6ab8a45bfdd19bc19213fde5501 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 03:41:12 -0500 Subject: Teach bash how to complete options for git-name-rev. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b55431fbf2..1dfb59207a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -326,6 +326,12 @@ _git_merge_base () COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } +_git_name_rev () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + COMPREPLY=($(compgen -W "--tags --all --stdin" -- "$cur")) +} + _git_pull () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -430,6 +436,7 @@ _git () ls-tree) _git_ls_tree ;; merge) _git_merge;; merge-base) _git_merge_base ;; + name-rev) _git_name_rev ;; pull) _git_pull ;; push) _git_push ;; reset) _git_reset ;; @@ -459,6 +466,7 @@ complete -o default -F _git_ls_remote git-ls-remote complete -o default -o nospace -F _git_ls_tree git-ls-tree complete -o default -F _git_merge git-merge complete -o default -F _git_merge_base git-merge-base +complete -o default -F _git_name_rev git-name-rev complete -o default -o nospace -F _git_pull git-pull complete -o default -o nospace -F _git_push git-push complete -o default -F _git_reset git-reset @@ -479,6 +487,7 @@ complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe complete -o default -o nospace -F _git_log git-log.exe complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe complete -o default -F _git_merge_base git-merge-base.exe +complete -o default -F _git_name_rev git-name-rev.exe complete -o default -o nospace -F _git_push git-push.exe complete -o default -o nospace -F _git_log git-show-branch.exe complete -o default -o nospace -F _git_log git-whatchanged.exe -- cgit v1.2.3 From d3d717a4ad0c8d7329e79f7d0313baec57c6b585 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 03:41:28 -0500 Subject: Add current branch in PS1 support to git-completion.bash. Many users want to display the current branch name of the current git repository as part of their PS1 prompt, much as their PS1 prompt might also display the current working directory name. We don't force our own PS1 onto the user. Instead we let them craft their own PS1 string and offer them the function __git_ps1 which they can invoke to obtain either "" (when not in a git repository) or "(%s)" where %s is the name of the current branch, as read from HEAD, with the leading refs/heads/ removed. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1dfb59207a..a740d05e28 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -18,12 +18,31 @@ # 2) Added the following line to your .bashrc: # source ~/.git-completion.sh # +# 3) Consider changing your PS1 to also show the current branch: +# PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ ' +# +# The argument to __git_ps1 will be displayed only if you +# are currently in a git repository. The %s token will be +# the name of the current branch. +# __gitdir () { echo "${__git_dir:-$(git rev-parse --git-dir 2>/dev/null)}" } +__git_ps1 () +{ + local b="$(git symbolic-ref HEAD 2>/dev/null)" + if [ -n "$b" ]; then + if [ -n "$1" ]; then + printf "$1" "${b##refs/heads/}" + else + printf " (%s)" "${b##refs/heads/}" + fi + fi +} + __git_refs () { local cmd i is_hash=y dir="${1:-$(__gitdir)}" -- cgit v1.2.3 From f53352fbaf2bcc6d209388bb2a68d888ceb8014e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 03:41:43 -0500 Subject: Teach bash how to complete git-format-patch. Provide completion for currently known long options supported by git-format-patch as well as the revision list specification argument, which is generally either a refname or in the form a..b. Since _git_log was the only code that knew how to complete a..b, but we want to start adding option support to _git_log also refactor the a..b completion logic out into its own function. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 60 +++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 16 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a740d05e28..dfdc3968b8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -144,6 +144,26 @@ __git_complete_file () esac } +__git_complete_revlist () +{ + local pfx cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + *...*) + pfx="${cur%...*}..." + cur="${cur#*...}" + COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur")) + ;; + *..*) + pfx="${cur%..*}.." + cur="${cur#*..}" + COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur")) + ;; + *) + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + ;; + esac +} + __git_commands () { local i IFS=" "$'\n' @@ -290,6 +310,26 @@ _git_fetch () esac } +_git_format_patch () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + COMPREPLY=($(compgen -W " + --stdout --attach --thread + --output-directory + --numbered --start-number + --keep-subject + --signoff + --in-reply-to= + --full-index --binary + " -- "$cur")) + return + ;; + esac + __git_complete_revlist +} + _git_ls_remote () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -303,22 +343,7 @@ _git_ls_tree () _git_log () { - local pfx cur="${COMP_WORDS[COMP_CWORD]}" - case "$cur" in - *...*) - pfx="${cur%...*}..." - cur="${cur#*...}" - COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur")) - ;; - *..*) - pfx="${cur%..*}.." - cur="${cur#*..}" - COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur")) - ;; - *) - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) - ;; - esac + __git_complete_revlist } _git_merge () @@ -450,6 +475,7 @@ _git () diff) _git_diff ;; diff-tree) _git_diff_tree ;; fetch) _git_fetch ;; + format-patch) _git_format_patch ;; log) _git_log ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; @@ -480,6 +506,7 @@ complete -o default -F _git_checkout git-checkout complete -o default -o nospace -F _git_diff git-diff complete -o default -F _git_diff_tree git-diff-tree complete -o default -o nospace -F _git_fetch git-fetch +complete -o default -o nospace -F _git_format_patch git-format-patch complete -o default -o nospace -F _git_log git-log complete -o default -F _git_ls_remote git-ls-remote complete -o default -o nospace -F _git_ls_tree git-ls-tree @@ -503,6 +530,7 @@ complete -o default -F _git_branch git-branch.exe complete -o default -o nospace -F _git_cat_file git-cat-file.exe complete -o default -o nospace -F _git_diff git-diff.exe complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe +complete -o default -o nospace -F _git_format_patch git-format-patch.exe complete -o default -o nospace -F _git_log git-log.exe complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe complete -o default -F _git_merge_base git-merge-base.exe -- cgit v1.2.3 From 1273231ee9c7a576a3654d8f2ba77267393564ab Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 03:41:59 -0500 Subject: Teach bash how to complete git-cherry-pick. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index dfdc3968b8..5582561acd 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -269,6 +269,21 @@ _git_checkout () COMPREPLY=($(compgen -W "-l -b $(__git_refs)" -- "$cur")) } +_git_cherry_pick () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + COMPREPLY=($(compgen -W " + --edit --no-commit + " -- "$cur")) + ;; + *) + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + ;; + esac +} + _git_diff () { __git_complete_file @@ -472,6 +487,7 @@ _git () branch) _git_branch ;; cat-file) _git_cat_file ;; checkout) _git_checkout ;; + cherry-pick) _git_cherry_pick ;; diff) _git_diff ;; diff-tree) _git_diff_tree ;; fetch) _git_fetch ;; @@ -503,6 +519,7 @@ complete -o default -F _gitk gitk complete -o default -F _git_branch git-branch complete -o default -o nospace -F _git_cat_file git-cat-file complete -o default -F _git_checkout git-checkout +complete -o default -F _git_cherry_pick git-cherry-pick complete -o default -o nospace -F _git_diff git-diff complete -o default -F _git_diff_tree git-diff-tree complete -o default -o nospace -F _git_fetch git-fetch -- cgit v1.2.3 From 61d926a3cdb8f03147580de53e448fc22370cbb1 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 03:42:07 -0500 Subject: Teach bash how to complete git-rebase. As git-rebase is a popular command bash should know how to complete reference names and its long options. We only support completions which make sense given the current state of the repository, that way users don't get shown --continue/--skip/--abort on the first execution. Also added support for long option --strategy to git-merge, as I missed that option earlier and just noticed it while implementing git-rebase. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 38 ++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 5582561acd..9f63ff62f8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -367,16 +367,16 @@ _git_merge () case "$cur" in --*) COMPREPLY=($(compgen -W " - --no-commit --no-summary --squash + --no-commit --no-summary --squash --strategy " -- "$cur")) return esac - if [ $COMP_CWORD -gt 1 -a X-s = "X${COMP_WORDS[COMP_CWORD-1]}" ] - then + case "${COMP_WORDS[COMP_CWORD-1]}" in + -s|--strategy) COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur")) - else - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) - fi + return + esac + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } _git_merge_base () @@ -443,6 +443,30 @@ _git_push () esac } +_git_rebase () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + if [ -d .dotest ]; then + COMPREPLY=($(compgen -W " + --continue --skip --abort + " -- "$cur")) + return + fi + case "$cur" in + --*) + COMPREPLY=($(compgen -W " + --onto --merge --strategy + " -- "$cur")) + return + esac + case "${COMP_WORDS[COMP_CWORD-1]}" in + -s|--strategy) + COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur")) + return + esac + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) +} + _git_reset () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -500,6 +524,7 @@ _git () name-rev) _git_name_rev ;; pull) _git_pull ;; push) _git_push ;; + rebase) _git_rebase ;; reset) _git_reset ;; show) _git_show ;; show-branch) _git_log ;; @@ -532,6 +557,7 @@ complete -o default -F _git_merge_base git-merge-base complete -o default -F _git_name_rev git-name-rev complete -o default -o nospace -F _git_pull git-pull complete -o default -o nospace -F _git_push git-push +complete -o default -F _git_rebase git-rebase complete -o default -F _git_reset git-reset complete -o default -F _git_show git-show complete -o default -o nospace -F _git_log git-show-branch -- cgit v1.2.3 From 6e31b866e4c8e6fc432e6087d56be73c08cf0f83 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 03:42:18 -0500 Subject: Teach bash about git log/show/whatchanged options. Typing out options to git log/show/whatchanged can take a while, but we can easily complete them with bash. So list the most common ones, especially --pretty=online|short|medium|... so that users don't need to type everything out. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9f63ff62f8..3852f467d8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -358,6 +358,29 @@ _git_ls_tree () _git_log () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --pretty=*) + COMPREPLY=($(compgen -W " + oneline short medium full fuller email raw + " -- "${cur##--pretty=}")) + return + ;; + --*) + COMPREPLY=($(compgen -W " + --max-count= --max-age= --since= --after= + --min-age= --before= --until= + --root --not --topo-order --date-order + --no-merges + --abbrev-commit --abbrev= + --relative-date + --author= --committer= --grep= + --all-match + --pretty= --name-status --name-only + " -- "$cur")) + return + ;; + esac __git_complete_revlist } @@ -474,12 +497,6 @@ _git_reset () COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur")) } -_git_show () -{ - local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) -} - _git () { local i c=1 command __git_dir @@ -526,7 +543,7 @@ _git () push) _git_push ;; rebase) _git_rebase ;; reset) _git_reset ;; - show) _git_show ;; + show) _git_log ;; show-branch) _git_log ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; @@ -559,7 +576,7 @@ complete -o default -o nospace -F _git_pull git-pull complete -o default -o nospace -F _git_push git-push complete -o default -F _git_rebase git-rebase complete -o default -F _git_reset git-reset -complete -o default -F _git_show git-show +complete -o default -F _git_log git-show complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged @@ -579,6 +596,7 @@ complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe complete -o default -F _git_merge_base git-merge-base.exe complete -o default -F _git_name_rev git-name-rev.exe complete -o default -o nospace -F _git_push git-push.exe +complete -o default -o nospace -F _git_log git-show.exe complete -o default -o nospace -F _git_log git-show-branch.exe complete -o default -o nospace -F _git_log git-whatchanged.exe fi -- cgit v1.2.3 From 35e65ecca78ceeca8eca72149e7546de94ed8607 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 03:42:32 -0500 Subject: Support bash completion of refs/remote. Now that people are really likely to start using separate remotes (due to the default in git-clone changing) we should support ref completion for these refs in as many commands as possible. While we are working on this routine we should use for-each-ref to obtain a list of local refs, as this should run faster than peek-remote as it does not need to dereference tag objects in order to produce the list of refs back to us. It should also be more friendly to users of StGIT as we won't generate a list of the StGIT metadata refs. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3852f467d8..a9c456f61b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -47,16 +47,26 @@ __git_refs () { local cmd i is_hash=y dir="${1:-$(__gitdir)}" if [ -d "$dir" ]; then - cmd=git-peek-remote - else - cmd=git-ls-remote + if [ -e "$dir/HEAD" ]; then echo HEAD; fi + for i in $(git --git-dir="$dir" \ + for-each-ref --format='%(refname)' \ + refs/tags refs/heads refs/remotes); do + case "$i" in + refs/tags/*) echo "${i#refs/tags/}" ;; + refs/heads/*) echo "${i#refs/heads/}" ;; + refs/remotes/*) echo "${i#refs/remotes/}" ;; + *) echo "$i" ;; + esac + done + return fi - for i in $($cmd "$dir" 2>/dev/null); do + for i in $(git-ls-remote "$dir" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;; n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;; + n,refs/remotes/*) is_hash=y; echo "${i#refs/remotes/}" ;; n,*) is_hash=y; echo "$i" ;; esac done -- cgit v1.2.3 From 5de40f59d4954738448e238b06eed72f73cca740 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 04:44:47 -0500 Subject: Teach bash about git-repo-config. This is a really ugly completion script for git-repo-config, but it has some nice properties. I've added all of the documented configuration parameters from Documentation/config.txt to the script, allowing the user to complete any standard configuration parameter name. We also have some intelligence for the remote.*.* and branch.*.* keys by completing not only the key name (e.g. remote.origin) but also the values (e.g. remote.*.fetch completes to the branches available on the corresponding remote). Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 154 +++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a9c456f61b..73c67691c9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -43,6 +43,27 @@ __git_ps1 () fi } +__git_heads () +{ + local cmd i is_hash=y dir="${1:-$(__gitdir)}" + if [ -d "$dir" ]; then + for i in $(git --git-dir="$dir" \ + for-each-ref --format='%(refname)' \ + refs/heads ); do + echo "${i#refs/heads/}" + done + return + fi + for i in $(git-ls-remote "$dir" 2>/dev/null); do + case "$is_hash,$i" in + y,*) is_hash=n ;; + n,*^{}) is_hash=y ;; + n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;; + n,*) is_hash=y; echo "$i" ;; + esac + done +} + __git_refs () { local cmd i is_hash=y dir="${1:-$(__gitdir)}" @@ -91,6 +112,23 @@ __git_refs2 () done } +__git_refs_remotes () +{ + local cmd i is_hash=y + for i in $(git-ls-remote "$1" 2>/dev/null); do + case "$is_hash,$i" in + n,refs/heads/*) + is_hash=y + echo "$i:refs/remotes/$1/${i#refs/heads/}" + ;; + y,*) is_hash=n ;; + n,*^{}) is_hash=y ;; + n,refs/tags/*) is_hash=y;; + n,*) is_hash=y; ;; + esac + done +} + __git_remotes () { local i ngoff IFS=$'\n' d="$(__gitdir)" @@ -500,6 +538,119 @@ _git_rebase () COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } +_git_repo_config () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + local prv="${COMP_WORDS[COMP_CWORD-1]}" + case "$prv" in + branch.*.remote) + COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + return + ;; + branch.*.merge) + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + return + ;; + remote.*.fetch) + local remote="${prv#remote.}" + remote="${remote%.fetch}" + COMPREPLY=($(compgen -W "$(__git_refs_remotes "$remote")" \ + -- "$cur")) + return + ;; + remote.*.push) + local remote="${prv#remote.}" + remote="${remote%.push}" + COMPREPLY=($(compgen -W "$(git --git-dir="$(__gitdir)" \ + for-each-ref --format='%(refname):%(refname)' \ + refs/heads)" -- "$cur")) + return + ;; + *.*) + COMPREPLY=() + return + ;; + esac + case "$cur" in + --*) + COMPREPLY=($(compgen -W " + --global --list --replace-all + --get --get-all --get-regexp + --unset --unset-all + " -- "$cur")) + return + ;; + branch.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + COMPREPLY=($(compgen -P "$pfx" -W "remote merge" -- "$cur")) + return + ;; + branch.*) + local pfx="${cur%.*}." + cur="${cur#*.}" + COMPREPLY=($(compgen -P "$pfx" -S . \ + -W "$(__git_heads)" -- "$cur")) + return + ;; + remote.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + COMPREPLY=($(compgen -P "$pfx" -W "url fetch push" -- "$cur")) + return + ;; + remote.*) + local pfx="${cur%.*}." + cur="${cur#*.}" + COMPREPLY=($(compgen -P "$pfx" -S . \ + -W "$(__git_remotes)" -- "$cur")) + return + ;; + esac + COMPREPLY=($(compgen -W " + apply.whitespace + core.fileMode + core.gitProxy + core.ignoreStat + core.preferSymlinkRefs + core.logAllRefUpdates + core.repositoryFormatVersion + core.sharedRepository + core.warnAmbiguousRefs + core.compression + core.legacyHeaders + i18n.commitEncoding + diff.color + diff.renameLimit + diff.renames + pager.color + status.color + log.showroot + show.difftree + showbranch.default + whatchanged.difftree + http.sslVerify + http.sslCert + http.sslKey + http.sslCAInfo + http.sslCAPath + http.maxRequests + http.lowSpeedLimit http.lowSpeedTime + http.noEPSV + pack.window + repack.useDeltaBaseOffset + pull.octopus pull.twohead + merge.summary + receive.unpackLimit + receive.denyNonFastForwards + user.name user.email + tar.umask + gitcvs.enabled + gitcvs.logfile + branch. remote. + " -- "$cur")) +} + _git_reset () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -552,6 +703,7 @@ _git () pull) _git_pull ;; push) _git_push ;; rebase) _git_rebase ;; + repo-config) _git_repo_config ;; reset) _git_reset ;; show) _git_log ;; show-branch) _git_log ;; @@ -585,6 +737,7 @@ complete -o default -F _git_name_rev git-name-rev complete -o default -o nospace -F _git_pull git-pull complete -o default -o nospace -F _git_push git-push complete -o default -F _git_rebase git-rebase +complete -o default -F _git_repo_config git-repo-config complete -o default -F _git_reset git-reset complete -o default -F _git_log git-show complete -o default -o nospace -F _git_log git-show-branch @@ -606,6 +759,7 @@ complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe complete -o default -F _git_merge_base git-merge-base.exe complete -o default -F _git_name_rev git-name-rev.exe complete -o default -o nospace -F _git_push git-push.exe +complete -o default -F _git_repo_config git-repo-config complete -o default -o nospace -F _git_log git-show.exe complete -o default -o nospace -F _git_log git-show-branch.exe complete -o default -o nospace -F _git_log git-whatchanged.exe -- cgit v1.2.3 From ce1e39d29ee373786ceda9e79d0906a6451ab5a5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 15:10:42 -0500 Subject: Support --strategy=x completion in addition to --strategy x. Because git-merge and git-rebase both accept -s, --strategy or --strategy= we should recognize all three formats in the bash completion functions and issue back all merge strategies on demand. I also moved the prior word testing to be before the current word testing, as the current word cannot be completed with -- if the prior word was an option which requires a parameter, such as -s or --strategy. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 73c67691c9..16b8dda17d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -435,18 +435,23 @@ _git_log () _git_merge () { local cur="${COMP_WORDS[COMP_CWORD]}" + case "${COMP_WORDS[COMP_CWORD-1]}" in + -s|--strategy) + COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur")) + return + esac case "$cur" in + --strategy=*) + COMPREPLY=($(compgen -W "$(__git_merge_strategies)" \ + -- "${cur##--strategy=}")) + return + ;; --*) COMPREPLY=($(compgen -W " --no-commit --no-summary --squash --strategy " -- "$cur")) return esac - case "${COMP_WORDS[COMP_CWORD-1]}" in - -s|--strategy) - COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur")) - return - esac COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } @@ -523,18 +528,23 @@ _git_rebase () " -- "$cur")) return fi + case "${COMP_WORDS[COMP_CWORD-1]}" in + -s|--strategy) + COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur")) + return + esac case "$cur" in + --strategy=*) + COMPREPLY=($(compgen -W "$(__git_merge_strategies)" \ + -- "${cur##--strategy=}")) + return + ;; --*) COMPREPLY=($(compgen -W " --onto --merge --strategy " -- "$cur")) return esac - case "${COMP_WORDS[COMP_CWORD-1]}" in - -s|--strategy) - COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur")) - return - esac COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } -- cgit v1.2.3 From b51ec6bddb572def384a21a18d5919a488e06ffb Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 15:11:52 -0500 Subject: Cache the list of merge strategies and available commands during load. Since the user's git installation is not likely to grow a new command or merge strategy in the lifespan of the current shell process we can save time during completion operations by caching these lists during sourcing of the completion support. If the git executable is not available or we run into errors while caching at load time then we defer these to runtime and generate the list on the fly. This might happen if the user doesn't put git into their PATH until after the completion script gets sourced. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 16b8dda17d..902f80b937 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -18,7 +18,13 @@ # 2) Added the following line to your .bashrc: # source ~/.git-completion.sh # -# 3) Consider changing your PS1 to also show the current branch: +# 3) You may want to make sure the git executable is available +# in your PATH before this script is sourced, as some caching +# is performed while the script loads. If git isn't found +# at source time then all lookups will be done on demand, +# which may be slightly slower. +# +# 4) Consider changing your PS1 to also show the current branch: # PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ ' # # The argument to __git_ps1 will be displayed only if you @@ -150,6 +156,10 @@ __git_remotes () __git_merge_strategies () { + if [ -n "$__git_merge_strategylist" ]; then + echo "$__git_merge_strategylist" + return + fi sed -n "/^all_strategies='/{ s/^all_strategies='// s/'// @@ -157,6 +167,8 @@ __git_merge_strategies () q }" "$(git --exec-path)/git-merge" } +__git_merge_strategylist= +__git_merge_strategylist="$(__git_merge_strategies 2>/dev/null)" __git_complete_file () { @@ -214,6 +226,10 @@ __git_complete_revlist () __git_commands () { + if [ -n "$__git_commandlist" ]; then + echo "$__git_commandlist" + return + fi local i IFS=" "$'\n' for i in $(git help -a|egrep '^ ') do @@ -263,6 +279,8 @@ __git_commands () esac done } +__git_commandlist= +__git_commandlist="$(__git_commands 2>/dev/null)" __git_aliases () { -- cgit v1.2.3 From 88329195159865c0fcc57a6017c959d13d7a1ad6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 27 Nov 2006 15:12:03 -0500 Subject: Teach bash about git-am/git-apply and their whitespace options. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 902f80b937..d8ae4d7886 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -307,6 +307,54 @@ __git_aliased_command () done } +__git_whitespacelist="nowarn warn error error-all strip" + +_git_am () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + if [ -d .dotest ]; then + COMPREPLY=($(compgen -W " + --skip --resolved + " -- "$cur")) + return + fi + case "$cur" in + --whitespace=*) + COMPREPLY=($(compgen -W "$__git_whitespacelist" \ + -- "${cur##--whitespace=}")) + return + ;; + --*) + COMPREPLY=($(compgen -W " + --signoff --utf8 --binary --3way --interactive + --whitespace= + " -- "$cur")) + return + esac + COMPREPLY=() +} + +_git_apply () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --whitespace=*) + COMPREPLY=($(compgen -W "$__git_whitespacelist" \ + -- "${cur##--whitespace=}")) + return + ;; + --*) + COMPREPLY=($(compgen -W " + --stat --numstat --summary --check --index + --cached --index-info --reverse --reject --unidiff-zero + --apply --no-add --exclude= + --whitespace= --inaccurate-eof --verbose + " -- "$cur")) + return + esac + COMPREPLY=() +} + _git_branch () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -714,6 +762,8 @@ _git () [ "$expansion" ] && command="$expansion" case "$command" in + am) _git_am ;; + apply) _git_apply ;; branch) _git_branch ;; cat-file) _git_cat_file ;; checkout) _git_checkout ;; @@ -748,6 +798,8 @@ _gitk () complete -o default -o nospace -F _git git complete -o default -F _gitk gitk +complete -o default -F _git_am git-am +complete -o default -F _git_apply git-apply complete -o default -F _git_branch git-branch complete -o default -o nospace -F _git_cat_file git-cat-file complete -o default -F _git_checkout git-checkout @@ -776,6 +828,7 @@ complete -o default -o nospace -F _git_log git-whatchanged # included the '.exe' suffix. # if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then +complete -o default -F _git_apply git-apply.exe complete -o default -o nospace -F _git git.exe complete -o default -F _git_branch git-branch.exe complete -o default -o nospace -F _git_cat_file git-cat-file.exe -- cgit v1.2.3 From 4548e855e4600a0f76329cf4f0dd9e8d17d66b08 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 28 Nov 2006 12:12:08 -0500 Subject: Teach bash how to complete long options for git-commit. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d8ae4d7886..be978cf3d3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -398,6 +398,20 @@ _git_cherry_pick () esac } +_git_commit () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + COMPREPLY=($(compgen -W " + --all --author= --signoff --verify --no-verify + --edit --amend --include --only + " -- "$cur")) + return + esac + COMPREPLY=() +} + _git_diff () { __git_complete_file @@ -768,6 +782,7 @@ _git () cat-file) _git_cat_file ;; checkout) _git_checkout ;; cherry-pick) _git_cherry_pick ;; + commit) _git_commit ;; diff) _git_diff ;; diff-tree) _git_diff_tree ;; fetch) _git_fetch ;; @@ -804,6 +819,7 @@ complete -o default -F _git_branch git-branch complete -o default -o nospace -F _git_cat_file git-cat-file complete -o default -F _git_checkout git-checkout complete -o default -F _git_cherry_pick git-cherry-pick +complete -o default -F _git_commit git-commit complete -o default -o nospace -F _git_diff git-diff complete -o default -F _git_diff_tree git-diff-tree complete -o default -o nospace -F _git_fetch git-fetch -- cgit v1.2.3 From 67ffa1142585742601011440a17528ef841bbf44 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 28 Nov 2006 12:12:26 -0500 Subject: Fix broken bash completion of local refs. Commit 35e65ecc broke completion of local refs, e.g. "git pull . fo" no longer would complete to "foo". Instead it printed out an internal git error ("fatal: Not a git repository: '.'"). The break occurred when I tried to improve performance by switching from git-peek-remote to git-for-each-ref. Apparently git-peek-remote will drop into directory "$1/.git" (where $1 is its first parameter) if it is given a repository with a working directory. This allowed the bash completion code to work properly even though it was not handing over the true repository directory. So now we do a stat in bash to see if we need to add "/.git" to the path string before running any command with --git-dir. I also tried to optimize away two "git rev-parse --git-dir" invocations in common cases like "git log fo" as typically the user is in the top level directory of their project and therefore the .git subdirectory is in the current working directory. This should make a difference on systems where fork+exec might take a little while. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 37 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index be978cf3d3..447ec20467 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -34,7 +34,19 @@ __gitdir () { - echo "${__git_dir:-$(git rev-parse --git-dir 2>/dev/null)}" + if [ -z "$1" ]; then + if [ -n "$__git_dir" ]; then + echo "$__git_dir" + elif [ -d .git ]; then + echo .git + else + git rev-parse --git-dir 2>/dev/null + fi + elif [ -d "$1/.git" ]; then + echo "$1/.git" + else + echo "$1" + fi } __git_ps1 () @@ -51,7 +63,7 @@ __git_ps1 () __git_heads () { - local cmd i is_hash=y dir="${1:-$(__gitdir)}" + local cmd i is_hash=y dir="$(__gitdir "$1")" if [ -d "$dir" ]; then for i in $(git --git-dir="$dir" \ for-each-ref --format='%(refname)' \ @@ -60,7 +72,7 @@ __git_heads () done return fi - for i in $(git-ls-remote "$dir" 2>/dev/null); do + for i in $(git-ls-remote "$1" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -72,7 +84,7 @@ __git_heads () __git_refs () { - local cmd i is_hash=y dir="${1:-$(__gitdir)}" + local cmd i is_hash=y dir="$(__gitdir "$1")" if [ -d "$dir" ]; then if [ -e "$dir/HEAD" ]; then echo HEAD; fi for i in $(git --git-dir="$dir" \ @@ -101,20 +113,9 @@ __git_refs () __git_refs2 () { - local cmd i is_hash=y dir="${1:-$(__gitdir)}" - if [ -d "$dir" ]; then - cmd=git-peek-remote - else - cmd=git-ls-remote - fi - for i in $($cmd "$dir" 2>/dev/null); do - case "$is_hash,$i" in - y,*) is_hash=n ;; - n,*^{}) is_hash=y ;; - n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}:${i#refs/tags/}" ;; - n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}:${i#refs/heads/}" ;; - n,*) is_hash=y; echo "$i:$i" ;; - esac + local i + for i in $(__git_refs "$1"); do + echo "$i:$i" done } -- cgit v1.2.3 From a159ca0cb7e0acdd37cb066327dcb020d95602d0 Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Wed, 13 Dec 2006 09:13:28 +0000 Subject: Allow subcommand.color and color.subcommand color configuration While adding colour to the branch command it was pointed out that a config option like "branch.color" conflicts with the pre-existing "branch.something" namespace used for specifying default merge urls and branches. The suggested solution was to flip the order of the components to "color.branch", which I did for colourising branch. This patch does the same thing for - git-log (color.diff) - git-status (color.status) - git-diff (color.diff) - pager (color.pager) I haven't removed the old config options; but they should probably be deprecated and eventually removed to prevent future namespace collisions. I've done this deprecation by changing the documentation for the config file to match the new names; and adding the "color.XXX" options to contrib/completion/git-completion.bash. Unfortunately git-svn reads "diff.color" and "pager.color"; which I don't like to change unilaterally. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 447ec20467..9c4d23a23c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -712,10 +712,13 @@ _git_repo_config () core.legacyHeaders i18n.commitEncoding diff.color + color.diff diff.renameLimit diff.renames pager.color + color.pager status.color + color.status log.showroot show.difftree showbranch.default -- cgit v1.2.3 From 9013192449ed1148da4805e33a2644fca92dd1c7 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 15 Dec 2006 02:20:03 -0500 Subject: Teach bash the new features of 'git show'. Now that 'git show' accepts ref:path as an argument to specify a tree or blob we should use the same completion logic as we support for cat-file's object identifier. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9c4d23a23c..234cd0954b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -752,6 +752,24 @@ _git_reset () COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur")) } +_git_show () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --pretty=*) + COMPREPLY=($(compgen -W " + oneline short medium full fuller email raw + " -- "${cur##--pretty=}")) + return + ;; + --*) + COMPREPLY=($(compgen -W "--pretty=" -- "$cur")) + return + ;; + esac + __git_complete_file +} + _git () { local i c=1 command __git_dir @@ -802,7 +820,7 @@ _git () rebase) _git_rebase ;; repo-config) _git_repo_config ;; reset) _git_reset ;; - show) _git_log ;; + show) _git_show ;; show-branch) _git_log ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; @@ -839,7 +857,7 @@ complete -o default -o nospace -F _git_push git-push complete -o default -F _git_rebase git-rebase complete -o default -F _git_repo_config git-repo-config complete -o default -F _git_reset git-reset -complete -o default -F _git_log git-show +complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged @@ -861,7 +879,7 @@ complete -o default -F _git_merge_base git-merge-base.exe complete -o default -F _git_name_rev git-name-rev.exe complete -o default -o nospace -F _git_push git-push.exe complete -o default -F _git_repo_config git-repo-config -complete -o default -o nospace -F _git_log git-show.exe +complete -o default -o nospace -F _git_show git-show.exe complete -o default -o nospace -F _git_log git-show-branch.exe complete -o default -o nospace -F _git_log git-whatchanged.exe fi -- cgit v1.2.3 From ff7f22f36e2931db7683121f20ee2e03bd426096 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 18 Dec 2006 02:26:42 -0500 Subject: vim syntax: follow recent changes to commit template This patch changes the syntax highlighting to correctly match the new text of the commit message introduced by 82dca84871637ac9812c0dec27f56d07cfba524c Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/vim/syntax/gitcommit.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/vim/syntax/gitcommit.vim b/contrib/vim/syntax/gitcommit.vim index a9de09fa2f..d911efbb4b 100644 --- a/contrib/vim/syntax/gitcommit.vim +++ b/contrib/vim/syntax/gitcommit.vim @@ -1,7 +1,7 @@ syn region gitLine start=/^#/ end=/$/ -syn region gitCommit start=/^# Updated but not checked in:$/ end=/^#$/ contains=gitHead,gitCommitFile +syn region gitCommit start=/^# Added but not yet committed:$/ end=/^#$/ contains=gitHead,gitCommitFile syn region gitHead contained start=/^# (.*)/ end=/^#$/ -syn region gitChanged start=/^# Changed but not updated:/ end=/^#$/ contains=gitHead,gitChangedFile +syn region gitChanged start=/^# Changed but not added:/ end=/^#$/ contains=gitHead,gitChangedFile syn region gitUntracked start=/^# Untracked files:/ end=/^#$/ contains=gitHead,gitUntrackedFile syn match gitCommitFile contained /^#\t.*/hs=s+2 -- cgit v1.2.3 From 4de0f9f9b6ed1731de00b67952504e5a783f54a4 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 22 Dec 2006 14:56:15 +0100 Subject: vc-git: Ignore errors caused by a non-existent directory in vc-git-registered. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index 8b6361922f..3eb4bd19e9 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -58,8 +58,9 @@ (with-temp-buffer (let* ((dir (file-name-directory file)) (name (file-relative-name file dir))) - (when dir (cd dir)) - (and (ignore-errors (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name))) + (and (ignore-errors + (when dir (cd dir)) + (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name))) (let ((str (buffer-string))) (and (> (length str) (length name)) (string= (substring str 0 (1+ (length name))) (concat name "\0")))))))) -- cgit v1.2.3 From d2c11a38c476bdfa3dd2387a0d933b8c00e4dfe3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Dec 2006 16:41:33 -0800 Subject: UTF-8: introduce i18n.logoutputencoding. It is plausible for somebody to want to view the commit log in a different encoding from i18n.commitencoding -- the project's policy may be UTF-8 and the user may be using a commit message hook to run iconv to conform to that policy (and either not have i18n.commitencoding to default to UTF-8 or have it explicitly set to UTF-8). Even then, Latin-1 may be more convenient for the usual pager and the terminal the user uses. The new variable i18n.logoutputencoding is used in preference to i18n.commitencoding to decide what encoding to recode the log output in when git-log and friends formats the commit log message. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 234cd0954b..7c7520ea29 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -711,6 +711,7 @@ _git_repo_config () core.compression core.legacyHeaders i18n.commitEncoding + i18n.logOutputEncoding diff.color color.diff diff.renameLimit -- cgit v1.2.3 From cdc0873a78771926b18d9331e04f466ded1b98f2 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 6 Jan 2007 11:20:57 +0100 Subject: git.el: Don't use --info-only when resolving a file. It doesn't make a difference for git.el, but it helps when interacting with git-rebase and friends. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 972c402ea0..38915e59aa 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -777,7 +777,7 @@ and returns the process output as a string." (interactive) (let ((files (git-marked-files-state 'unmerged))) (when files - (apply #'git-run-command nil nil "update-index" "--info-only" "--" (git-get-filenames files)) + (apply #'git-run-command nil nil "update-index" "--" (git-get-filenames files)) (git-set-files-state files 'modified) (git-refresh-files)))) -- cgit v1.2.3 From 9fa77a51a49b16fa8886643d7f6346eb0a0601c5 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 6 Jan 2007 14:46:47 +0100 Subject: git.el: Avoid setting font lock keywords before entering log-edit mode. Instead, reinitialize the keywords after the fact. This avoids conflicts with other users of log-edit mode, like pcl-cvs. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 38915e59aa..ede3ab2bd8 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -49,6 +49,7 @@ (eval-when-compile (require 'cl)) (require 'ewoc) +(require 'log-edit) ;;;; Customizations @@ -147,6 +148,13 @@ if there is already one that displays the same directory." (defconst git-log-msg-separator "--- log message follows this line ---") +(defvar git-log-edit-font-lock-keywords + `(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)$" + (1 font-lock-keyword-face) + (2 font-lock-function-name-face)) + (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$") + (1 font-lock-comment-face)))) + (defun git-get-env-strings (env) "Build a list of NAME=VALUE strings from a list of environment strings." (mapcar (lambda (entry) (concat (car entry) "=" (cdr entry))) env)) @@ -894,14 +902,9 @@ and returns the process output as a string." (sign-off (insert (format "\n\nSigned-off-by: %s <%s>\n" (git-get-committer-name) (git-get-committer-email))))))) - (let ((log-edit-font-lock-keywords - `(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)" - (1 font-lock-keyword-face) - (2 font-lock-function-name-face)) - (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$") - (1 font-lock-comment-face))))) - (log-edit #'git-do-commit nil #'git-log-edit-files buffer) - (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) + (log-edit #'git-do-commit nil #'git-log-edit-files buffer) + (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) + (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))) (defun git-find-file () "Visit the current file in its own buffer." -- cgit v1.2.3 From 03d311eda2d8c2d23855b9d3e904c7648925ab56 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 9 Jan 2007 21:27:40 +0100 Subject: git.el: Define the propertize function if needed, for XEmacs compatibility. Also use `concat' instead of `format' in the pretty-printer since format doesn't preserve properties under XEmacs. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index ede3ab2bd8..d90ba816e0 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -280,6 +280,15 @@ and returns the process output as a string." (git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name))) (git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name)))) +; propertize definition for XEmacs, stolen from erc-compat +(eval-when-compile + (unless (fboundp 'propertize) + (defun propertize (string &rest props) + (let ((string (copy-sequence string))) + (while props + (put-text-property 0 (length string) (nth 0 props) (nth 1 props) string) + (setq props (cddr props))) + string)))) ;;;; Wrappers for basic git commands ;;;; ------------------------------------------------------------ @@ -448,11 +457,10 @@ and returns the process output as a string." (defun git-fileinfo-prettyprint (info) "Pretty-printer for the git-fileinfo structure." - (insert (format " %s %s %s %s%s" - (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ") - (git-status-code-as-string (git-fileinfo->state info)) - (git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info)) - (git-escape-file-name (git-fileinfo->name info)) + (insert (concat " " (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ") + " " (git-status-code-as-string (git-fileinfo->state info)) + " " (git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info)) + " " (git-escape-file-name (git-fileinfo->name info)) (git-rename-as-string info)))) (defun git-parse-status (status) -- cgit v1.2.3 From 2aad957a519b354e248da8c76ee0d3997083dde6 Mon Sep 17 00:00:00 2001 From: Jason Riedy Date: Mon, 15 Jan 2007 17:31:29 -0800 Subject: Replace "echo -n" with printf in shell scripts. Not all echos know -n. This was causing a test failure in t5401-update-hooks.sh, but not t3800-mktag.sh for some reason. Signed-off-by: Jason Riedy Signed-off-by: Junio C Hamano --- contrib/remotes2config.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh index 25901e2b3b..b996996bfb 100644 --- a/contrib/remotes2config.sh +++ b/contrib/remotes2config.sh @@ -11,7 +11,7 @@ if [ -d "$GIT_DIR"/remotes ]; then { cd "$GIT_DIR"/remotes ls | while read f; do - name=$(echo -n "$f" | tr -c "A-Za-z0-9" ".") + name=$(printf "$f" | tr -c "A-Za-z0-9" ".") sed -n \ -e "s/^URL: \(.*\)$/remote.$name.url \1 ./p" \ -e "s/^Pull: \(.*\)$/remote.$name.fetch \1 ^$ /p" \ -- cgit v1.2.3 From 222664e74dec10303bfa0c99e88f31563fdc6952 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 22 Jan 2007 22:21:15 -0500 Subject: contrib/vim: update syntax for changed commit template Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/vim/syntax/gitcommit.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/vim/syntax/gitcommit.vim b/contrib/vim/syntax/gitcommit.vim index d911efbb4b..332121b40e 100644 --- a/contrib/vim/syntax/gitcommit.vim +++ b/contrib/vim/syntax/gitcommit.vim @@ -1,7 +1,7 @@ syn region gitLine start=/^#/ end=/$/ -syn region gitCommit start=/^# Added but not yet committed:$/ end=/^#$/ contains=gitHead,gitCommitFile +syn region gitCommit start=/^# Changes to be committed:$/ end=/^#$/ contains=gitHead,gitCommitFile syn region gitHead contained start=/^# (.*)/ end=/^#$/ -syn region gitChanged start=/^# Changed but not added:/ end=/^#$/ contains=gitHead,gitChangedFile +syn region gitChanged start=/^# Changed but not updated:/ end=/^#$/ contains=gitHead,gitChangedFile syn region gitUntracked start=/^# Untracked files:/ end=/^#$/ contains=gitHead,gitUntrackedFile syn match gitCommitFile contained /^#\t.*/hs=s+2 -- cgit v1.2.3 From fd73423f01361f112dbb9972ebce8eabae7dd8b1 Mon Sep 17 00:00:00 2001 From: Sam Vilain Date: Fri, 26 Jan 2007 12:41:23 +1300 Subject: contrib/emacs/vc-git.el: support vc-version-other-window Currently, the vc-git-checkout function uses `git checkout' to fetch a file from the git repository to the working copy. However, it is completely ignoring the input argument that specifies the destination file. `git-checkout' does not support specifying this, so we have to use `git-cat-file', capture the output in a buffer and then save it. Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index 3eb4bd19e9..65c4550069 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -53,6 +53,10 @@ (let ((name (file-relative-name file))) (eq 0 (apply #'call-process "git" nil (get-buffer "*Messages") nil (append args (list name)))))) +(defun vc-git--run-command-out (output &rest args) + "Run a git command, output to output." + (eq 0 (apply #'call-process "git" nil output nil (append args)))) + (defun vc-git-registered (file) "Check whether FILE is registered with git." (with-temp-buffer @@ -120,7 +124,28 @@ (vc-git--run-command file "commit" "-m" comment "--only" "--"))) (defun vc-git-checkout (file &optional editable rev destfile) - (vc-git--run-command file "checkout" (or rev "HEAD"))) + (if destfile + (let ((mybuff (get-buffer-create "vc-git-checkout-tmp"))) + (let ((rv + (vc-git--run-command-out + mybuff "cat-file" "blob" + (concat (or rev "HEAD") + ":" + (let ((output (vc-git--run-command-string + (file-relative-name file) + "ls-files" "--full-name"))) + (string-match "\\(.*\\)" output) + (match-string 1 output)) + ))) + ) + (if rv + (save-current-buffer + (set-buffer mybuff) + (set-visited-file-name destfile t) + (save-buffer) + ) + rv))) + (vc-git--run-command file "checkout" (or rev "HEAD")))) (defun vc-git-annotate-command (file buf &optional rev) ; FIXME: rev is ignored -- cgit v1.2.3 From 40d6dc0f9d4045d237f8334224777922789c6d04 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 26 Jan 2007 11:57:50 +0100 Subject: vc-git.el: Take into account the destination name in vc-checkout. This is necessary for vc-version-other-window. Based on a patch by Sam Vilain . Currently, the vc-git-checkout function uses `git checkout' to fetch a file from the git repository to the working copy. However, it is completely ignoring the input argument that specifies the destination file. `git-checkout' does not support specifying this, so we have to use `git-cat-file', capture the output in a buffer and then save it. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index 65c4550069..e456ab9712 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -53,10 +53,6 @@ (let ((name (file-relative-name file))) (eq 0 (apply #'call-process "git" nil (get-buffer "*Messages") nil (append args (list name)))))) -(defun vc-git--run-command-out (output &rest args) - "Run a git command, output to output." - (eq 0 (apply #'call-process "git" nil output nil (append args)))) - (defun vc-git-registered (file) "Check whether FILE is registered with git." (with-temp-buffer @@ -125,26 +121,14 @@ (defun vc-git-checkout (file &optional editable rev destfile) (if destfile - (let ((mybuff (get-buffer-create "vc-git-checkout-tmp"))) - (let ((rv - (vc-git--run-command-out - mybuff "cat-file" "blob" - (concat (or rev "HEAD") - ":" - (let ((output (vc-git--run-command-string - (file-relative-name file) - "ls-files" "--full-name"))) - (string-match "\\(.*\\)" output) - (match-string 1 output)) - ))) - ) - (if rv - (save-current-buffer - (set-buffer mybuff) - (set-visited-file-name destfile t) - (save-buffer) - ) - rv))) + (let ((fullname (substring + (vc-git--run-command-string file "ls-files" "-z" "--full-name" "--") + 0 -1)) + (coding-system-for-read 'no-conversion) + (coding-system-for-write 'no-conversion)) + (with-temp-file destfile + (eq 0 (call-process "git" nil t nil "cat-file" "blob" + (concat (or rev "HEAD") ":" fullname))))) (vc-git--run-command file "checkout" (or rev "HEAD")))) (defun vc-git-annotate-command (file buf &optional rev) -- cgit v1.2.3 From cace16fdcb5d1a3518f92e04c65e78f06b0cd051 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sun, 28 Jan 2007 12:53:26 -0800 Subject: Add a sample program 'blameview' to show how to use git-blame --incremental Signed-off-by: Junio C Hamano --- contrib/blameview/README | 10 ++++ contrib/blameview/blameview.perl | 118 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 contrib/blameview/README create mode 100755 contrib/blameview/blameview.perl (limited to 'contrib') diff --git a/contrib/blameview/README b/contrib/blameview/README new file mode 100644 index 0000000000..50a6f67fd6 --- /dev/null +++ b/contrib/blameview/README @@ -0,0 +1,10 @@ +This is a sample program to use 'git-blame --incremental', based +on this message. + +From: Jeff King +Subject: Re: More precise tag following +To: Linus Torvalds +Cc: git@vger.kernel.org +Date: Sat, 27 Jan 2007 18:52:38 -0500 +Message-ID: <20070127235238.GA28706@coredump.intra.peff.net> + diff --git a/contrib/blameview/blameview.perl b/contrib/blameview/blameview.perl new file mode 100755 index 0000000000..a55f799f00 --- /dev/null +++ b/contrib/blameview/blameview.perl @@ -0,0 +1,118 @@ +#!/usr/bin/perl + +use Gtk2 -init; +use Gtk2::SimpleList; + +my $fn = shift or die "require filename to blame"; + +Gtk2::Rc->parse_string(<<'EOS'); +style "treeview_style" +{ + GtkTreeView::vertical-separator = 0 +} +class "GtkTreeView" style "treeview_style" +EOS + +my $window = Gtk2::Window->new('toplevel'); +$window->signal_connect(destroy => sub { Gtk2->main_quit }); +my $scrolled_window = Gtk2::ScrolledWindow->new; +$window->add($scrolled_window); +my $fileview = Gtk2::SimpleList->new( + 'Commit' => 'text', + 'CommitInfo' => 'text', + 'FileLine' => 'text', + 'Data' => 'text' +); +$scrolled_window->add($fileview); +$fileview->get_column(0)->set_spacing(0); +$fileview->set_size_request(1024, 768); +$fileview->set_rules_hint(1); + +open(my $fh, '<', $fn) + or die "unable to open $fn: $!"; +while(<$fh>) { + chomp; + $fileview->{data}->[$.] = ['HEAD', '?', "$fn:$.", $_]; +} + +my $blame; +open($blame, '-|', qw(git blame --incremental --), $fn) + or die "cannot start git-blame $fn"; + +Glib::IO->add_watch(fileno($blame), 'in', \&read_blame_line); + +$window->show_all; +Gtk2->main; +exit 0; + +my %commitinfo = (); + +sub flush_blame_line { + my ($attr) = @_; + + return unless defined $attr; + + my ($commit, $s_lno, $lno, $cnt) = + @{$attr}{qw(COMMIT S_LNO LNO CNT)}; + + my ($filename, $author, $author_time, $author_tz) = + @{$commitinfo{$commit}}{qw(FILENAME AUTHOR AUTHOR-TIME AUTHOR-TZ)}; + my $info = $author . ' ' . format_time($author_time, $author_tz); + + for(my $i = 0; $i < $cnt; $i++) { + @{$fileview->{data}->[$lno+$i-1]}[0,1,2] = + (substr($commit, 0, 8), $info, + $filename . ':' . ($s_lno+$i)); + } +} + +my $buf; +my $current; +sub read_blame_line { + + my $r = sysread($blame, $buf, 1024, length($buf)); + die "I/O error" unless defined $r; + + if ($r == 0) { + flush_blame_line($current); + $current = undef; + return 0; + } + + while ($buf =~ s/([^\n]*)\n//) { + my $line = $1; + + if (($commit, $s_lno, $lno, $cnt) = + ($line =~ /^([0-9a-f]{40}) (\d+) (\d+) (\d+)$/)) { + flush_blame_line($current); + $current = +{ + COMMIT => $1, + S_LNO => $2, + LNO => $3, + CNT => $4, + }; + next; + } + + # extended attribute values + if ($line =~ /^(author|author-mail|author-time|author-tz|committer|committer-mail|committer-time|committer-tz|summary|filename) (.*)$/) { + my $commit = $current->{COMMIT}; + $commitinfo{$commit}{uc($1)} = $2; + next; + } + } + return 1; +} + +sub format_time { + my $time = shift; + my $tz = shift; + + my $minutes = $tz < 0 ? 0-$tz : $tz; + $minutes = ($minutes / 100)*60 + ($minutes % 100); + $minutes = $tz < 0 ? 0-$minutes : $minutes; + $time += $minutes * 60; + my @t = gmtime($time); + return sprintf('%04d-%02d-%02d %02d:%02d:%02d %s', + $t[5] + 1900, @t[4,3,2,1,0], $tz); +} -- cgit v1.2.3 From e0d10e1c63bc52b37bbec99b07deee794058d9b4 Mon Sep 17 00:00:00 2001 From: Tom Prince Date: Sun, 28 Jan 2007 16:16:53 -0800 Subject: [PATCH] Rename git-repo-config to git-config. Signed-off-by: Tom Prince Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 ++++++++------- contrib/emacs/git.el | 8 ++++---- contrib/gitview/gitview | 2 +- contrib/remotes2config.sh | 4 ++-- 4 files changed, 15 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7c7520ea29..83c69ecf48 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -145,7 +145,7 @@ __git_remotes () echo ${i#$d/remotes/} done [ "$ngoff" ] && shopt -u nullglob - for i in $(git --git-dir="$d" repo-config --list); do + for i in $(git --git-dir="$d" config --list); do case "$i" in remote.*.url=*) i="${i#remote.}" @@ -286,7 +286,7 @@ __git_commandlist="$(__git_commands 2>/dev/null)" __git_aliases () { local i IFS=$'\n' - for i in $(git --git-dir="$(__gitdir)" repo-config --list); do + for i in $(git --git-dir="$(__gitdir)" config --list); do case "$i" in alias.*) i="${i#alias.}" @@ -299,7 +299,7 @@ __git_aliases () __git_aliased_command () { local word cmdline=$(git --git-dir="$(__gitdir)" \ - repo-config --get "alias.$1") + config --get "alias.$1") for word in $cmdline; do if [ "${word##-*}" ]; then echo $word @@ -629,7 +629,7 @@ _git_rebase () COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } -_git_repo_config () +_git_config () { local cur="${COMP_WORDS[COMP_CWORD]}" local prv="${COMP_WORDS[COMP_CWORD-1]}" @@ -806,6 +806,7 @@ _git () checkout) _git_checkout ;; cherry-pick) _git_cherry_pick ;; commit) _git_commit ;; + config) _git_config ;; diff) _git_diff ;; diff-tree) _git_diff_tree ;; fetch) _git_fetch ;; @@ -819,7 +820,7 @@ _git () pull) _git_pull ;; push) _git_push ;; rebase) _git_rebase ;; - repo-config) _git_repo_config ;; + repo-config) _git_config ;; reset) _git_reset ;; show) _git_show ;; show-branch) _git_log ;; @@ -856,7 +857,7 @@ complete -o default -F _git_name_rev git-name-rev complete -o default -o nospace -F _git_pull git-pull complete -o default -o nospace -F _git_push git-push complete -o default -F _git_rebase git-rebase -complete -o default -F _git_repo_config git-repo-config +complete -o default -F _git_config git-config complete -o default -F _git_reset git-reset complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_log git-show-branch @@ -879,7 +880,7 @@ complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe complete -o default -F _git_merge_base git-merge-base.exe complete -o default -F _git_name_rev git-name-rev.exe complete -o default -o nospace -F _git_push git-push.exe -complete -o default -F _git_repo_config git-repo-config +complete -o default -F _git_config git-config complete -o default -o nospace -F _git_show git-show.exe complete -o default -o nospace -F _git_log git-show-branch.exe complete -o default -o nospace -F _git_log git-whatchanged.exe diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d90ba816e0..24629eb3e2 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -222,7 +222,7 @@ and returns the process output as a string." "Return the name to use as GIT_COMMITTER_NAME." ; copied from log-edit (or git-committer-name - (git-repo-config "user.name") + (git-config "user.name") (and (boundp 'add-log-full-name) add-log-full-name) (and (fboundp 'user-full-name) (user-full-name)) (and (boundp 'user-full-name) user-full-name))) @@ -231,7 +231,7 @@ and returns the process output as a string." "Return the email address to use as GIT_COMMITTER_EMAIL." ; copied from log-edit (or git-committer-email - (git-repo-config "user.email") + (git-config "user.email") (and (boundp 'add-log-mailing-address) add-log-mailing-address) (and (fboundp 'user-mail-address) (user-mail-address)) (and (boundp 'user-mail-address) user-mail-address))) @@ -298,9 +298,9 @@ and returns the process output as a string." (git-get-string-sha1 (git-call-process-env-string nil "rev-parse" rev))) -(defun git-repo-config (key) +(defun git-config (key) "Retrieve the value associated to KEY in the git repository config file." - (let ((str (git-call-process-env-string nil "repo-config" key))) + (let ((str (git-call-process-env-string nil "config" key))) (and str (car (split-string str "\n"))))) (defun git-symbolic-ref (ref) diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 3b6bdceeeb..521b2fcd32 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -497,7 +497,7 @@ class GitView: fp.close() def get_encoding(self): - fp = os.popen("git repo-config --get i18n.commitencoding") + fp = os.popen("git config --get i18n.commitencoding") self.encoding=string.strip(fp.readline()) fp.close() if (self.encoding == ""): diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh index b996996bfb..dc09eae972 100644 --- a/contrib/remotes2config.sh +++ b/contrib/remotes2config.sh @@ -26,8 +26,8 @@ if [ -d "$GIT_DIR"/remotes ]; then mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old fi ;; *) - echo "git-repo-config $key "$value" $regex" - git-repo-config $key "$value" $regex || error=1 ;; + echo "git-config $key "$value" $regex" + git-config $key "$value" $regex || error=1 ;; esac done fi -- cgit v1.2.3 From 73a2acc0a09829f887fdf2dbcfba217102227932 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 30 Jan 2007 13:26:49 +0530 Subject: blameview: Use git-cat-file to read the file content. Fix blameview to use git-cat-file to read the file content. This make sure we show the right content when we have modified file in the working directory which is not committed. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Junio C Hamano --- contrib/blameview/blameview.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/blameview/blameview.perl b/contrib/blameview/blameview.perl index a55f799f00..5e9a67c123 100755 --- a/contrib/blameview/blameview.perl +++ b/contrib/blameview/blameview.perl @@ -28,7 +28,8 @@ $fileview->get_column(0)->set_spacing(0); $fileview->set_size_request(1024, 768); $fileview->set_rules_hint(1); -open(my $fh, '<', $fn) +my $fh; +open($fh, '-|', "git cat-file blob HEAD:$fn") or die "unable to open $fn: $!"; while(<$fh>) { chomp; -- cgit v1.2.3 From 16d6b8ab6fd7f68bfd9f4d312965cb99e8ad911b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 00:16:59 +0100 Subject: Initial import of a python script to import changesets from Perforce into git. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 150 ++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 contrib/fast-import/p4-fast-export.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py new file mode 100644 index 0000000000..238f6e3524 --- /dev/null +++ b/contrib/fast-import/p4-fast-export.py @@ -0,0 +1,150 @@ +#!/usr/bin/python +# +# p4-fast-export.py +# +# Author: Simon Hausmann +# License: MIT +# +# TODO: - fix date parsing (how hard can it be?) +# - support integrations (at least p4i) +# - support incremental imports +# - create tags +# - instead of reading all files into a variable try to pipe from +# - p4 print directly to stdout. need to figure out file size somehow +# though. +# - support p4 submit (hah!) +# - don't hardcode the import to master +# +import os, string, sys + +# yep, that's hardcoded right. will fix to a commandline option rsn :) +prefix = "//depot/qt/main/" +# that's in revision range syntax, for example @2342,523634 +changeRange = "" + +def describe(change): + output = os.popen("p4 describe %s" % change).readlines() + + firstLine = output[0] + + author = firstLine.split(" ")[3] + author = author[:author.find("@")] + + filesSection = 0 + try: + filesSection = output.index("Affected files ...\n") + except ValueError: + sys.stderr.write("Change %s doesn't seem to affect any files. Weird.\n" % change) + return [], [], [], [] + + differencesSection = 0 + try: + differencesSection = output.index("Differences ...\n") + except ValueError: + sys.stderr.write("Change %s doesn't seem to have a differences section. Weird.\n" % change) + return [], [], [], [] + + log = output[2:filesSection - 1] + + lines = output[filesSection + 2:differencesSection - 1] + + changed = [] + removed = [] + + for line in lines: + # chop off "... " and trailing newline + line = line[4:len(line) - 1] + + lastSpace = line.rfind(" ") + if lastSpace == -1: + sys.stderr.write("trouble parsing line %s, skipping!\n" % line) + continue + + operation = line[lastSpace + 1:] + path = line[:lastSpace] + + if operation == "delete": + removed.append(path) + else: + changed.append(path) + + return author, log, changed, removed + +def p4cat(path): + return os.popen("p4 print -q \"%s\"" % path).read() + +def stripRevision(path): + hashPos = path.rindex("#") + return path[:hashPos] + +def getUserMap(): + users = {} + output = os.popen("p4 users") + for line in output: + firstSpace = line.index(" ") + secondSpace = line.index(" ", firstSpace + 1) + key = line[:firstSpace] + email = line[firstSpace + 1:secondSpace] + openParenPos = line.index("(", secondSpace) + closedParenPos = line.index(")", openParenPos) + name = line[openParenPos + 1:closedParenPos] + + users[key] = name + " " + email + + return users + + +users = getUserMap() + +output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() + +changes = [] +for line in output: + changeNum = line.split(" ")[1] + changes.append(changeNum) + +changes.reverse() + +sys.stderr.write("\n") + +cnt = 0 +for change in changes: + [ author, log, changedFiles, removedFiles ] = describe(change) + sys.stderr.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + cnt = cnt + 1 +# sys.stderr.write("%s\n" % log) +# sys.stderr.write("%s\n" % changedFiles) +# sys.stderr.write("%s\n" % removedFiles) + + print "commit refs/heads/master" + if author in users: + print "committer %s 1 2" % users[author] + else: + print "committer %s 1 2" % author + print "data < Date: Wed, 31 Jan 2007 09:39:20 +0100 Subject: Added basic support for specifying the depot path to import from as well as the range of perforce changes. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 238f6e3524..abd6da4668 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -17,10 +17,22 @@ # import os, string, sys -# yep, that's hardcoded right. will fix to a commandline option rsn :) -prefix = "//depot/qt/main/" -# that's in revision range syntax, for example @2342,523634 +if len(sys.argv) != 2: + sys.stderr.write("usage: %s //depot/path[@revRange]\n" % sys.argv[0]); + sys.stderr.write("\n example:\n"); + sys.stderr.write(" %s //depot/my/project -- to import everything\n"); + sys.stderr.write(" %s //depot/my/project@1,6 -- to import only from revision 1 to 6\n"); + sys.stderr.write("\n"); + sys.exit(1) + +prefix = sys.argv[1] changeRange = "" +try: + atIdx = prefix.index("@") + changeRange = prefix[atIdx:] + prefix = prefix[0:atIdx] +except ValueError: + changeRange = "" def describe(change): output = os.popen("p4 describe %s" % change).readlines() -- cgit v1.2.3 From 06bb04454fb91fbc144ed2b3abb3492c923f98f5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 09:49:41 +0100 Subject: Slightly improved help usage output and made specifying the trailing slash for the depot path optional. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index abd6da4668..8df3d73392 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -20,8 +20,10 @@ import os, string, sys if len(sys.argv) != 2: sys.stderr.write("usage: %s //depot/path[@revRange]\n" % sys.argv[0]); sys.stderr.write("\n example:\n"); - sys.stderr.write(" %s //depot/my/project -- to import everything\n"); - sys.stderr.write(" %s //depot/my/project@1,6 -- to import only from revision 1 to 6\n"); + sys.stderr.write(" %s //depot/my/project/ -- to import everything\n"); + sys.stderr.write(" %s //depot/my/project/@1,6 -- to import only from revision 1 to 6\n"); + sys.stderr.write("\n"); + sys.stderr.write(" (a ... is not needed in the path p4 specification, it's added implicitly)\n"); sys.stderr.write("\n"); sys.exit(1) @@ -34,6 +36,9 @@ try: except ValueError: changeRange = "" +if not prefix.endswith("/"): + prefix += "/" + def describe(change): output = os.popen("p4 describe %s" % change).readlines() -- cgit v1.2.3 From 72b2f0ada30adbb2880d551bd92d9519396897dd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 16:39:46 +0100 Subject: Implemented basic support for converting the date of the perforce change to the git format. The timezone isn't correctly set up yet though. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 8df3d73392..689349d551 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -5,8 +5,7 @@ # Author: Simon Hausmann # License: MIT # -# TODO: - fix date parsing (how hard can it be?) -# - support integrations (at least p4i) +# TODO: - support integrations (at least p4i) # - support incremental imports # - create tags # - instead of reading all files into a variable try to pipe from @@ -15,7 +14,7 @@ # - support p4 submit (hah!) # - don't hardcode the import to master # -import os, string, sys +import os, string, sys, time if len(sys.argv) != 2: sys.stderr.write("usage: %s //depot/path[@revRange]\n" % sys.argv[0]); @@ -44,22 +43,25 @@ def describe(change): firstLine = output[0] - author = firstLine.split(" ")[3] + splitted = firstLine.split(" ") + author = splitted[3] author = author[:author.find("@")] + tm = time.strptime(splitted[5] + " " + splitted[6] + time.tzname[0], "%Y/%m/%d %H:%M:%S %Z") + epoch = int(time.mktime(tm)) filesSection = 0 try: filesSection = output.index("Affected files ...\n") except ValueError: sys.stderr.write("Change %s doesn't seem to affect any files. Weird.\n" % change) - return [], [], [], [] + return [], [], [], [], [] differencesSection = 0 try: differencesSection = output.index("Differences ...\n") except ValueError: sys.stderr.write("Change %s doesn't seem to have a differences section. Weird.\n" % change) - return [], [], [], [] + return [], [], [], [], [] log = output[2:filesSection - 1] @@ -85,7 +87,7 @@ def describe(change): else: changed.append(path) - return author, log, changed, removed + return author, log, epoch, changed, removed def p4cat(path): return os.popen("p4 print -q \"%s\"" % path).read() @@ -124,9 +126,9 @@ changes.reverse() sys.stderr.write("\n") -cnt = 0 +cnt = 1 for change in changes: - [ author, log, changedFiles, removedFiles ] = describe(change) + [ author, log, epoch, changedFiles, removedFiles ] = describe(change) sys.stderr.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) cnt = cnt + 1 # sys.stderr.write("%s\n" % log) @@ -135,9 +137,9 @@ for change in changes: print "commit refs/heads/master" if author in users: - print "committer %s 1 2" % users[author] + print "committer %s %s +0000" % (users[author], epoch) else: - print "committer %s 1 2" % author + print "committer %s %s +0000" % (author, epoch) print "data < Date: Wed, 31 Jan 2007 19:43:16 +0100 Subject: Some fixes to the timezone conversion between the date of a perforce change and the git commit. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 689349d551..f3b5f35cb3 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -46,7 +46,7 @@ def describe(change): splitted = firstLine.split(" ") author = splitted[3] author = author[:author.find("@")] - tm = time.strptime(splitted[5] + " " + splitted[6] + time.tzname[0], "%Y/%m/%d %H:%M:%S %Z") + tm = time.strptime(splitted[5] + " " + splitted[6], "%Y/%m/%d %H:%M:%S ") epoch = int(time.mktime(tm)) filesSection = 0 @@ -126,6 +126,8 @@ changes.reverse() sys.stderr.write("\n") +tz = - time.timezone / 36 + cnt = 1 for change in changes: [ author, log, epoch, changedFiles, removedFiles ] = describe(change) @@ -137,9 +139,9 @@ for change in changes: print "commit refs/heads/master" if author in users: - print "committer %s %s +0000" % (users[author], epoch) + print "committer %s %s %s" % (users[author], epoch, tz) else: - print "committer %s %s +0000" % (author, epoch) + print "committer %s %s %s" % (author, epoch, tz) print "data < Date: Wed, 31 Jan 2007 20:16:26 +0100 Subject: Speed up the import of individual files from Perforce into git by passing the output of "p4 print" directly to git fast-import. Also try to set the mode of the file in git correctly based on file type heuristics. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index f3b5f35cb3..72a4fd70a5 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -5,12 +5,11 @@ # Author: Simon Hausmann # License: MIT # -# TODO: - support integrations (at least p4i) +# TODO: +# - support integrations (at least p4i) # - support incremental imports # - create tags # - instead of reading all files into a variable try to pipe from -# - p4 print directly to stdout. need to figure out file size somehow -# though. # - support p4 submit (hah!) # - don't hardcode the import to master # @@ -92,6 +91,17 @@ def describe(change): def p4cat(path): return os.popen("p4 print -q \"%s\"" % path).read() +def p4Stat(path): + output = os.popen("p4 fstat -Ol \"%s\"" % path).readlines() + fileSize = 0 + mode = 644 + for line in output: + if line.startswith("... headType x"): + mode = 755 + elif line.startswith("... fileSize "): + fileSize = long(line[12:]) + return mode, fileSize + def stripRevision(path): hashPos = path.rindex("#") return path[:hashPos] @@ -112,7 +122,6 @@ def getUserMap(): return users - users = getUserMap() output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() @@ -133,9 +142,6 @@ for change in changes: [ author, log, epoch, changedFiles, removedFiles ] = describe(change) sys.stderr.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) cnt = cnt + 1 -# sys.stderr.write("%s\n" % log) -# sys.stderr.write("%s\n" % changedFiles) -# sys.stderr.write("%s\n" % removedFiles) print "commit refs/heads/master" if author in users: @@ -154,10 +160,13 @@ for change in changes: sys.stderr.write("\nchanged files: ignoring path %s outside of %s in change %s\n" % (f, prefix, change)) continue relpath = f[len(prefix):] - print "M 644 inline %s" % stripRevision(relpath) - data = p4cat(f) - print "data %s" % len(data) - sys.stdout.write(data) + + [mode, fileSize] = p4Stat(f) + + print "M %s inline %s" % (mode, stripRevision(relpath)) + print "data %s" % fileSize + sys.stdout.flush(); + os.system("p4 print -q \"%s\"" % f) print "" for f in removedFiles: -- cgit v1.2.3 From 3f2ddd47c7f311516dc7092b0a89b46291e40271 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 20:48:39 +0100 Subject: Removed unused p4cat function and added helper function for the perforce python interface (p4Cmd). Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 72a4fd70a5..e284b19502 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -14,6 +14,7 @@ # - don't hardcode the import to master # import os, string, sys, time +import marshal if len(sys.argv) != 2: sys.stderr.write("usage: %s //depot/path[@revRange]\n" % sys.argv[0]); @@ -37,6 +38,18 @@ except ValueError: if not prefix.endswith("/"): prefix += "/" +def p4Cmd(cmd): + pipe = os.popen("p4 -G %s" % cmd, "rb") + result = {} + try: + while True: + entry = marshal.load(pipe) + result.update(entry) + except EOFError: + pass + pipe.close() + return result + def describe(change): output = os.popen("p4 describe %s" % change).readlines() @@ -88,9 +101,6 @@ def describe(change): return author, log, epoch, changed, removed -def p4cat(path): - return os.popen("p4 print -q \"%s\"" % path).read() - def p4Stat(path): output = os.popen("p4 fstat -Ol \"%s\"" % path).readlines() fileSize = 0 -- cgit v1.2.3 From 701ce876331eca10031c104f42cc6fccc425ac94 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 21:54:56 +0100 Subject: Changed the import mechanism to write to git fast-import through a pipe instead of having p4-fast-export write to stdout and let the caller connect it to git fast-import. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 52 ++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 25 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index e284b19502..662dd01d8d 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -14,16 +14,16 @@ # - don't hardcode the import to master # import os, string, sys, time -import marshal +import marshal, popen2 if len(sys.argv) != 2: - sys.stderr.write("usage: %s //depot/path[@revRange]\n" % sys.argv[0]); - sys.stderr.write("\n example:\n"); - sys.stderr.write(" %s //depot/my/project/ -- to import everything\n"); - sys.stderr.write(" %s //depot/my/project/@1,6 -- to import only from revision 1 to 6\n"); - sys.stderr.write("\n"); - sys.stderr.write(" (a ... is not needed in the path p4 specification, it's added implicitly)\n"); - sys.stderr.write("\n"); + print "usage: %s //depot/path[@revRange]" % sys.argv[0] + print "\n example:" + print " %s //depot/my/project/ -- to import everything" + print " %s //depot/my/project/@1,6 -- to import only from revision 1 to 6" + print "" + print " (a ... is not needed in the path p4 specification, it's added implicitly)" + print "" sys.exit(1) prefix = sys.argv[1] @@ -147,23 +147,23 @@ sys.stderr.write("\n") tz = - time.timezone / 36 +gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") + cnt = 1 for change in changes: [ author, log, epoch, changedFiles, removedFiles ] = describe(change) - sys.stderr.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) cnt = cnt + 1 - print "commit refs/heads/master" + gitStream.write("commit refs/heads/master\n") if author in users: - print "committer %s %s %s" % (users[author], epoch, tz) + gitStream.write("committer %s %s %s\n" % (users[author], epoch, tz)) else: - print "committer %s %s %s" % (author, epoch, tz) - print "data < %s %s\n" % (author, epoch, tz)) + gitStream.write("data < Date: Wed, 31 Jan 2007 22:13:17 +0100 Subject: Minor code cleanups and ported some p4 interfacing code over to the p4 python mode. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 53 +++++++++-------------------------- 1 file changed, 14 insertions(+), 39 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 662dd01d8d..3ccef526eb 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -51,54 +51,30 @@ def p4Cmd(cmd): return result def describe(change): - output = os.popen("p4 describe %s" % change).readlines() + describeOutput = p4Cmd("describe %s" % change) - firstLine = output[0] + author = describeOutput["user"] + epoch = describeOutput["time"] - splitted = firstLine.split(" ") - author = splitted[3] - author = author[:author.find("@")] - tm = time.strptime(splitted[5] + " " + splitted[6], "%Y/%m/%d %H:%M:%S ") - epoch = int(time.mktime(tm)) - - filesSection = 0 - try: - filesSection = output.index("Affected files ...\n") - except ValueError: - sys.stderr.write("Change %s doesn't seem to affect any files. Weird.\n" % change) - return [], [], [], [], [] - - differencesSection = 0 - try: - differencesSection = output.index("Differences ...\n") - except ValueError: - sys.stderr.write("Change %s doesn't seem to have a differences section. Weird.\n" % change) - return [], [], [], [], [] - - log = output[2:filesSection - 1] - - lines = output[filesSection + 2:differencesSection - 1] + log = describeOutput["desc"] changed = [] removed = [] - for line in lines: - # chop off "... " and trailing newline - line = line[4:len(line) - 1] - - lastSpace = line.rfind(" ") - if lastSpace == -1: - sys.stderr.write("trouble parsing line %s, skipping!\n" % line) - continue + i = 0 + while describeOutput.has_key("depotFile%s" % i): + path = describeOutput["depotFile%s" % i] + rev = describeOutput["rev%s" % i] + action = describeOutput["action%s" % i] + path = path + "#" + rev - operation = line[lastSpace + 1:] - path = line[:lastSpace] - - if operation == "delete": + if action == "delete": removed.append(path) else: changed.append(path) + i = i + 1 + return author, log, epoch, changed, removed def p4Stat(path): @@ -161,8 +137,7 @@ for change in changes: else: gitStream.write("committer %s %s %s\n" % (author, epoch, tz)) gitStream.write("data < Date: Wed, 31 Jan 2007 22:19:18 +0100 Subject: Instead of parsing the output of "p4 users" use the python objects of "p4 -G users". Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 3ccef526eb..d3e65f0f39 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -38,18 +38,25 @@ except ValueError: if not prefix.endswith("/"): prefix += "/" -def p4Cmd(cmd): +def p4CmdList(cmd): pipe = os.popen("p4 -G %s" % cmd, "rb") - result = {} + result = [] try: while True: entry = marshal.load(pipe) - result.update(entry) + result.append(entry) except EOFError: pass pipe.close() return result +def p4Cmd(cmd): + list = p4CmdList(cmd) + result = {} + for entry in list: + result.update(entry) + return result; + def describe(change): describeOutput = p4Cmd("describe %s" % change) @@ -94,18 +101,11 @@ def stripRevision(path): def getUserMap(): users = {} - output = os.popen("p4 users") - for line in output: - firstSpace = line.index(" ") - secondSpace = line.index(" ", firstSpace + 1) - key = line[:firstSpace] - email = line[firstSpace + 1:secondSpace] - openParenPos = line.index("(", secondSpace) - closedParenPos = line.index(")", openParenPos) - name = line[openParenPos + 1:closedParenPos] - - users[key] = name + " " + email + for output in p4CmdList("users"): + if not output.has_key("User"): + continue + users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" return users users = getUserMap() -- cgit v1.2.3 From 0dd0b9d01116d57c612a5fa90de803bdd836ce11 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 22:31:28 +0100 Subject: Ported the remaining functions that parsed p4 shell output over to the p4 python interface. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 89 ++++++++++++----------------------- 1 file changed, 31 insertions(+), 58 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index d3e65f0f39..45d5157961 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -57,47 +57,8 @@ def p4Cmd(cmd): result.update(entry) return result; -def describe(change): - describeOutput = p4Cmd("describe %s" % change) - - author = describeOutput["user"] - epoch = describeOutput["time"] - - log = describeOutput["desc"] - - changed = [] - removed = [] - - i = 0 - while describeOutput.has_key("depotFile%s" % i): - path = describeOutput["depotFile%s" % i] - rev = describeOutput["rev%s" % i] - action = describeOutput["action%s" % i] - path = path + "#" + rev - - if action == "delete": - removed.append(path) - else: - changed.append(path) - - i = i + 1 - - return author, log, epoch, changed, removed - -def p4Stat(path): - output = os.popen("p4 fstat -Ol \"%s\"" % path).readlines() - fileSize = 0 - mode = 644 - for line in output: - if line.startswith("... headType x"): - mode = 755 - elif line.startswith("... fileSize "): - fileSize = long(line[12:]) - return mode, fileSize - -def stripRevision(path): - hashPos = path.rindex("#") - return path[:hashPos] +def p4FileSize(path): + return int(p4Cmd("fstat -Ol \"%s\"" % path)["fileSize"]) def getUserMap(): users = {} @@ -127,38 +88,50 @@ gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") cnt = 1 for change in changes: - [ author, log, epoch, changedFiles, removedFiles ] = describe(change) + description = p4Cmd("describe %s" % change) + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) cnt = cnt + 1 + epoch = description["time"] + author = description["user"] + gitStream.write("commit refs/heads/master\n") if author in users: gitStream.write("committer %s %s %s\n" % (users[author], epoch, tz)) else: gitStream.write("committer %s %s %s\n" % (author, epoch, tz)) gitStream.write("data < Date: Wed, 31 Jan 2007 22:38:07 +0100 Subject: Avoid calling fstat for every imported file (slow!) and instead read the file data first into the python process and use the length of the bytes read for the size field of git fast-import. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 45d5157961..133447c4e7 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -57,9 +57,6 @@ def p4Cmd(cmd): result.update(entry) return result; -def p4FileSize(path): - return int(p4Cmd("fstat -Ol \"%s\"" % path)["fileSize"]) - def getUserMap(): users = {} @@ -121,14 +118,15 @@ for change in changes: if action == "delete": gitStream.write("D %s\n" % relPath) else: - fileSize = p4FileSize(depotPath) mode = 644 if description["type%s" % fnum].startswith("x"): mode = 755 + data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + gitStream.write("M %s inline %s\n" % (mode, relPath)) - gitStream.write("data %s\n" % fileSize) - gitStream.write(os.popen("p4 print -q \"%s\"" % depotPath).read()) + gitStream.write("data %s\n" % len(data)) + gitStream.write(data) gitStream.write("\n") fnum = fnum + 1 -- cgit v1.2.3 From f26037dce365b3e525596c6f0236ab203d87a872 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 22:41:08 +0100 Subject: Permit calling p4-fast-export with a depot path that has the typical ... wildcard at the end. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 133447c4e7..72e01224bf 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -35,6 +35,9 @@ try: except ValueError: changeRange = "" +if prefix.endswith("..."): + prefix = prefix[:-3] + if not prefix.endswith("/"): prefix += "/" -- cgit v1.2.3 From 214bed82390479bdba31337ac420bb09e9b366e9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 22:47:53 +0100 Subject: Fixed displaying import progress by calling flush on stdout. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 72e01224bf..a1dc54013e 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -91,6 +91,7 @@ for change in changes: description = p4Cmd("describe %s" % change) sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() cnt = cnt + 1 epoch = description["time"] -- cgit v1.2.3 From 71f7c0d0bb20936fc831e49bbfc9355f1e5ca211 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 23:03:01 +0100 Subject: Create a git tag for every changeset imported from perforce. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index a1dc54013e..588554d672 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -98,12 +98,17 @@ for change in changes: author = description["user"] gitStream.write("commit refs/heads/master\n") + committer = "" if author in users: - gitStream.write("committer %s %s %s\n" % (users[author], epoch, tz)) + committer = "%s %s %s" % (users[author], epoch, tz) else: - gitStream.write("committer %s %s %s\n" % (author, epoch, tz)) + committer = "%s %s %s" % (author, epoch, tz) + + gitStream.write("committer %s\n" % committer) + gitStream.write("data < Date: Wed, 31 Jan 2007 23:09:24 +0100 Subject: Fix file permissions of p4-fast-export.py to be executable. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 contrib/fast-import/p4-fast-export.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py old mode 100644 new mode 100755 -- cgit v1.2.3 From 61b3cf7c471d2f94eefcf6616c1204f32e641542 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 1 Feb 2007 00:08:51 +0100 Subject: Started working on incremental imports from Perforce. Try to find the last imported p4 change number from the git tags and try to pass the right parent for commits to git fast-import. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 37 +++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 588554d672..da3eb35841 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -26,6 +26,8 @@ if len(sys.argv) != 2: print "" sys.exit(1) +master = "refs/heads/p4" +branch = "refs/heads/p4-import" prefix = sys.argv[1] changeRange = "" try: @@ -70,6 +72,25 @@ def getUserMap(): return users users = getUserMap() +topMerge = "" + +incremental = 0 +# try incremental import +if len(changeRange) == 0: + try: + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % master) + output = sout.read() + tagIdx = output.index(" tags/p4/") + caretIdx = output.index("^") + revision = int(output[tagIdx + 9 : caretIdx]) + 1 + changeRange = "@%s,#head" % revision + topMerge = os.popen("git-rev-parse %s" % master).read()[:-1] + incremental = 1 + except: + pass + +if incremental == 0: + branch = master output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() @@ -80,6 +101,10 @@ for line in output: changes.reverse() +if len(changes) == 0: + print "no changes to import!" + sys.exit(1) + sys.stderr.write("\n") tz = - time.timezone / 36 @@ -97,7 +122,7 @@ for change in changes: epoch = description["time"] author = description["user"] - gitStream.write("commit refs/heads/master\n") + gitStream.write("commit %s\n" % branch) committer = "" if author in users: committer = "%s %s %s" % (users[author], epoch, tz) @@ -111,6 +136,10 @@ for change in changes: gitStream.write("\n[ imported from %s; change %s ]\n" % (prefix, change)) gitStream.write("EOT\n\n") + if len(topMerge) > 0: + gitStream.write("merge %s\n" % topMerge) + topMerge = "" + fnum = 0 while description.has_key("depotFile%s" % fnum): path = description["depotFile%s" % fnum] @@ -143,7 +172,7 @@ for change in changes: gitStream.write("\n") gitStream.write("tag p4/%s\n" % change) - gitStream.write("from refs/heads/master\n"); + gitStream.write("from %s\n" % branch); gitStream.write("tagger %s\n" % committer); gitStream.write("data 0\n\n") @@ -152,4 +181,8 @@ gitStream.close() gitOutput.close() gitError.close() +if incremental == 1: + os.popen("git rebase p4-import p4") + os.popen("git branch -d p4-import") + print "" -- cgit v1.2.3 From f16255f559c96b8c5fb096b00c94a69f5fcc498f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 1 Feb 2007 08:23:39 +0100 Subject: Simplify the incremental import by elimination the need for a temporary import branch. It turns out that git fast-import can "resume" from an existing branch just fine. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index da3eb35841..c5b15206b1 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -7,11 +7,7 @@ # # TODO: # - support integrations (at least p4i) -# - support incremental imports -# - create tags -# - instead of reading all files into a variable try to pipe from # - support p4 submit (hah!) -# - don't hardcode the import to master # import os, string, sys, time import marshal, popen2 @@ -26,8 +22,7 @@ if len(sys.argv) != 2: print "" sys.exit(1) -master = "refs/heads/p4" -branch = "refs/heads/p4-import" +branch = "refs/heads/p4" prefix = sys.argv[1] changeRange = "" try: @@ -74,24 +69,18 @@ def getUserMap(): users = getUserMap() topMerge = "" -incremental = 0 -# try incremental import if len(changeRange) == 0: try: - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % master) + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) output = sout.read() tagIdx = output.index(" tags/p4/") caretIdx = output.index("^") revision = int(output[tagIdx + 9 : caretIdx]) + 1 changeRange = "@%s,#head" % revision - topMerge = os.popen("git-rev-parse %s" % master).read()[:-1] - incremental = 1 + topMerge = os.popen("git-rev-parse %s" % branch).read()[:-1] except: pass -if incremental == 0: - branch = master - output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() changes = [] @@ -181,8 +170,4 @@ gitStream.close() gitOutput.close() gitError.close() -if incremental == 1: - os.popen("git rebase p4-import p4") - os.popen("git branch -d p4-import") - print "" -- cgit v1.2.3 From 68f1336fe370ca2153d691631876b0f85a3fb17f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 1 Feb 2007 17:42:23 +0100 Subject: Code cleanups, move the code to create a commit with fast-import into a separate function out of the main loop. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 126 ++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 59 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index c5b15206b1..8455c1f41c 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -25,6 +25,9 @@ if len(sys.argv) != 2: branch = "refs/heads/p4" prefix = sys.argv[1] changeRange = "" +users = {} +initialParent = "" + try: atIdx = prefix.index("@") changeRange = prefix[atIdx:] @@ -57,6 +60,68 @@ def p4Cmd(cmd): result.update(entry) return result; +def commit(details): + global initialParent + global users + + epoch = details["time"] + author = details["user"] + + gitStream.write("commit %s\n" % branch) + committer = "" + if author in users: + committer = "%s %s %s" % (users[author], epoch, tz) + else: + committer = "%s %s %s" % (author, epoch, tz) + + gitStream.write("committer %s\n" % committer) + + gitStream.write("data < 0: + gitStream.write("merge %s\n" % initialParent) + initialParent = "" + + fnum = 0 + while details.has_key("depotFile%s" % fnum): + path = details["depotFile%s" % fnum] + if not path.startswith(prefix): + print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) + fnum = fnum + 1 + continue + + rev = details["rev%s" % fnum] + depotPath = path + "#" + rev + relPath = path[len(prefix):] + action = details["action%s" % fnum] + + if action == "delete": + gitStream.write("D %s\n" % relPath) + else: + mode = 644 + if details["type%s" % fnum].startswith("x"): + mode = 755 + + data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + + gitStream.write("M %s inline %s\n" % (mode, relPath)) + gitStream.write("data %s\n" % len(data)) + gitStream.write(data) + gitStream.write("\n") + + fnum = fnum + 1 + + gitStream.write("\n") + + gitStream.write("tag p4/%s\n" % change) + gitStream.write("from %s\n" % branch); + gitStream.write("tagger %s\n" % committer); + gitStream.write("data 0\n\n") + + def getUserMap(): users = {} @@ -67,7 +132,6 @@ def getUserMap(): return users users = getUserMap() -topMerge = "" if len(changeRange) == 0: try: @@ -77,7 +141,7 @@ if len(changeRange) == 0: caretIdx = output.index("^") revision = int(output[tagIdx + 9 : caretIdx]) + 1 changeRange = "@%s,#head" % revision - topMerge = os.popen("git-rev-parse %s" % branch).read()[:-1] + initialParent = os.popen("git-rev-parse %s" % branch).read()[:-1] except: pass @@ -108,63 +172,7 @@ for change in changes: sys.stdout.flush() cnt = cnt + 1 - epoch = description["time"] - author = description["user"] - - gitStream.write("commit %s\n" % branch) - committer = "" - if author in users: - committer = "%s %s %s" % (users[author], epoch, tz) - else: - committer = "%s %s %s" % (author, epoch, tz) - - gitStream.write("committer %s\n" % committer) - - gitStream.write("data < 0: - gitStream.write("merge %s\n" % topMerge) - topMerge = "" - - fnum = 0 - while description.has_key("depotFile%s" % fnum): - path = description["depotFile%s" % fnum] - if not path.startswith(prefix): - print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) - fnum = fnum + 1 - continue - - rev = description["rev%s" % fnum] - depotPath = path + "#" + rev - relPath = path[len(prefix):] - action = description["action%s" % fnum] - - if action == "delete": - gitStream.write("D %s\n" % relPath) - else: - mode = 644 - if description["type%s" % fnum].startswith("x"): - mode = 755 - - data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() - - gitStream.write("M %s inline %s\n" % (mode, relPath)) - gitStream.write("data %s\n" % len(data)) - gitStream.write(data) - gitStream.write("\n") - - fnum = fnum + 1 - - gitStream.write("\n") - - gitStream.write("tag p4/%s\n" % change) - gitStream.write("from %s\n" % branch); - gitStream.write("tagger %s\n" % committer); - gitStream.write("data 0\n\n") - + commit(description) gitStream.close() gitOutput.close() -- cgit v1.2.3 From 6d48d12f5d34a2acb6d8b56bfe3c26d4db92b1a5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 1 Feb 2007 18:19:55 +0100 Subject: Initial support for importing a directory from Perforce at a specified revision. Use p4 files //depot/path/...@revision to determine the state of the project and create a "fake" git commit from it. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 94 ++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 29 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 8455c1f41c..19f5f03476 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -25,15 +25,21 @@ if len(sys.argv) != 2: branch = "refs/heads/p4" prefix = sys.argv[1] changeRange = "" +revision = "" users = {} initialParent = "" -try: +if prefix.find("@") != -1: atIdx = prefix.index("@") changeRange = prefix[atIdx:] + if changeRange.find(",") == -1: + revision = changeRange + changeRange = "" prefix = prefix[0:atIdx] -except ValueError: - changeRange = "" +elif prefix.find("#") != -1: + hashIdx = prefix.index("#") + revision = prefix[hashIdx:] + prefix = prefix[0:hashIdx] if prefix.endswith("..."): prefix = prefix[:-3] @@ -78,7 +84,7 @@ def commit(details): gitStream.write("data < 0: @@ -116,7 +122,7 @@ def commit(details): gitStream.write("\n") - gitStream.write("tag p4/%s\n" % change) + gitStream.write("tag p4/%s\n" % details["change"]) gitStream.write("from %s\n" % branch); gitStream.write("tagger %s\n" % committer); gitStream.write("data 0\n\n") @@ -139,43 +145,73 @@ if len(changeRange) == 0: output = sout.read() tagIdx = output.index(" tags/p4/") caretIdx = output.index("^") - revision = int(output[tagIdx + 9 : caretIdx]) + 1 - changeRange = "@%s,#head" % revision + rev = int(output[tagIdx + 9 : caretIdx]) + 1 + changeRange = "@%s,#head" % rev initialParent = os.popen("git-rev-parse %s" % branch).read()[:-1] except: pass -output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() +sys.stderr.write("\n") -changes = [] -for line in output: - changeNum = line.split(" ")[1] - changes.append(changeNum) +tz = - time.timezone / 36 -changes.reverse() +if len(revision) > 0: + print "Doing initial import of %s from revision %s" % (prefix, revision) -if len(changes) == 0: - print "no changes to import!" - sys.exit(1) + details = { "user" : "git perforce import user", "time" : int(time.time()) } + details["desc"] = "Initial import of %s from the state at revision %s" % (prefix, revision) + details["change"] = revision + newestRevision = 0 -sys.stderr.write("\n") + fileCnt = 0 + for info in p4CmdList("files %s...%s" % (prefix, revision)): + if info["action"] == "delete": + continue + for prop in [ "depotFile", "rev", "action", "type" ]: + details["%s%s" % (prop, fileCnt)] = info[prop] -tz = - time.timezone / 36 + change = info["change"] + if change > newestRevision: + newestRevision = change + + fileCnt = fileCnt + 1 + + details["change"] = newestRevision + + gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") + commit(details) + + gitStream.close() + gitOutput.close() + gitError.close() +else: + output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() + + changes = [] + for line in output: + changeNum = line.split(" ")[1] + changes.append(changeNum) + + changes.reverse() + + if len(changes) == 0: + print "no changes to import!" + sys.exit(1) -gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") + gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") -cnt = 1 -for change in changes: - description = p4Cmd("describe %s" % change) + cnt = 1 + for change in changes: + description = p4Cmd("describe %s" % change) - sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) - sys.stdout.flush() - cnt = cnt + 1 + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() + cnt = cnt + 1 - commit(description) + commit(description) -gitStream.close() -gitOutput.close() -gitError.close() + gitStream.close() + gitOutput.close() + gitError.close() print "" -- cgit v1.2.3 From 9f4cc6f76b8a15a89b85a43e0c0016a64d368709 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 4 Feb 2007 02:38:17 -0500 Subject: bash: Remove short option completions for branch/checkout/diff. The short options (-l, -f, -d) for git-branch are rather silly to include in the completion generation as these options must be fully typed out by the user and most users already know what the options are anyway, so including them in the suggested completions does not offer huge value. (The same goes for git-checkout and git-diff.) Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 83c69ecf48..971fefb05f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -359,7 +359,7 @@ _git_apply () _git_branch () { local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "-l -f -d -D $(__git_refs)" -- "$cur")) + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } _git_cat_file () @@ -381,7 +381,7 @@ _git_cat_file () _git_checkout () { local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "-l -b $(__git_refs)" -- "$cur")) + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } _git_cherry_pick () @@ -421,7 +421,7 @@ _git_diff () _git_diff_tree () { local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "-r -p -M $(__git_refs)" -- "$cur")) + COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } _git_fetch () -- cgit v1.2.3 From 2e3a430a9a976ec9324b33fef41dfeb55353a1ae Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 4 Feb 2007 02:38:21 -0500 Subject: bash: Classify cat-file and reflog as plumbing. Now that git-show is capable of displaying any file content from any revision and is the approved Porcelain-ish level method of doing so, cat-file should no longer be classified as a user-level utility by the bash completion package. I'm also classifying the new git-reflog command as plumbing for the time being as there are no subcommands which are really useful to the end-user. git-gc already invokes `git reflog expire --all`, which makes it rather unnecessary for the user to invoke it directly. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 971fefb05f..b87f96d9ea 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1,7 +1,7 @@ # # bash completion support for core Git. # -# Copyright (C) 2006 Shawn Pearce +# Copyright (C) 2006,2007 Shawn Pearce # Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/). # # The contained completion routines provide support for completing: @@ -235,6 +235,7 @@ __git_commands () for i in $(git help -a|egrep '^ ') do case $i in + cat-file) : plumbing;; check-ref-format) : plumbing;; commit-tree) : plumbing;; convert-objects) : plumbing;; @@ -258,6 +259,7 @@ __git_commands () peek-remote) : plumbing;; read-tree) : plumbing;; receive-pack) : plumbing;; + reflog) : plumbing;; rerere) : plumbing;; rev-list) : plumbing;; rev-parse) : plumbing;; @@ -362,22 +364,6 @@ _git_branch () COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) } -_git_cat_file () -{ - local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[0]},$COMP_CWORD" in - git-cat-file*,1) - COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur")) - ;; - git,2) - COMPREPLY=($(compgen -W "-p -t blob tree commit tag" -- "$cur")) - ;; - *) - __git_complete_file - ;; - esac -} - _git_checkout () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -802,7 +788,6 @@ _git () am) _git_am ;; apply) _git_apply ;; branch) _git_branch ;; - cat-file) _git_cat_file ;; checkout) _git_checkout ;; cherry-pick) _git_cherry_pick ;; commit) _git_commit ;; @@ -840,7 +825,6 @@ complete -o default -F _gitk gitk complete -o default -F _git_am git-am complete -o default -F _git_apply git-apply complete -o default -F _git_branch git-branch -complete -o default -o nospace -F _git_cat_file git-cat-file complete -o default -F _git_checkout git-checkout complete -o default -F _git_cherry_pick git-cherry-pick complete -o default -F _git_commit git-commit @@ -871,7 +855,6 @@ if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then complete -o default -F _git_apply git-apply.exe complete -o default -o nospace -F _git git.exe complete -o default -F _git_branch git-branch.exe -complete -o default -o nospace -F _git_cat_file git-cat-file.exe complete -o default -o nospace -F _git_diff git-diff.exe complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe complete -o default -o nospace -F _git_format_patch git-format-patch.exe -- cgit v1.2.3 From 8435b548480a6b6d110fd2f59b3139f25ffb392e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 4 Feb 2007 02:38:23 -0500 Subject: bash: Complete long options to git-add. The new --interactive mode of git-add can be very useful, so users will probably want to have completion for it. Likewise the new git-add--interactive executable is actually a plumbing command. Its invoked by `git add --interactive` and is not intended to be invoked directly by the user. Therefore we should hide it from the list of available Git commands. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b87f96d9ea..93f2af5e1f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -235,6 +235,7 @@ __git_commands () for i in $(git help -a|egrep '^ ') do case $i in + add--interactive) : plumbing;; cat-file) : plumbing;; check-ref-format) : plumbing;; commit-tree) : plumbing;; @@ -358,6 +359,19 @@ _git_apply () COMPREPLY=() } +_git_add () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + COMPREPLY=($(compgen -W " + --interactive + " -- "$cur")) + return + esac + COMPREPLY=() +} + _git_branch () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -786,6 +800,7 @@ _git () case "$command" in am) _git_am ;; + add) _git_add ;; apply) _git_apply ;; branch) _git_branch ;; checkout) _git_checkout ;; @@ -852,6 +867,7 @@ complete -o default -o nospace -F _git_log git-whatchanged # included the '.exe' suffix. # if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then +complete -o default -F _git_add git-add.exe complete -o default -F _git_apply git-apply.exe complete -o default -o nospace -F _git git.exe complete -o default -F _git_branch git-branch.exe -- cgit v1.2.3 From 72e5e989b8c22dc6dd2b4f889d5d0dabf698b387 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 4 Feb 2007 02:38:27 -0500 Subject: bash: Add space after unique command name is completed. Because we use the nospace option for our completion function for the main 'git' wrapper bash won't automatically add a space after a unique completion has been made by the user. This has been pointed out in the past by Linus Torvalds as an undesired behavior. I agree. We have to use the nospace option to ensure path completion for a command such as `git show` works properly, but that breaks the common case of getting the space for a unique completion. So now we set IFS=$'\n' (linefeed) and add a trailing space to every possible completion option. This causes bash to insert the space when the completion is unique. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 93f2af5e1f..1cf576e1e5 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -61,6 +61,20 @@ __git_ps1 () fi } +__gitcomp () +{ + local all c s=$'\n' IFS=' '$'\t'$'\n' + for c in $1; do + case "$c" in + --*=*) all="$all$c$s" ;; + *) all="$all$c $s" ;; + esac + done + IFS=$s + COMPREPLY=($(compgen -W "$all" -- "${COMP_WORDS[COMP_CWORD]}")) + return +} + __git_heads () { local cmd i is_hash=y dir="$(__gitdir "$1")" @@ -787,12 +801,12 @@ _git () done if [ $c -eq $COMP_CWORD -a -z "$command" ]; then - COMPREPLY=($(compgen -W " - --git-dir= --version --exec-path - $(__git_commands) - $(__git_aliases) - " -- "${COMP_WORDS[COMP_CWORD]}")) - return; + case "${COMP_WORDS[COMP_CWORD]}" in + --*=*) COMPREPLY=() ;; + --*) __gitcomp "--git-dir= --bare --version --exec-path" ;; + *) __gitcomp "$(__git_commands) $(__git_aliases)" ;; + esac + return fi local expansion=$(__git_aliased_command "$command") -- cgit v1.2.3 From a925c6f165a3374ff4a69d7c991ead2d9fa90b98 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 4 Feb 2007 02:38:30 -0500 Subject: bash: Classify more commends out of completion. Most of these commands are not ones you want to invoke from the command line on a frequent basis, or have been renamed in 1.5.0 to more friendly versions, but the old names are being left behind to support existing scripts in the wild. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1cf576e1e5..382c8177a3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -250,16 +250,24 @@ __git_commands () do case $i in add--interactive) : plumbing;; + applymbox) : ask gittus;; + applypatch) : ask gittus;; + archimport) : import;; cat-file) : plumbing;; check-ref-format) : plumbing;; commit-tree) : plumbing;; convert-objects) : plumbing;; + cvsexportcommit) : export;; + cvsimport) : import;; cvsserver) : daemon;; daemon) : daemon;; + fsck-objects) : plumbing;; fetch-pack) : plumbing;; + fmt-merge-msg) : plumbing;; hash-object) : plumbing;; http-*) : transport;; index-pack) : plumbing;; + init-db) : deprecated;; local-fetch) : plumbing;; mailinfo) : plumbing;; mailsplit) : plumbing;; @@ -272,9 +280,13 @@ __git_commands () parse-remote) : plumbing;; patch-id) : plumbing;; peek-remote) : plumbing;; + prune) : plumbing;; + prune-packed) : plumbing;; + quiltimport) : import;; read-tree) : plumbing;; receive-pack) : plumbing;; reflog) : plumbing;; + repo-config) : plumbing;; rerere) : plumbing;; rev-list) : plumbing;; rev-parse) : plumbing;; @@ -285,14 +297,19 @@ __git_commands () show-index) : plumbing;; ssh-*) : transport;; stripspace) : plumbing;; + svn) : import export;; + svnimport) : import;; symbolic-ref) : plumbing;; + tar-tree) : deprecated;; unpack-file) : plumbing;; unpack-objects) : plumbing;; + update-index) : plumbing;; update-ref) : plumbing;; update-server-info) : daemon;; upload-archive) : plumbing;; upload-pack) : plumbing;; write-tree) : plumbing;; + verify-tag) : plumbing;; *) echo $i;; esac done @@ -834,7 +851,6 @@ _git () pull) _git_pull ;; push) _git_push ;; rebase) _git_rebase ;; - repo-config) _git_config ;; reset) _git_reset ;; show) _git_show ;; show-branch) _git_log ;; -- cgit v1.2.3 From 78d4d6a2815f20607336fcb238ba23efc00e1b0a Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 4 Feb 2007 02:38:37 -0500 Subject: bash: Support unique completion on git-config. In many cases we know a completion will be unique, but we've disabled bash's automatic space addition (-o nospace) so we need to do it ourselves when necessary. This change adds additional support for new configuration options added in 1.5.0, as well as some extended completion support for the color.* family of options. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 120 ++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 38 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 382c8177a3..38d61210a2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -64,14 +64,19 @@ __git_ps1 () __gitcomp () { local all c s=$'\n' IFS=' '$'\t'$'\n' + local cur="${COMP_WORDS[COMP_CWORD]}" + if [ -n "$2" ]; then + cur="$3" + fi for c in $1; do - case "$c" in - --*=*) all="$all$c$s" ;; - *) all="$all$c $s" ;; + case "$c$4" in + --*=*) all="$all$c$4$s" ;; + *.) all="$all$c$4$s" ;; + *) all="$all$c$4 $s" ;; esac done IFS=$s - COMPREPLY=($(compgen -W "$all" -- "${COMP_WORDS[COMP_CWORD]}")) + COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur")) return } @@ -666,26 +671,40 @@ _git_config () local prv="${COMP_WORDS[COMP_CWORD-1]}" case "$prv" in branch.*.remote) - COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + __gitcomp "$(__git_remotes)" return ;; branch.*.merge) - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" return ;; remote.*.fetch) local remote="${prv#remote.}" remote="${remote%.fetch}" - COMPREPLY=($(compgen -W "$(__git_refs_remotes "$remote")" \ - -- "$cur")) + __gitcomp "$(__git_refs_remotes "$remote")" return ;; remote.*.push) local remote="${prv#remote.}" remote="${remote%.push}" - COMPREPLY=($(compgen -W "$(git --git-dir="$(__gitdir)" \ + __gitcomp "$(git --git-dir="$(__gitdir)" \ for-each-ref --format='%(refname):%(refname)' \ - refs/heads)" -- "$cur")) + refs/heads)" + return + ;; + pull.twohead|pull.octopus) + __gitcomp "$(__git_merge_strategies)" + return + ;; + color.branch|color.diff|color.status) + __gitcomp "always never auto" + return + ;; + color.*.*) + __gitcomp " + black red green yellow blue magenta cyan white + bold dim ul blink reverse + " return ;; *.*) @@ -695,41 +714,39 @@ _git_config () esac case "$cur" in --*) - COMPREPLY=($(compgen -W " + __gitcomp " --global --list --replace-all --get --get-all --get-regexp --unset --unset-all - " -- "$cur")) + " return ;; branch.*.*) local pfx="${cur%.*}." cur="${cur##*.}" - COMPREPLY=($(compgen -P "$pfx" -W "remote merge" -- "$cur")) + __gitcomp "remote merge" "$pfx" "$cur" return ;; branch.*) local pfx="${cur%.*}." cur="${cur#*.}" - COMPREPLY=($(compgen -P "$pfx" -S . \ - -W "$(__git_heads)" -- "$cur")) + __gitcomp "$(__git_heads)" "$pfx" "$cur" "." return ;; remote.*.*) local pfx="${cur%.*}." cur="${cur##*.}" - COMPREPLY=($(compgen -P "$pfx" -W "url fetch push" -- "$cur")) + __gitcomp "url fetch push" "$pfx" "$cur" return ;; remote.*) local pfx="${cur%.*}." cur="${cur#*.}" - COMPREPLY=($(compgen -P "$pfx" -S . \ - -W "$(__git_remotes)" -- "$cur")) + __gitcomp "$(__git_remotes)" "$pfx" "$cur" "." return ;; esac - COMPREPLY=($(compgen -W " + __gitcomp " apply.whitespace core.fileMode core.gitProxy @@ -741,40 +758,67 @@ _git_config () core.warnAmbiguousRefs core.compression core.legacyHeaders - i18n.commitEncoding - i18n.logOutputEncoding - diff.color + core.packedGitWindowSize + core.packedGitLimit + color.branch + color.branch.current + color.branch.local + color.branch.remote + color.branch.plain color.diff - diff.renameLimit - diff.renames - pager.color + color.diff.plain + color.diff.meta + color.diff.frag + color.diff.old + color.diff.new + color.diff.commit + color.diff.whitespace color.pager - status.color color.status - log.showroot - show.difftree - showbranch.default - whatchanged.difftree + color.status.header + color.status.added + color.status.changed + color.status.untracked + diff.renameLimit + diff.renames + fetch.unpackLimit + format.headers + gitcvs.enabled + gitcvs.logfile + gc.reflogexpire + gc.reflogexpireunreachable + gc.rerereresolved + gc.rerereunresolved http.sslVerify http.sslCert http.sslKey http.sslCAInfo http.sslCAPath http.maxRequests - http.lowSpeedLimit http.lowSpeedTime + http.lowSpeedLimit + http.lowSpeedTime http.noEPSV + i18n.commitEncoding + i18n.logOutputEncoding + log.showroot + merge.summary + merge.verbosity pack.window + pull.octopus + pull.twohead repack.useDeltaBaseOffset - pull.octopus pull.twohead - merge.summary + show.difftree + showbranch.default + tar.umask + transfer.unpackLimit receive.unpackLimit receive.denyNonFastForwards - user.name user.email - tar.umask - gitcvs.enabled - gitcvs.logfile + user.name + user.email + user.signingkey + whatchanged.difftree branch. remote. - " -- "$cur")) + " } _git_reset () -- cgit v1.2.3 From b3391775e87bed073b93a0a534169a794eceebd7 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 4 Feb 2007 02:38:43 -0500 Subject: bash: Support unique completion when possible. Because our use of -o nospace prevents bash from adding a trailing space when a completion is unique and has been fully completed, we need to perform this addition on our own. This (large) change converts all existing uses of compgen to our wrapper __gitcomp which attempts to handle this by tacking a trailing space onto the end of each offered option. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 190 ++++++++++++++++----------------- 1 file changed, 91 insertions(+), 99 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 38d61210a2..3b1f100f1b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -65,7 +65,7 @@ __gitcomp () { local all c s=$'\n' IFS=' '$'\t'$'\n' local cur="${COMP_WORDS[COMP_CWORD]}" - if [ -n "$2" ]; then + if [ $# -gt 2 ]; then cur="$3" fi for c in $1; do @@ -219,7 +219,7 @@ __git_complete_file () -- "$cur")) ;; *) - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" ;; esac } @@ -231,15 +231,18 @@ __git_complete_revlist () *...*) pfx="${cur%...*}..." cur="${cur#*...}" - COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" "$pfx" "$cur" ;; *..*) pfx="${cur%..*}.." cur="${cur#*..}" - COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" "$pfx" "$cur" + ;; + *.) + __gitcomp "$cur." ;; *) - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" ;; esac } @@ -353,22 +356,19 @@ _git_am () { local cur="${COMP_WORDS[COMP_CWORD]}" if [ -d .dotest ]; then - COMPREPLY=($(compgen -W " - --skip --resolved - " -- "$cur")) + __gitcomp "--skip --resolved" return fi case "$cur" in --whitespace=*) - COMPREPLY=($(compgen -W "$__git_whitespacelist" \ - -- "${cur##--whitespace=}")) + __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}" return ;; --*) - COMPREPLY=($(compgen -W " + __gitcomp " --signoff --utf8 --binary --3way --interactive --whitespace= - " -- "$cur")) + " return esac COMPREPLY=() @@ -379,17 +379,16 @@ _git_apply () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --whitespace=*) - COMPREPLY=($(compgen -W "$__git_whitespacelist" \ - -- "${cur##--whitespace=}")) + __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}" return ;; --*) - COMPREPLY=($(compgen -W " + __gitcomp " --stat --numstat --summary --check --index --cached --index-info --reverse --reject --unidiff-zero --apply --no-add --exclude= --whitespace= --inaccurate-eof --verbose - " -- "$cur")) + " return esac COMPREPLY=() @@ -400,9 +399,7 @@ _git_add () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - COMPREPLY=($(compgen -W " - --interactive - " -- "$cur")) + __gitcomp "--interactive" return esac COMPREPLY=() @@ -410,14 +407,12 @@ _git_add () _git_branch () { - local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" } _git_checkout () { - local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" } _git_cherry_pick () @@ -425,12 +420,10 @@ _git_cherry_pick () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - COMPREPLY=($(compgen -W " - --edit --no-commit - " -- "$cur")) + __gitcomp "--edit --no-commit" ;; *) - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" ;; esac } @@ -440,10 +433,10 @@ _git_commit () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - COMPREPLY=($(compgen -W " + __gitcomp " --all --author= --signoff --verify --no-verify --edit --amend --include --only - " -- "$cur")) + " return esac COMPREPLY=() @@ -456,8 +449,7 @@ _git_diff () _git_diff_tree () { - local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" } _git_fetch () @@ -466,16 +458,15 @@ _git_fetch () case "${COMP_WORDS[0]},$COMP_CWORD" in git-fetch*,1) - COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + __gitcomp "$(__git_remotes)" ;; git,2) - COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + __gitcomp "$(__git_remotes)" ;; *) case "$cur" in *:*) - cur="${cur#*:}" - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" "" "${cur#*:}" ;; *) local remote @@ -483,7 +474,7 @@ _git_fetch () git-fetch) remote="${COMP_WORDS[1]}" ;; git) remote="${COMP_WORDS[2]}" ;; esac - COMPREPLY=($(compgen -W "$(__git_refs2 "$remote")" -- "$cur")) + __gitcomp "$(__git_refs2 "$remote")" ;; esac ;; @@ -495,7 +486,7 @@ _git_format_patch () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - COMPREPLY=($(compgen -W " + __gitcomp " --stdout --attach --thread --output-directory --numbered --start-number @@ -503,7 +494,7 @@ _git_format_patch () --signoff --in-reply-to= --full-index --binary - " -- "$cur")) + " return ;; esac @@ -512,8 +503,7 @@ _git_format_patch () _git_ls_remote () { - local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + __gitcomp "$(__git_remotes)" } _git_ls_tree () @@ -526,13 +516,13 @@ _git_log () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) - COMPREPLY=($(compgen -W " + __gitcomp " oneline short medium full fuller email raw - " -- "${cur##--pretty=}")) + " "" "${cur##--pretty=}" return ;; --*) - COMPREPLY=($(compgen -W " + __gitcomp " --max-count= --max-age= --since= --after= --min-age= --before= --until= --root --not --topo-order --date-order @@ -542,7 +532,7 @@ _git_log () --author= --committer= --grep= --all-match --pretty= --name-status --name-only - " -- "$cur")) + " return ;; esac @@ -554,34 +544,31 @@ _git_merge () local cur="${COMP_WORDS[COMP_CWORD]}" case "${COMP_WORDS[COMP_CWORD-1]}" in -s|--strategy) - COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur")) + __gitcomp "$(__git_merge_strategies)" return esac case "$cur" in --strategy=*) - COMPREPLY=($(compgen -W "$(__git_merge_strategies)" \ - -- "${cur##--strategy=}")) + __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" return ;; --*) - COMPREPLY=($(compgen -W " + __gitcomp " --no-commit --no-summary --squash --strategy - " -- "$cur")) + " return esac - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" } _git_merge_base () { - local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" } _git_name_rev () { - local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "--tags --all --stdin" -- "$cur")) + __gitcomp "--tags --all --stdin" } _git_pull () @@ -590,10 +577,10 @@ _git_pull () case "${COMP_WORDS[0]},$COMP_CWORD" in git-pull*,1) - COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + __gitcomp "$(__git_remotes)" ;; git,2) - COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + __gitcomp "$(__git_remotes)" ;; *) local remote @@ -601,7 +588,7 @@ _git_pull () git-pull) remote="${COMP_WORDS[1]}" ;; git) remote="${COMP_WORDS[2]}" ;; esac - COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur")) + __gitcomp "$(__git_refs "$remote")" ;; esac } @@ -612,10 +599,10 @@ _git_push () case "${COMP_WORDS[0]},$COMP_CWORD" in git-push*,1) - COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + __gitcomp "$(__git_remotes)" ;; git,2) - COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur")) + __gitcomp "$(__git_remotes)" ;; *) case "$cur" in @@ -625,11 +612,10 @@ _git_push () git-push) remote="${COMP_WORDS[1]}" ;; git) remote="${COMP_WORDS[2]}" ;; esac - cur="${cur#*:}" - COMPREPLY=($(compgen -W "$(__git_refs "$remote")" -- "$cur")) + __gitcomp "$(__git_refs "$remote")" "" "${cur#*:}" ;; *) - COMPREPLY=($(compgen -W "$(__git_refs2)" -- "$cur")) + __gitcomp "$(__git_refs2)" ;; esac ;; @@ -640,29 +626,24 @@ _git_rebase () { local cur="${COMP_WORDS[COMP_CWORD]}" if [ -d .dotest ]; then - COMPREPLY=($(compgen -W " - --continue --skip --abort - " -- "$cur")) + __gitcomp "--continue --skip --abort" return fi case "${COMP_WORDS[COMP_CWORD-1]}" in -s|--strategy) - COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur")) + __gitcomp "$(__git_merge_strategies)" return esac case "$cur" in --strategy=*) - COMPREPLY=($(compgen -W "$(__git_merge_strategies)" \ - -- "${cur##--strategy=}")) + __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" return ;; --*) - COMPREPLY=($(compgen -W " - --onto --merge --strategy - " -- "$cur")) + __gitcomp "--onto --merge --strategy" return esac - COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur")) + __gitcomp "$(__git_refs)" } _git_config () @@ -824,8 +805,13 @@ _git_config () _git_reset () { local cur="${COMP_WORDS[COMP_CWORD]}" - local opt="--mixed --hard --soft" - COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur")) + case "$cur" in + --*) + __gitcomp "--mixed --hard --soft" + return + ;; + esac + __gitcomp "$(__git_refs)" } _git_show () @@ -833,13 +819,13 @@ _git_show () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) - COMPREPLY=($(compgen -W " + __gitcomp " oneline short medium full fuller email raw - " -- "${cur##--pretty=}")) + " "" "${cur##--pretty=}" return ;; --*) - COMPREPLY=($(compgen -W "--pretty=" -- "$cur")) + __gitcomp "--pretty=" return ;; esac @@ -906,32 +892,38 @@ _git () _gitk () { local cur="${COMP_WORDS[COMP_CWORD]}" - COMPREPLY=($(compgen -W "--all $(__git_refs)" -- "$cur")) + case "$cur" in + --*) + __gitcomp "--not --all" + return + ;; + esac + __gitcomp "$(__git_refs)" } complete -o default -o nospace -F _git git -complete -o default -F _gitk gitk -complete -o default -F _git_am git-am -complete -o default -F _git_apply git-apply -complete -o default -F _git_branch git-branch -complete -o default -F _git_checkout git-checkout -complete -o default -F _git_cherry_pick git-cherry-pick -complete -o default -F _git_commit git-commit +complete -o default -o nospace -F _gitk gitk +complete -o default -o nospace -F _git_am git-am +complete -o default -o nospace -F _git_apply git-apply +complete -o default -o nospace -F _git_branch git-branch +complete -o default -o nospace -F _git_checkout git-checkout +complete -o default -o nospace -F _git_cherry_pick git-cherry-pick +complete -o default -o nospace -F _git_commit git-commit complete -o default -o nospace -F _git_diff git-diff -complete -o default -F _git_diff_tree git-diff-tree +complete -o default -o nospace -F _git_diff_tree git-diff-tree complete -o default -o nospace -F _git_fetch git-fetch complete -o default -o nospace -F _git_format_patch git-format-patch complete -o default -o nospace -F _git_log git-log -complete -o default -F _git_ls_remote git-ls-remote +complete -o default -o nospace -F _git_ls_remote git-ls-remote complete -o default -o nospace -F _git_ls_tree git-ls-tree -complete -o default -F _git_merge git-merge -complete -o default -F _git_merge_base git-merge-base -complete -o default -F _git_name_rev git-name-rev +complete -o default -o nospace -F _git_merge git-merge +complete -o default -o nospace -F _git_merge_base git-merge-base +complete -o default -o nospace -F _git_name_rev git-name-rev complete -o default -o nospace -F _git_pull git-pull complete -o default -o nospace -F _git_push git-push -complete -o default -F _git_rebase git-rebase -complete -o default -F _git_config git-config -complete -o default -F _git_reset git-reset +complete -o default -o nospace -F _git_rebase git-rebase +complete -o default -o nospace -F _git_config git-config +complete -o default -o nospace -F _git_reset git-reset complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged @@ -941,19 +933,19 @@ complete -o default -o nospace -F _git_log git-whatchanged # included the '.exe' suffix. # if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then -complete -o default -F _git_add git-add.exe -complete -o default -F _git_apply git-apply.exe +complete -o default -o nospace -F _git_add git-add.exe +complete -o default -o nospace -F _git_apply git-apply.exe complete -o default -o nospace -F _git git.exe -complete -o default -F _git_branch git-branch.exe +complete -o default -o nospace -F _git_branch git-branch.exe complete -o default -o nospace -F _git_diff git-diff.exe complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe complete -o default -o nospace -F _git_format_patch git-format-patch.exe complete -o default -o nospace -F _git_log git-log.exe complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe -complete -o default -F _git_merge_base git-merge-base.exe -complete -o default -F _git_name_rev git-name-rev.exe +complete -o default -o nospace -F _git_merge_base git-merge-base.exe +complete -o default -o nospace -F _git_name_rev git-name-rev.exe complete -o default -o nospace -F _git_push git-push.exe -complete -o default -F _git_config git-config +complete -o default -o nospace -F _git_config git-config complete -o default -o nospace -F _git_show git-show.exe complete -o default -o nospace -F _git_log git-show-branch.exe complete -o default -o nospace -F _git_log git-whatchanged.exe -- cgit v1.2.3 From ec8048913217d8ff6e54950a0cb8ab2e739a1d1f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 4 Feb 2007 02:38:47 -0500 Subject: bash: Support internal revlist options better. format-patch/log/whatchanged all take --not and --all as options to the internal revlist process. So these should be supported as possible completions. gitk takes anything rev-list/log/whatchanged takes, so we should use complete_revlist to handle its options. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3b1f100f1b..466cc32f4c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -494,6 +494,7 @@ _git_format_patch () --signoff --in-reply-to= --full-index --binary + --not --all " return ;; @@ -532,6 +533,7 @@ _git_log () --author= --committer= --grep= --all-match --pretty= --name-status --name-only + --not --all " return ;; @@ -898,7 +900,7 @@ _gitk () return ;; esac - __gitcomp "$(__git_refs)" + __git_complete_revlist } complete -o default -o nospace -F _git git -- cgit v1.2.3 From d8a9fea5ea44a4068cc95de9368798ae3cee74ee Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 5 Feb 2007 15:44:22 -0500 Subject: bash: Support completion on git-cherry. I just realized I did not support ref name completion for git-cherry. This tool is just too useful to contributors who submit patches upstream by email; completion support for it is very handy. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 466cc32f4c..b434332bf7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -415,6 +415,11 @@ _git_checkout () __gitcomp "$(__git_refs)" } +_git_cherry () +{ + __gitcomp "$(__git_refs)" +} + _git_cherry_pick () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -867,6 +872,7 @@ _git () apply) _git_apply ;; branch) _git_branch ;; checkout) _git_checkout ;; + cherry) _git_cherry ;; cherry-pick) _git_cherry_pick ;; commit) _git_commit ;; config) _git_config ;; @@ -909,6 +915,7 @@ complete -o default -o nospace -F _git_am git-am complete -o default -o nospace -F _git_apply git-apply complete -o default -o nospace -F _git_branch git-branch complete -o default -o nospace -F _git_checkout git-checkout +complete -o default -o nospace -F _git_cherry git-cherry complete -o default -o nospace -F _git_cherry_pick git-cherry-pick complete -o default -o nospace -F _git_commit git-commit complete -o default -o nospace -F _git_diff git-diff @@ -939,6 +946,7 @@ complete -o default -o nospace -F _git_add git-add.exe complete -o default -o nospace -F _git_apply git-apply.exe complete -o default -o nospace -F _git git.exe complete -o default -o nospace -F _git_branch git-branch.exe +complete -o default -o nospace -F _git_cherry git-cherry.exe complete -o default -o nospace -F _git_diff git-diff.exe complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe complete -o default -o nospace -F _git_format_patch git-format-patch.exe -- cgit v1.2.3 From 983591c31e2529fbba8dd2d69dce5ab446584921 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 5 Feb 2007 15:44:24 -0500 Subject: bash: Hide diff-stages from completion. Apparently nobody really makes use of git-diff-stages, as nobody has complained that it is not supported by the git-diff frontend. Since its likely this will go away in the future, we should not offer it as a possible subcommand completion. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b434332bf7..c0dae5484d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -269,6 +269,7 @@ __git_commands () cvsimport) : import;; cvsserver) : daemon;; daemon) : daemon;; + diff-stages) : nobody uses it;; fsck-objects) : plumbing;; fetch-pack) : plumbing;; fmt-merge-msg) : plumbing;; -- cgit v1.2.3 From b26c87488fa89e444972c486326eecca5b2132c7 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 5 Feb 2007 15:44:28 -0500 Subject: bash: Offer --prune completion for git-gc. I'm lazy. I don't want to type out --prune if bash can do it for me with --. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c0dae5484d..324bfbd8bb 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -508,6 +508,18 @@ _git_format_patch () __git_complete_revlist } +_git_gc () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--prune" + return + ;; + esac + COMPREPLY=() +} + _git_ls_remote () { __gitcomp "$(__git_remotes)" @@ -881,6 +893,7 @@ _git () diff-tree) _git_diff_tree ;; fetch) _git_fetch ;; format-patch) _git_format_patch ;; + gc) _git_gc ;; log) _git_log ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; @@ -923,6 +936,7 @@ complete -o default -o nospace -F _git_diff git-diff complete -o default -o nospace -F _git_diff_tree git-diff-tree complete -o default -o nospace -F _git_fetch git-fetch complete -o default -o nospace -F _git_format_patch git-format-patch +complete -o default -o nospace -F _git_gc git-gc complete -o default -o nospace -F _git_log git-log complete -o default -o nospace -F _git_ls_remote git-ls-remote complete -o default -o nospace -F _git_ls_tree git-ls-tree -- cgit v1.2.3 From e459415c9c3c8e855998dd0247f23d32a0dca4ff Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 5 Feb 2007 15:44:30 -0500 Subject: bash: Hide git-resolve, its deprecated. Don't offer resolve as a possible subcommand completion. If you read the top of the script, there is a big warning about how it will go away soon in the near future. People should not be using it. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 324bfbd8bb..089a7b0571 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -297,6 +297,7 @@ __git_commands () reflog) : plumbing;; repo-config) : plumbing;; rerere) : plumbing;; + resolve) : dead dont use;; rev-list) : plumbing;; rev-parse) : plumbing;; runstatus) : plumbing;; -- cgit v1.2.3 From 1b71eb35ddb20057a266b05258de24d350ed5ba5 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 5 Feb 2007 15:44:32 -0500 Subject: bash: Support --add completion to git-config. We've recently added --add as an argument to git-config, but I missed putting it into the earlier round of git-config updates within the bash completion. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 089a7b0571..f9b827a617 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -719,7 +719,7 @@ _git_config () __gitcomp " --global --list --replace-all --get --get-all --get-regexp - --unset --unset-all + --add --unset --unset-all " return ;; -- cgit v1.2.3 From b2e69f6299b5a46840600176679b94843cf63a8d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 5 Feb 2007 15:44:37 -0500 Subject: bash: Support git-bisect and its subcommands. We now offer completion support for git-bisect's subcommands, as well as ref name completion on the good/bad/reset subcommands. This should make interacting with git-bisect slightly easier on the fingers. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f9b827a617..430e6367a7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -407,6 +407,35 @@ _git_add () COMPREPLY=() } +_git_bisect () +{ + local i c=1 command + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + start|bad|good|reset|visualize|replay|log) + command="$i" + break + ;; + esac + c=$((++c)) + done + + if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + __gitcomp "start bad good reset visualize replay log" + return + fi + + case "$command" in + bad|good|reset) + __gitcomp "$(__git_refs)" + ;; + *) + COMPREPLY=() + ;; + esac +} + _git_branch () { __gitcomp "$(__git_refs)" @@ -884,6 +913,7 @@ _git () am) _git_am ;; add) _git_add ;; apply) _git_apply ;; + bisect) _git_bisect ;; branch) _git_branch ;; checkout) _git_checkout ;; cherry) _git_cherry ;; @@ -928,6 +958,7 @@ complete -o default -o nospace -F _git git complete -o default -o nospace -F _gitk gitk complete -o default -o nospace -F _git_am git-am complete -o default -o nospace -F _git_apply git-apply +complete -o default -o nospace -F _git_bisect git-bisect complete -o default -o nospace -F _git_branch git-branch complete -o default -o nospace -F _git_checkout git-checkout complete -o default -o nospace -F _git_cherry git-cherry -- cgit v1.2.3 From 3fb624521e059c6c3caef470a9ff03f72b86036c Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 30 Jan 2007 13:26:50 +0530 Subject: blameview: Support browsable functionality to blameview. Double clicking on the row execs a new blameview with commit hash as argument. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Junio C Hamano --- contrib/blameview/blameview.perl | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/blameview/blameview.perl b/contrib/blameview/blameview.perl index 5e9a67c123..807d01fe3d 100755 --- a/contrib/blameview/blameview.perl +++ b/contrib/blameview/blameview.perl @@ -3,7 +3,17 @@ use Gtk2 -init; use Gtk2::SimpleList; -my $fn = shift or die "require filename to blame"; +my $hash; +my $fn; +if ( @ARGV == 1 ) { + $hash = "HEAD"; + $fn = shift; +} elsif ( @ARGV == 2 ) { + $hash = shift; + $fn = shift; +} else { + die "Usage blameview [] "; +} Gtk2::Rc->parse_string(<<'EOS'); style "treeview_style" @@ -27,17 +37,24 @@ $scrolled_window->add($fileview); $fileview->get_column(0)->set_spacing(0); $fileview->set_size_request(1024, 768); $fileview->set_rules_hint(1); +$fileview->signal_connect (row_activated => sub { + my ($sl, $path, $column) = @_; + my $row_ref = $sl->get_row_data_from_path ($path); + system("blameview @$row_ref[0] $fn"); + # $row_ref is now an array ref to the double-clicked row's data. + }); my $fh; -open($fh, '-|', "git cat-file blob HEAD:$fn") +open($fh, '-|', "git cat-file blob $hash:$fn") or die "unable to open $fn: $!"; + while(<$fh>) { chomp; $fileview->{data}->[$.] = ['HEAD', '?', "$fn:$.", $_]; } my $blame; -open($blame, '-|', qw(git blame --incremental --), $fn) +open($blame, '-|', qw(git blame --incremental --), $fn, $hash) or die "cannot start git-blame $fn"; Glib::IO->add_watch(fileno($blame), 'in', \&read_blame_line); -- cgit v1.2.3 From 98d47d4ccf76725e7833c1bbda1da82f7648925f Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Fri, 12 Jan 2007 22:57:03 +0100 Subject: Add hg-to-git conversion utility. hg-to-git.py is able to convert a Mercurial repository into a git one, and preserves the branches in the process (unlike tailor) hg-to-git.py can probably be greatly improved (it's a rather crude combination of shell and python) but it does already work quite well for me. Features: - supports incremental conversion (for keeping a git repo in sync with a hg one) - supports hg branches - converts hg tags Signed-off-by: Stelian Pop Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 233 ++++++++++++++++++++++++++++++++++++++++ contrib/hg-to-git/hg-to-git.txt | 21 ++++ 2 files changed, 254 insertions(+) create mode 100755 contrib/hg-to-git/hg-to-git.py create mode 100644 contrib/hg-to-git/hg-to-git.txt (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py new file mode 100755 index 0000000000..37337ff01f --- /dev/null +++ b/contrib/hg-to-git/hg-to-git.py @@ -0,0 +1,233 @@ +#! /usr/bin/python + +""" hg-to-svn.py - A Mercurial to GIT converter + + Copyright (C)2007 Stelian Pop + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +""" + +import os, os.path, sys +import tempfile, popen2, pickle, getopt +import re + +# Maps hg version -> git version +hgvers = {} +# List of children for each hg revision +hgchildren = {} +# Current branch for each hg revision +hgbranch = {} + +#------------------------------------------------------------------------------ + +def usage(): + + print """\ +%s: [OPTIONS] + +options: + -s, --gitstate=FILE: name of the state to be saved/read + for incrementals + +required: + hgprj: name of the HG project to import (directory) +""" % sys.argv[0] + +#------------------------------------------------------------------------------ + +def getgitenv(user, date): + env = '' + elems = re.compile('(.*?)\s+<(.*)>').match(user) + if elems: + env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1) + env += 'export GIT_COMMITER_NAME="%s" ;' % elems.group(1) + env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2) + env += 'export GIT_COMMITER_EMAIL="%s" ;' % elems.group(2) + else: + env += 'export GIT_AUTHOR_NAME="%s" ;' % user + env += 'export GIT_COMMITER_NAME="%s" ;' % user + env += 'export GIT_AUTHOR_EMAIL= ;' + env += 'export GIT_COMMITER_EMAIL= ;' + + env += 'export GIT_AUTHOR_DATE="%s" ;' % date + env += 'export GIT_COMMITTER_DATE="%s" ;' % date + return env + +#------------------------------------------------------------------------------ + +state = '' + +try: + opts, args = getopt.getopt(sys.argv[1:], 's:t:', ['gitstate=', 'tempdir=']) + for o, a in opts: + if o in ('-s', '--gitstate'): + state = a + state = os.path.abspath(state) + + if len(args) != 1: + raise('params') +except: + usage() + sys.exit(1) + +hgprj = args[0] +os.chdir(hgprj) + +if state: + if os.path.exists(state): + print 'State does exist, reading' + f = open(state, 'r') + hgvers = pickle.load(f) + else: + print 'State does not exist, first run' + +tip = os.popen('hg tip | head -1 | cut -f 2 -d :').read().strip() +print 'tip is', tip + +# Calculate the branches +print 'analysing the branches...' +hgchildren["0"] = () +hgbranch["0"] = "master" +for cset in range(1, int(tip) + 1): + hgchildren[str(cset)] = () + prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines() + if len(prnts) > 0: + parent = prnts[0].strip() + else: + parent = str(cset - 1) + hgchildren[parent] += ( str(cset), ) + if len(prnts) > 1: + mparent = prnts[1].strip() + hgchildren[mparent] += ( str(cset), ) + else: + mparent = None + + if mparent: + # For merge changesets, take either one, preferably the 'master' branch + if hgbranch[mparent] == 'master': + hgbranch[str(cset)] = 'master' + else: + hgbranch[str(cset)] = hgbranch[parent] + else: + # Normal changesets + # For first children, take the parent branch, for the others create a new branch + if hgchildren[parent][0] == str(cset): + hgbranch[str(cset)] = hgbranch[parent] + else: + hgbranch[str(cset)] = "branch-" + str(cset) + +if not hgvers.has_key("0"): + print 'creating repository' + os.system('git-init-db') + +# loop through every hg changeset +for cset in range(int(tip) + 1): + + # incremental, already seen + if hgvers.has_key(str(cset)): + continue + + # get info + prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines() + if len(prnts) > 0: + parent = prnts[0].strip() + else: + parent = str(cset - 1) + if len(prnts) > 1: + mparent = prnts[1].strip() + else: + mparent = None + + (fdcomment, filecomment) = tempfile.mkstemp() + csetcomment = os.popen('hg log -r %d -v | grep -v ^changeset: | grep -v ^parent: | grep -v ^user: | grep -v ^date | grep -v ^files: | grep -v ^description: | grep -v ^tag:' % cset).read().strip() + os.write(fdcomment, csetcomment) + os.close(fdcomment) + + date = os.popen('hg log -r %d | grep ^date: | cut -f 2- -d :' % cset).read().strip() + + tag = os.popen('hg log -r %d | grep ^tag: | cut -f 2- -d :' % cset).read().strip() + + user = os.popen('hg log -r %d | grep ^user: | cut -f 2- -d :' % cset).read().strip() + + print '-----------------------------------------' + print 'cset:', cset + print 'branch:', hgbranch[str(cset)] + print 'user:', user + print 'date:', date + print 'comment:', csetcomment + print 'parent:', parent + if mparent: + print 'mparent:', mparent + if tag: + print 'tag:', tag + print '-----------------------------------------' + + # checkout the parent if necessary + if cset != 0: + if hgbranch[str(cset)] == "branch-" + str(cset): + print 'creating new branch', hgbranch[str(cset)] + os.system('git-checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent])) + else: + print 'checking out branch', hgbranch[str(cset)] + os.system('git-checkout %s' % hgbranch[str(cset)]) + + # merge + if mparent: + if hgbranch[parent] == hgbranch[str(cset)]: + otherbranch = hgbranch[mparent] + else: + otherbranch = hgbranch[parent] + print 'merging', otherbranch, 'into', hgbranch[str(cset)] + os.system(getgitenv(user, date) + 'git-merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch)) + + # remove everything except .git and .hg directories + os.system('find . \( -path "./.hg" -o -path "./.git" \) -prune -o ! -name "." -print | xargs rm -rf') + + # repopulate with checkouted files + os.system('hg update -C %d' % cset) + + # add new files + os.system('git-ls-files -x .hg --others | git-update-index --add --stdin') + # delete removed files + os.system('git-ls-files -x .hg --deleted | git-update-index --remove --stdin') + + # commit + os.system(getgitenv(user, date) + 'git-commit -a -F %s' % filecomment) + os.unlink(filecomment) + + # tag + if tag and tag != 'tip': + os.system(getgitenv(user, date) + 'git-tag %s' % tag) + + # delete branch if not used anymore... + if mparent and len(hgchildren[str(cset)]): + print "Deleting unused branch:", otherbranch + os.system('git-branch -d %s' % otherbranch) + + # retrieve and record the version + vvv = os.popen('git-show | head -1').read() + vvv = vvv[vvv.index(' ') + 1 : ].strip() + print 'record', cset, '->', vvv + hgvers[str(cset)] = vvv + +os.system('git-repack -a -d') + +# write the state for incrementals +if state: + print 'Writing state' + f = open(state, 'w') + pickle.dump(hgvers, f) + +# vim: et ts=8 sw=4 sts=4 diff --git a/contrib/hg-to-git/hg-to-git.txt b/contrib/hg-to-git/hg-to-git.txt new file mode 100644 index 0000000000..91f8fe6410 --- /dev/null +++ b/contrib/hg-to-git/hg-to-git.txt @@ -0,0 +1,21 @@ +hg-to-git.py is able to convert a Mercurial repository into a git one, +and preserves the branches in the process (unlike tailor) + +hg-to-git.py can probably be greatly improved (it's a rather crude +combination of shell and python) but it does already work quite well for +me. Features: + - supports incremental conversion + (for keeping a git repo in sync with a hg one) + - supports hg branches + - converts hg tags + +Note that the git repository will be created 'in place' (at the same +location as the source hg repo). You will have to manually remove the +'.hg' directory after the conversion. + +Also note that the incremental conversion uses 'simple' hg changesets +identifiers (ordinals, as opposed to SHA-1 ids), and since these ids +are not stable across different repositories the hg-to-git.py state file +is forever tied to one hg repository. + +Stelian Pop -- cgit v1.2.3 From 28389d45fb2161d4dcdc1bbfabbcc2fb135914c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Mon, 5 Feb 2007 14:22:28 -0800 Subject: git-blame: an Emacs minor mode to view file with git-blame output. Here's another version of git-blame.el that automatically tries to create a sensible list of colors to use for both light and dark backgrounds. Plus a few minor fixes. To use: 1) Load into emacs: M-x load-file RET git-blame.el RET 2) Open a git-controlled file 3) Blame: M-x git-blame-mode Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 180 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 contrib/emacs/git-blame.el (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el new file mode 100644 index 0000000000..62cf24c996 --- /dev/null +++ b/contrib/emacs/git-blame.el @@ -0,0 +1,180 @@ +;;; git-blame.el +;; David Kågedal +;; Message-ID: <87iren2vqx.fsf@morpheus.local> + +(require 'cl) +(defun color-scale (l) + (let* ((colors ()) + r g b) + (setq r l) + (while r + (setq g l) + (while g + (setq b l) + (while b + (push (concat "#" (car r) (car g) (car b)) colors) + (pop b)) + (pop g)) + (pop r)) + colors)) + +(defvar git-blame-dark-colors + (color-scale '("00" "04" "08" "0c" + "10" "14" "18" "1c" + "20" "24" "28" "2c" + "30" "34" "38" "3c"))) + +(defvar git-blame-light-colors + (color-scale '("c0" "c4" "c8" "cc" + "d0" "d4" "d8" "dc" + "e0" "e4" "e8" "ec" + "f0" "f4" "f8" "fc"))) + +(defvar git-blame-ancient-color "dark green") + +(defvar git-blame-overlays nil) +(defvar git-blame-cache nil) + +(defvar git-blame-mode nil) +(make-variable-buffer-local 'git-blame-mode) +(push (list 'git-blame-mode " blame") minor-mode-alist) + +(defun git-blame-mode (&optional arg) + (interactive "P") + (if arg + (setq git-blame-mode (eq arg 1)) + (setq git-blame-mode (not git-blame-mode))) + (make-local-variable 'git-blame-overlays) + (make-local-variable 'git-blame-colors) + (make-local-variable 'git-blame-cache) + (let ((bgmode (cdr (assoc 'background-mode (frame-parameters))))) + (if (eq bgmode 'dark) + (setq git-blame-colors git-blame-dark-colors) + (setq git-blame-colors git-blame-light-colors))) + (if git-blame-mode + (git-blame-run) + (git-blame-cleanup))) + +(defun git-blame-run () + (let* ((display-buf (current-buffer)) + (blame-buf (get-buffer-create + (concat " git blame for " (buffer-name)))) + (proc (start-process "git-blame" blame-buf + "git" "blame" "--incremental" + (file-name-nondirectory buffer-file-name)))) + (mapcar 'delete-overlay git-blame-overlays) + (setq git-blame-overlays nil) + (setq git-blame-cache (make-hash-table :test 'equal)) + (with-current-buffer blame-buf + (erase-buffer) + (make-local-variable 'git-blame-file) + (make-local-variable 'git-blame-current) + (setq git-blame-file display-buf) + (setq git-blame-current nil)) + (set-process-filter proc 'git-blame-filter) + (set-process-sentinel proc 'git-blame-sentinel))) + +(defun git-blame-cleanup () + "Remove all blame properties" + (mapcar 'delete-overlay git-blame-overlays) + (setq git-blame-overlays nil) + (let ((modified (buffer-modified-p))) + (remove-text-properties (point-min) (point-max) '(point-entered nil)) + (set-buffer-modified-p modified))) + +(defun git-blame-sentinel (proc status) + ;;(kill-buffer (process-buffer proc)) + (message "git blame finished")) + +(defvar in-blame-filter nil) + +(defun git-blame-filter (proc str) + (save-excursion + (set-buffer (process-buffer proc)) + (goto-char (process-mark proc)) + (insert-before-markers str) + (goto-char 0) + (unless in-blame-filter + (let ((more t) + (in-blame-filter t)) + (while more + (setq more (git-blame-parse))))))) + +(defun git-blame-parse () + (cond ((looking-at "\\([0-9a-f]\\{40\\}\\) \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\)\n") + (let ((hash (match-string 1)) + (src-line (string-to-number (match-string 2))) + (res-line (string-to-number (match-string 3))) + (num-lines (string-to-number (match-string 4)))) + (setq git-blame-current + (git-blame-new-commit + hash src-line res-line num-lines))) + (delete-region (point) (match-end 0)) + t) + ((looking-at "filename \\(.+\\)\n") + (let ((filename (match-string 1))) + (git-blame-add-info "filename" filename)) + (delete-region (point) (match-end 0)) + t) + ((looking-at "\\([a-z-]+\\) \\(.+\\)\n") + (let ((key (match-string 1)) + (value (match-string 2))) + (git-blame-add-info key value)) + (delete-region (point) (match-end 0)) + t) + ((looking-at "boundary\n") + (setq git-blame-current nil) + (delete-region (point) (match-end 0)) + t) + (t + nil))) + + +(defun git-blame-new-commit (hash src-line res-line num-lines) + (save-excursion + (set-buffer git-blame-file) + (let ((info (gethash hash git-blame-cache)) + (inhibit-point-motion-hooks t)) + (when (not info) + (let ((color (pop git-blame-colors))) + (unless color + (setq color git-blame-ancient-color)) + (setq info (list hash src-line res-line num-lines + (cons 'color color)))) + (puthash hash info git-blame-cache)) + (goto-line res-line) + (while (> num-lines 0) + (if (get-text-property (point) 'git-blame) + (forward-line) + (let* ((start (point)) + (end (progn (forward-line 1) (point))) + (ovl (make-overlay start end))) + (push ovl git-blame-overlays) + (overlay-put ovl 'git-blame info) + (overlay-put ovl 'help-echo hash) + (overlay-put ovl 'face (list :background + (cdr (assq 'color (cddddr info))))) + ;;(overlay-put ovl 'point-entered + ;; `(lambda (x y) (git-blame-identify ,hash))) + (let ((modified (buffer-modified-p))) + (put-text-property (if (= start 1) start (1- start)) (1- end) + 'point-entered + `(lambda (x y) (git-blame-identify ,hash))) + (set-buffer-modified-p modified)))) + (setq num-lines (1- num-lines)))))) + +(defun git-blame-add-info (key value) + (if git-blame-current + (nconc git-blame-current (list (cons (intern key) value))))) + +(defun git-blame-current-commit () + (let ((info (get-char-property (point) 'git-blame))) + (if info + (car info) + (error "No commit info")))) + +(defun git-blame-identify (&optional hash) + (interactive) + (shell-command + (format "git log -1 --pretty=oneline %s" (or hash + (git-blame-current-commit))))) -- cgit v1.2.3 From c5650b0840ef1630ab9e67e9e314fb73ca112cdc Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 4 Feb 2007 23:52:02 -0500 Subject: bash: Support git-rebase -m continuation completion. Apparently `git-rebase -m` uses a metadata directory within .git (.git/.dotest-merge) rather than .dotest used by git-am (and git-rebase without the -m option). This caused the completion code to not offer --continue, --skip or --abort when working within a `git-rebase -m` session. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 430e6367a7..b0ff87d8d0 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -675,7 +675,7 @@ _git_push () _git_rebase () { local cur="${COMP_WORDS[COMP_CWORD]}" - if [ -d .dotest ]; then + if [ -d .dotest ] || [ -d .git/.dotest-merge ]; then __gitcomp "--continue --skip --abort" return fi -- cgit v1.2.3 From 88293c675c0c15c8247de32e903f4302bac63027 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 4 Feb 2007 23:52:08 -0500 Subject: bash: Complete git-remote subcommands. Completing the 3 core subcommands to git-remote, along with the names of remotes for 'show' and 'prune' (which take only existing remotes) is handy. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b0ff87d8d0..eecdaa0e75 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -852,6 +852,32 @@ _git_config () " } +_git_remote () +{ + local i c=1 command + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + add|show|prune) command="$i"; break ;; + esac + c=$((++c)) + done + + if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + __gitcomp "add show prune" + return + fi + + case "$command" in + show|prune) + __gitcomp "$(__git_remotes)" + ;; + *) + COMPREPLY=() + ;; + esac +} + _git_reset () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -934,6 +960,7 @@ _git () pull) _git_pull ;; push) _git_push ;; rebase) _git_rebase ;; + remote) _git_remote ;; reset) _git_reset ;; show) _git_show ;; show-branch) _git_log ;; @@ -979,6 +1006,7 @@ complete -o default -o nospace -F _git_pull git-pull complete -o default -o nospace -F _git_push git-push complete -o default -o nospace -F _git_rebase git-rebase complete -o default -o nospace -F _git_config git-config +complete -o default -o nospace -F _git_remote git-remote complete -o default -o nospace -F _git_reset git-reset complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_log git-show-branch -- cgit v1.2.3 From a7fd83b0b06c518c90b59836264cf93efce0795f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 6 Feb 2007 16:33:16 -0800 Subject: Remove contrib/colordiff This has completely been superseded by built-in --color option. Signed-off-by: Junio C Hamano --- contrib/colordiff/README | 2 - contrib/colordiff/colordiff.perl | 196 --------------------------------------- 2 files changed, 198 deletions(-) delete mode 100644 contrib/colordiff/README delete mode 100755 contrib/colordiff/colordiff.perl (limited to 'contrib') diff --git a/contrib/colordiff/README b/contrib/colordiff/README deleted file mode 100644 index 2678fdf9c2..0000000000 --- a/contrib/colordiff/README +++ /dev/null @@ -1,2 +0,0 @@ -This is "colordiff" (http://colordiff.sourceforge.net/) by Dave -Ewart , modified specifically for git. diff --git a/contrib/colordiff/colordiff.perl b/contrib/colordiff/colordiff.perl deleted file mode 100755 index 9566a765ef..0000000000 --- a/contrib/colordiff/colordiff.perl +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/perl -w -# -# $Id: colordiff.pl,v 1.4.2.10 2004/01/04 15:02:59 daveewart Exp $ - -######################################################################## -# # -# ColorDiff - a wrapper/replacment for 'diff' producing # -# colourful output # -# # -# Copyright (C)2002-2004 Dave Ewart (davee@sungate.co.uk) # -# # -######################################################################## -# # -# This program is free software; you can redistribute it and/or modify # -# it under the terms of the GNU General Public License as published by # -# the Free Software Foundation; either version 2 of the License, or # -# (at your option) any later version. # -# # -# This program is distributed in the hope that it will be useful, # -# but WITHOUT ANY WARRANTY; without even the implied warranty of # -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # -# GNU General Public License for more details. # -# # -# You should have received a copy of the GNU General Public License # -# along with this program; if not, write to the Free Software # -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # -# # -######################################################################## - -use strict; -use Getopt::Long qw(:config pass_through); -use IPC::Open2; - -my $app_name = 'colordiff'; -my $version = '1.0.4'; -my $author = 'Dave Ewart'; -my $author_email = 'davee@sungate.co.uk'; -my $app_www = 'http://colordiff.sourceforge.net/'; -my $copyright = '(C)2002-2004'; -my $show_banner = 1; - -# ANSI sequences for colours -my %colour; -$colour{white} = "\033[1;37m"; -$colour{yellow} = "\033[1;33m"; -$colour{green} = "\033[1;32m"; -$colour{blue} = "\033[1;34m"; -$colour{cyan} = "\033[1;36m"; -$colour{red} = "\033[1;31m"; -$colour{magenta} = "\033[1;35m"; -$colour{black} = "\033[1;30m"; -$colour{darkwhite} = "\033[0;37m"; -$colour{darkyellow} = "\033[0;33m"; -$colour{darkgreen} = "\033[0;32m"; -$colour{darkblue} = "\033[0;34m"; -$colour{darkcyan} = "\033[0;36m"; -$colour{darkred} = "\033[0;31m"; -$colour{darkmagenta} = "\033[0;35m"; -$colour{darkblack} = "\033[0;30m"; -$colour{OFF} = "\033[0;0m"; - -# Default colours if /etc/colordiffrc or ~/.colordiffrc do not exist -my $plain_text = $colour{OFF}; -my $file_old = $colour{red}; -my $file_new = $colour{blue}; -my $diff_stuff = $colour{magenta}; - -# Locations for personal and system-wide colour configurations -my $HOME = $ENV{HOME}; -my $etcdir = '/etc'; - -my ($setting, $value); -my @config_files = ("$etcdir/colordiffrc", "$HOME/.colordiffrc"); -my $config_file; - -foreach $config_file (@config_files) { - if (open(COLORDIFFRC, "<$config_file")) { - while () { - chop; - next if (/^#/ || /^$/); - s/\s+//g; - ($setting, $value) = split ('='); - if ($setting eq 'banner') { - if ($value eq 'no') { - $show_banner = 0; - } - next; - } - if (!defined $colour{$value}) { - print "Invalid colour specification ($value) in $config_file\n"; - next; - } - if ($setting eq 'plain') { - $plain_text = $colour{$value}; - } - elsif ($setting eq 'oldtext') { - $file_old = $colour{$value}; - } - elsif ($setting eq 'newtext') { - $file_new = $colour{$value}; - } - elsif ($setting eq 'diffstuff') { - $diff_stuff = $colour{$value}; - } - else { - print "Unknown option in $etcdir/colordiffrc: $setting\n"; - } - } - close COLORDIFFRC; - } -} - -# colordiff specific options here. Need to pre-declare if using variables -GetOptions( - "no-banner" => sub { $show_banner = 0 }, - "plain-text=s" => \&set_color, - "file-old=s" => \&set_color, - "file-new=s" => \&set_color, - "diff-stuff=s" => \&set_color -); - -if ($show_banner == 1) { - print STDERR "$app_name $version ($app_www)\n"; - print STDERR "$copyright $author, $author_email\n\n"; -} - -if (defined $ARGV[0]) { - # More reliable way of pulling in arguments - open2(\*INPUTSTREAM, undef, "git", "diff", @ARGV); -} -else { - *INPUTSTREAM = \*STDIN; -} - -my $record; -my $nrecs = 0; -my $inside_file_old = 1; -my $nparents = undef; - -while () { - $nrecs++; - if (/^(\@\@+) -[-+0-9, ]+ \1/) { - print "$diff_stuff"; - $nparents = length($1) - 1; - } - elsif (/^diff -/ || /^index / || - /^old mode / || /^new mode / || - /^deleted file mode / || /^new file mode / || - /^similarity index / || /^dissimilarity index / || - /^copy from / || /^copy to / || - /^rename from / || /^rename to /) { - $nparents = undef; - print "$diff_stuff"; - } - elsif (defined $nparents) { - if ($nparents == 1) { - if (/^\+/) { - print $file_new; - } - elsif (/^-/) { - print $file_old; - } - else { - print $plain_text; - } - } - elsif (/^ {$nparents}/) { - print "$plain_text"; - } - elsif (/^[+ ]{$nparents}/) { - print "$file_new"; - } - elsif (/^[- ]{$nparents}/) { - print "$file_old"; - } - else { - print $plain_text; - } - } - elsif (/^--- / || /^\+\+\+ /) { - print $diff_stuff; - } - else { - print "$plain_text"; - } - s/$/$colour{OFF}/; - print "$_"; -} -close INPUTSTREAM; - -sub set_color { - my ($type, $color) = @_; - - $type =~ s/-/_/; - eval "\$$type = \$colour{$color}"; -} -- cgit v1.2.3 From c4cf2d4f8793ea8f889052391d72dd4066e88155 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Feb 2007 22:57:01 +0100 Subject: Minor cleanups and print an error message of git fast-import if it fails. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 19f5f03476..06de0eae6d 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -154,6 +154,9 @@ if len(changeRange) == 0: sys.stderr.write("\n") tz = - time.timezone / 36 +tzsign = ("%s" % tz)[0] +if tzsign != '+' and tzsign != '-': + tz = "+" + ("%s" % tz) if len(revision) > 0: print "Doing initial import of %s from revision %s" % (prefix, revision) @@ -165,21 +168,25 @@ if len(revision) > 0: fileCnt = 0 for info in p4CmdList("files %s...%s" % (prefix, revision)): + change = info["change"] + if change > newestRevision: + newestRevision = change + if info["action"] == "delete": continue + for prop in [ "depotFile", "rev", "action", "type" ]: details["%s%s" % (prop, fileCnt)] = info[prop] - change = info["change"] - if change > newestRevision: - newestRevision = change - fileCnt = fileCnt + 1 details["change"] = newestRevision gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") - commit(details) + try: + commit(details) + except: + print gitError.read() gitStream.close() gitOutput.close() -- cgit v1.2.3 From e3d37cf0980646b3dbc19e01684c665482a3129d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Feb 2007 23:09:49 +0100 Subject: Fixed incremental imports by using the correct "from" command instead of "merge" with git fast-import. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 06de0eae6d..3e573cd786 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -88,7 +88,7 @@ def commit(details): gitStream.write("EOT\n\n") if len(initialParent) > 0: - gitStream.write("merge %s\n" % initialParent) + gitStream.write("from %s\n" % initialParent) initialParent = "" fnum = 0 -- cgit v1.2.3 From 1cd573866a76d62866b65c458e723983ba58117e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Feb 2007 23:25:56 +0100 Subject: Make incremental imports easier to use by storing the p4 depot path after an import in .git/config and re-using it when we're invoked again later. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 3e573cd786..be4fd36c36 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -12,7 +12,14 @@ import os, string, sys, time import marshal, popen2 -if len(sys.argv) != 2: +branch = "refs/heads/p4" +prefix = os.popen("git-repo-config --get p4.depotpath").read() +if len(prefix) != 0: + prefix = prefix[:-1] + +if len(sys.argv) == 1 and len(prefix) != 0: + print "[using previously specified depot path %s]" % prefix +elif len(sys.argv) != 2: print "usage: %s //depot/path[@revRange]" % sys.argv[0] print "\n example:" print " %s //depot/my/project/ -- to import everything" @@ -21,9 +28,12 @@ if len(sys.argv) != 2: print " (a ... is not needed in the path p4 specification, it's added implicitly)" print "" sys.exit(1) +else: + if len(prefix) != 0 and prefix != sys.argv[1]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (prefix, sys.argv[1]) + sys.exit(1) + prefix = sys.argv[1] -branch = "refs/heads/p4" -prefix = sys.argv[1] changeRange = "" revision = "" users = {} @@ -222,3 +232,6 @@ else: gitError.close() print "" + +os.popen("git-repo-config p4.depotpath %s" % prefix).read() + -- cgit v1.2.3 From 23efd2545b292208740b0f2fb7446d99ce744000 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Feb 2007 23:37:54 +0100 Subject: Make specifying the revision ranges more convenient. Added support for @all as revision range specifier to import all changes to a given depot path. Also default to an import of #head if no revrange is specified. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index be4fd36c36..16e3d8d42a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -13,7 +13,7 @@ import os, string, sys, time import marshal, popen2 branch = "refs/heads/p4" -prefix = os.popen("git-repo-config --get p4.depotpath").read() +prefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() if len(prefix) != 0: prefix = prefix[:-1] @@ -22,7 +22,8 @@ if len(sys.argv) == 1 and len(prefix) != 0: elif len(sys.argv) != 2: print "usage: %s //depot/path[@revRange]" % sys.argv[0] print "\n example:" - print " %s //depot/my/project/ -- to import everything" + print " %s //depot/my/project/ -- to import the current head" + print " %s //depot/my/project/@all -- to import everything" print " %s //depot/my/project/@1,6 -- to import only from revision 1 to 6" print "" print " (a ... is not needed in the path p4 specification, it's added implicitly)" @@ -42,7 +43,9 @@ initialParent = "" if prefix.find("@") != -1: atIdx = prefix.index("@") changeRange = prefix[atIdx:] - if changeRange.find(",") == -1: + if changeRange == "@all": + changeRange = "" + elif changeRange.find(",") == -1: revision = changeRange changeRange = "" prefix = prefix[0:atIdx] @@ -50,6 +53,8 @@ elif prefix.find("#") != -1: hashIdx = prefix.index("#") revision = prefix[hashIdx:] prefix = prefix[0:hashIdx] +elif len(previousDepotPath) == 0: + revision = "#head" if prefix.endswith("..."): prefix = prefix[:-3] -- cgit v1.2.3 From 1e30c07dfcd0ca74ff4aec2004b5722e69b5a639 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Feb 2007 23:51:51 +0100 Subject: Fix calculation of the newest imported revision for #head imports. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 16e3d8d42a..36381fbecd 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -183,7 +183,7 @@ if len(revision) > 0: fileCnt = 0 for info in p4CmdList("files %s...%s" % (prefix, revision)): - change = info["change"] + change = int(info["change"]) if change > newestRevision: newestRevision = change -- cgit v1.2.3 From 7315866824607e0a7da807dbee84c60cfe5c5719 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Feb 2007 15:45:16 +0100 Subject: Catch io exceptions from git fast-import again and print the error message. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 36381fbecd..9f4b16a974 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -230,7 +230,11 @@ else: sys.stdout.flush() cnt = cnt + 1 - commit(description) + try: + commit(description) + except: + print gitError.read() + sys.exit(1) gitStream.close() gitOutput.close() @@ -240,3 +244,4 @@ print "" os.popen("git-repo-config p4.depotpath %s" % prefix).read() +sys.exit(0) -- cgit v1.2.3 From c9c527d7b60f406e143c1f7947a8c6c5ea9d9a8a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Feb 2007 15:53:11 +0100 Subject: Made the name of the git branch used for the perforce import configurable through a new --branch= commandline option. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 9f4b16a974..c44292473a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -10,16 +10,26 @@ # - support p4 submit (hah!) # import os, string, sys, time -import marshal, popen2 +import marshal, popen2, getopt branch = "refs/heads/p4" prefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() if len(prefix) != 0: prefix = prefix[:-1] -if len(sys.argv) == 1 and len(prefix) != 0: +try: + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=" ]) +except getopt.GetoptError: + print "fixme, syntax error" + sys.exit(1) + +for o, a in opts: + if o == "--branch": + branch = "refs/heads/" + a + +if len(args) == 0 and len(prefix) != 0: print "[using previously specified depot path %s]" % prefix -elif len(sys.argv) != 2: +elif len(args) != 1: print "usage: %s //depot/path[@revRange]" % sys.argv[0] print "\n example:" print " %s //depot/my/project/ -- to import the current head" @@ -30,10 +40,10 @@ elif len(sys.argv) != 2: print "" sys.exit(1) else: - if len(prefix) != 0 and prefix != sys.argv[1]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (prefix, sys.argv[1]) + if len(prefix) != 0 and prefix != args[0]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (prefix, args[0]) sys.exit(1) - prefix = sys.argv[1] + prefix = args[0] changeRange = "" revision = "" -- cgit v1.2.3 From 590dd4bfd25b3c7592004258384f7541d9315712 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 8 Feb 2007 15:26:01 -0500 Subject: tar archive frontend for fast-import. This is an example fast-import frontend, in less than 100 lines of Perl. It accepts one or more tar archives on the command line, passes them through gzcat/bzcat/zcat if necessary, parses out the individual file headers and feeds all contained data to fast-import. No temporary files are involved. Each tar is treated as one commit, with the commit timestamp coming from the oldest file modification date found within the tar. Each tar is also tagged with an annotated tag, using the basename of the tar file as the name of the tag. Currently symbolic links and hard links are not handled by the importer. The file checksums are also not verified. Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 105 +++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100755 contrib/fast-import/import-tars.perl (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl new file mode 100755 index 0000000000..26c42c9780 --- /dev/null +++ b/contrib/fast-import/import-tars.perl @@ -0,0 +1,105 @@ +#!/usr/bin/perl + +## tar archive frontend for git-fast-import +## +## For example: +## +## mkdir project; cd project; git init +## perl import-tars.perl *.tar.bz2 +## git whatchanged import-tars +## + +use strict; +die "usage: import-tars *.tar.{gz,bz2,Z}\n" unless @ARGV; + +my $branch_name = 'import-tars'; +my $branch_ref = "refs/heads/$branch_name"; +my $committer_name = 'T Ar Creator'; +my $committer_email = 'tar@example.com'; + +open(FI, '|-', 'git', 'fast-import', '--quiet') + or die "Unable to start git fast-import: $!\n"; +foreach my $tar_file (@ARGV) +{ + $tar_file =~ m,([^/]+)$,; + my $tar_name = $1; + + if ($tar_name =~ s/\.(tar\.gz|tgz)$//) { + open(I, '-|', 'gzcat', $tar_file) or die "Unable to gzcat $tar_file: $!\n"; + } elsif ($tar_name =~ s/\.(tar\.bz2|tbz2)$//) { + open(I, '-|', 'bzcat', $tar_file) or die "Unable to bzcat $tar_file: $!\n"; + } elsif ($tar_name =~ s/\.tar\.Z$//) { + open(I, '-|', 'zcat', $tar_file) or die "Unable to zcat $tar_file: $!\n"; + } elsif ($tar_name =~ s/\.tar$//) { + open(I, $tar_file) or die "Unable to open $tar_file: $!\n"; + } else { + die "Unrecognized compression format: $tar_file\n"; + } + + my $commit_time = 0; + my $next_mark = 1; + my $have_top_dir = 1; + my ($top_dir, %files); + + while (read(I, $_, 512) == 512) { + my ($name, $mode, $uid, $gid, $size, $mtime, + $chksum, $typeflag, $linkname, $magic, + $version, $uname, $gname, $devmajor, $devminor, + $prefix) = unpack 'Z100 Z8 Z8 Z8 Z12 Z12 + Z8 Z1 Z100 Z6 + Z2 Z32 Z32 Z8 Z8 Z*', $_; + last unless $name; + $mode = oct $mode; + $size = oct $size; + $mtime = oct $mtime; + next if $mode & 0040000; + + print FI "blob\n", "mark :$next_mark\n", "data $size\n"; + while ($size > 0 && read(I, $_, 512) == 512) { + print FI substr($_, 0, $size); + $size -= 512; + } + print FI "\n"; + + my $path = "$prefix$name"; + $files{$path} = [$next_mark++, $mode]; + + $commit_time = $mtime if $mtime > $commit_time; + $path =~ m,^([^/]+)/,; + $top_dir = $1 unless $top_dir; + $have_top_dir = 0 if $top_dir ne $1; + } + + print FI < $commit_time +0000 +data < $commit_time +0000 +data < Date: Thu, 8 Feb 2007 23:00:19 +0100 Subject: Added a little helper script to debug the output of the p4 python interface. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-debug.p4 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100755 contrib/fast-import/p4-debug.p4 (limited to 'contrib') diff --git a/contrib/fast-import/p4-debug.p4 b/contrib/fast-import/p4-debug.p4 new file mode 100755 index 0000000000..8fb159fd6a --- /dev/null +++ b/contrib/fast-import/p4-debug.p4 @@ -0,0 +1,25 @@ +#!/usr/bin/python +# +# p4-debug.py +# +# Author: Simon Hausmann +# License: MIT +# +# executes a p4 command with -G and prints the resulting python dicts +# +import os, string, sys +import marshal, popen2 + +cmd = "" +for arg in sys.argv[1:]: + cmd += arg + " " + +pipe = os.popen("p4 -G %s" % cmd, "rb") +try: + while True: + entry = marshal.load(pipe) + print entry +except EOFError: + pass +pipe.close() + -- cgit v1.2.3 From b52ba1a5d6c18250f497d3aba03943442766d9e1 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 9 Feb 2007 09:19:28 +0100 Subject: git-blame: Add Emacs Lisp file headers and GNU GPL boilerplate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Emacs Lisp file headers, according to "Coding Conventions" chapter in Emacs Lisp Reference Manual and Elisp Area Convetions for EmacsWiki: http://www.emacswiki.org/cgi-bin/wiki/ElispAreaConventions Those include: copyright notice, GNU GPL boilerplate, description and instalation instructions as provided in email and in commit message introducing git-blame.el, compatibility notes from another email by David Kågedal about what to change to use it in GNU Emacs 20, and "git-blame ends here" to detect if file was truncated. First line includes setting file encoding via first line local variable values (file variables). Added comment to "(require 'cl)" to note why it is needed; "Coding Conventions" advises to avoid require the `cl' package of Common Lisp extensions at run time. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 75 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index 62cf24c996..ba9d8a6cde 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -1,8 +1,73 @@ -;;; git-blame.el -;; David Kågedal +;;; git-blame.el --- Minor mode for incremental blame for Git -*- coding: utf-8 -*- +;; +;; Copyright (C) 2007 David Kågedal +;; +;; Authors: David Kågedal +;; Created: 31 Jan 2007 ;; Message-ID: <87iren2vqx.fsf@morpheus.local> +;; License: GPL +;; Keywords: git, version control, release management +;; +;; Compatibility: Emacs21 + + +;; This file is *NOT* part of GNU Emacs. +;; This file is distributed under the same terms as GNU Emacs. + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation; either version 2 of +;; the License, or (at your option) any later version. + +;; This program is distributed in the hope that it will be +;; useful, but WITHOUT ANY WARRANTY; without even the implied +;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +;; PURPOSE. See the GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public +;; License along with this program; if not, write to the Free +;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +;; MA 02111-1307 USA + +;; http://www.fsf.org/copyleft/gpl.html + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Commentary: +;; +;; Here is an Emacs implementation of incremental git-blame. When you +;; turn it on while viewing a file, the editor buffer will be updated by +;; setting the background of individual lines to a color that reflects +;; which commit it comes from. And when you move around the buffer, a +;; one-line summary will be shown in the echo area. + +;;; Installation: +;; +;; 1) Load into emacs: M-x load-file RET git-blame.el RET +;; 2) Open a git-controlled file +;; 3) Blame: M-x git-blame-mode + +;;; Compatibility: +;; +;; It requires GNU Emacs 21. If you'are using Emacs 20, try +;; changing this: +;; +;; (overlay-put ovl 'face (list :background +;; (cdr (assq 'color (cddddr info))))) +;; +;; to +;; +;; (overlay-put ovl 'face (cons 'background-color +;; (cdr (assq 'color (cddddr info))))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;; Code: + +(require 'cl) ; to use `push', `pop' -(require 'cl) (defun color-scale (l) (let* ((colors ()) r g b) @@ -178,3 +243,7 @@ (shell-command (format "git log -1 --pretty=oneline %s" (or hash (git-blame-current-commit))))) + +(provide 'git-blame) + +;;; git-blame.el ends here -- cgit v1.2.3 From f6f125fbaa79c097f10afdbd7103ec068df10397 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Fri, 9 Feb 2007 09:19:59 +0100 Subject: git-blame: Change installation instructions Change installation instructions to using either "(require 'git-blame)" or appropriate autoload instruction in GNU Emacs init file, .emacs This required adding "(provide 'git-blame)" at the end of git-blame.el and adding [preliminary] docstring to `git-blame-mode' function for consistency (to mark function as interactive in `autoload' we have to provide docstring as DOCSTRING is third arg, and INTERACTIVE fourth, and both are optional). `git-blame-mode' is marked to autoload. While at it ensure that we add `git-blame-mode' to `minor-mode-alist' only once (in a way that does not depend on `cl' package). Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index ba9d8a6cde..56a548b4a8 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -44,9 +44,20 @@ ;;; Installation: ;; -;; 1) Load into emacs: M-x load-file RET git-blame.el RET -;; 2) Open a git-controlled file -;; 3) Blame: M-x git-blame-mode +;; To use this package, put it somewhere in `load-path' (or add +;; directory with git-blame.el to `load-path'), and add the following +;; line to your .emacs: +;; +;; (require 'git-blame) +;; +;; If you do not want to load this package before it is necessary, you +;; can make use of the `autoload' feature, e.g. by adding to your .emacs +;; the following lines +;; +;; (autoload 'git-blame-mode "git-blame" +;; "Minor mode for incremental blame for Git." t) +;; +;; Then first use of `M-x git-blame-mode' would load the package. ;;; Compatibility: ;; @@ -102,8 +113,12 @@ (defvar git-blame-mode nil) (make-variable-buffer-local 'git-blame-mode) -(push (list 'git-blame-mode " blame") minor-mode-alist) +(unless (assq 'git-blame-mode minor-mode-alist) + (setq minor-mode-alist + (cons (list 'git-blame-mode " blame") + minor-mode-alist))) +;;;###autoload (defun git-blame-mode (&optional arg) (interactive "P") (if arg -- cgit v1.2.3 From 9f85fb324dc21b595a77313936b39930437209e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Fri, 9 Feb 2007 09:20:38 +0100 Subject: Handle uncommitted changes and cache descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index 56a548b4a8..c99437e5e5 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -187,8 +187,10 @@ (res-line (string-to-number (match-string 3))) (num-lines (string-to-number (match-string 4)))) (setq git-blame-current - (git-blame-new-commit - hash src-line res-line num-lines))) + (if (string= hash "0000000000000000000000000000000000000000") + nil + (git-blame-new-commit + hash src-line res-line num-lines)))) (delete-region (point) (match-end 0)) t) ((looking-at "filename \\(.+\\)\n") @@ -220,6 +222,7 @@ (unless color (setq color git-blame-ancient-color)) (setq info (list hash src-line res-line num-lines + (git-describe-commit hash) (cons 'color color)))) (puthash hash info git-blame-cache)) (goto-line res-line) @@ -233,7 +236,8 @@ (overlay-put ovl 'git-blame info) (overlay-put ovl 'help-echo hash) (overlay-put ovl 'face (list :background - (cdr (assq 'color (cddddr info))))) + (cdr (assq 'color (nthcdr 5 info))))) + ;; the point-entered property doesn't seem to work in overlays ;;(overlay-put ovl 'point-entered ;; `(lambda (x y) (git-blame-identify ,hash))) (let ((modified (buffer-modified-p))) @@ -253,11 +257,21 @@ (car info) (error "No commit info")))) +(defun git-describe-commit (hash) + (with-temp-buffer + (call-process "git" nil t nil + "log" "-1" "--pretty=oneline" + hash) + (buffer-substring (point-min) (1- (point-max))))) + +(defvar git-blame-last-identification nil) +(make-variable-buffer-local 'git-blame-last-identification) (defun git-blame-identify (&optional hash) (interactive) - (shell-command - (format "git log -1 --pretty=oneline %s" (or hash - (git-blame-current-commit))))) + (let ((info (gethash (or hash (git-blame-current-commit)) git-blame-cache))) + (when (and info (not (eq info git-blame-last-identification))) + (message "%s" (nth 4 info)) + (setq git-blame-last-identification info)))) (provide 'git-blame) -- cgit v1.2.3 From fa882116006e0403bb1b74973bb5f3f34f1cf523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Fri, 9 Feb 2007 09:21:03 +0100 Subject: git-blame.el: improve color handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index c99437e5e5..7ffc153bda 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -95,16 +95,10 @@ colors)) (defvar git-blame-dark-colors - (color-scale '("00" "04" "08" "0c" - "10" "14" "18" "1c" - "20" "24" "28" "2c" - "30" "34" "38" "3c"))) + (color-scale '("0c" "04" "24" "1c" "2c" "34" "14" "3c"))) (defvar git-blame-light-colors - (color-scale '("c0" "c4" "c8" "cc" - "d0" "d4" "d8" "dc" - "e0" "e4" "e8" "ec" - "f0" "f4" "f8" "fc"))) + (color-scale '("c4" "d4" "cc" "dc" "f4" "e4" "fc" "ec"))) (defvar git-blame-ancient-color "dark green") @@ -127,13 +121,15 @@ (make-local-variable 'git-blame-overlays) (make-local-variable 'git-blame-colors) (make-local-variable 'git-blame-cache) - (let ((bgmode (cdr (assoc 'background-mode (frame-parameters))))) - (if (eq bgmode 'dark) - (setq git-blame-colors git-blame-dark-colors) - (setq git-blame-colors git-blame-light-colors))) + (git-blame-cleanup) (if git-blame-mode - (git-blame-run) - (git-blame-cleanup))) + (progn + (let ((bgmode (cdr (assoc 'background-mode (frame-parameters))))) + (if (eq bgmode 'dark) + (setq git-blame-colors git-blame-dark-colors) + (setq git-blame-colors git-blame-light-colors))) + (setq git-blame-cache (make-hash-table :test 'equal)) + (git-blame-run)))) (defun git-blame-run () (let* ((display-buf (current-buffer)) -- cgit v1.2.3 From f0f39bb4c4ba36a07d7a6f034ba12f2ca0522db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Fri, 9 Feb 2007 09:21:32 +0100 Subject: git-blame.el: blame unsaved changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make git-blame use the current buffer contents for the blame, instead of the saved file. This makes the blame correct even if there are unsaved changes. Also added a git-reblame command. Signed-off-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 65 +++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 21 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index 7ffc153bda..f1839647bc 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -102,8 +102,17 @@ (defvar git-blame-ancient-color "dark green") -(defvar git-blame-overlays nil) -(defvar git-blame-cache nil) +(defvar git-blame-proc nil + "The running git-blame process") +(make-variable-buffer-local 'git-blame-proc) + +(defvar git-blame-overlays nil + "The git-blame overlays used in the current buffer.") +(make-variable-buffer-local 'git-blame-overlays) + +(defvar git-blame-cache nil + "A cache of git-blame information for the current buffer") +(make-variable-buffer-local 'git-blame-cache) (defvar git-blame-mode nil) (make-variable-buffer-local 'git-blame-mode) @@ -118,9 +127,7 @@ (if arg (setq git-blame-mode (eq arg 1)) (setq git-blame-mode (not git-blame-mode))) - (make-local-variable 'git-blame-overlays) (make-local-variable 'git-blame-colors) - (make-local-variable 'git-blame-cache) (git-blame-cleanup) (if git-blame-mode (progn @@ -131,24 +138,37 @@ (setq git-blame-cache (make-hash-table :test 'equal)) (git-blame-run)))) +;;;###autoload +(defun git-reblame () + "Recalculate all blame information in the current buffer" + (unless git-blame-mode + (error "git-blame is not active")) + (interactive) + (git-blame-cleanup) + (git-blame-run)) + (defun git-blame-run () - (let* ((display-buf (current-buffer)) - (blame-buf (get-buffer-create - (concat " git blame for " (buffer-name)))) - (proc (start-process "git-blame" blame-buf - "git" "blame" "--incremental" - (file-name-nondirectory buffer-file-name)))) - (mapcar 'delete-overlay git-blame-overlays) - (setq git-blame-overlays nil) - (setq git-blame-cache (make-hash-table :test 'equal)) - (with-current-buffer blame-buf - (erase-buffer) - (make-local-variable 'git-blame-file) - (make-local-variable 'git-blame-current) - (setq git-blame-file display-buf) - (setq git-blame-current nil)) - (set-process-filter proc 'git-blame-filter) - (set-process-sentinel proc 'git-blame-sentinel))) + (if git-blame-proc + ;; Should maybe queue up a new run here + (message "Already running git blame") + (let ((display-buf (current-buffer)) + (blame-buf (get-buffer-create + (concat " git blame for " (buffer-name))))) + (setq git-blame-proc + (start-process "git-blame" blame-buf + "git" "blame" + "--incremental" "--contents" "-" + (file-name-nondirectory buffer-file-name))) + (with-current-buffer blame-buf + (erase-buffer) + (make-local-variable 'git-blame-file) + (make-local-variable 'git-blame-current) + (setq git-blame-file display-buf) + (setq git-blame-current nil)) + (set-process-filter git-blame-proc 'git-blame-filter) + (set-process-sentinel git-blame-proc 'git-blame-sentinel) + (process-send-region git-blame-proc (point-min) (point-max)) + (process-send-eof git-blame-proc)))) (defun git-blame-cleanup () "Remove all blame properties" @@ -159,6 +179,9 @@ (set-buffer-modified-p modified))) (defun git-blame-sentinel (proc status) + (with-current-buffer (process-buffer proc) + (with-current-buffer git-blame-file + (setq git-blame-proc nil))) ;;(kill-buffer (process-buffer proc)) (message "git blame finished")) -- cgit v1.2.3 From 96df551cb89264ed88b717b9ace8ad3edc0e09a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Fri, 9 Feb 2007 09:21:51 +0100 Subject: git-blame.el: Doc fixes and cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index f1839647bc..7daa4e6993 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -123,6 +123,7 @@ ;;;###autoload (defun git-blame-mode (&optional arg) + "Minor mode for displaying Git blame" (interactive "P") (if arg (setq git-blame-mode (eq arg 1)) @@ -170,20 +171,25 @@ (process-send-region git-blame-proc (point-min) (point-max)) (process-send-eof git-blame-proc)))) +(defun remove-git-blame-text-properties (start end) + (let ((modified (buffer-modified-p)) + (inhibit-read-only t)) + (remove-text-properties start end '(point-entered nil)) + (set-buffer-modified-p modified))) + (defun git-blame-cleanup () "Remove all blame properties" (mapcar 'delete-overlay git-blame-overlays) (setq git-blame-overlays nil) - (let ((modified (buffer-modified-p))) - (remove-text-properties (point-min) (point-max) '(point-entered nil)) - (set-buffer-modified-p modified))) + (remove-git-blame-text-properties (point-min) (point-max))) (defun git-blame-sentinel (proc status) (with-current-buffer (process-buffer proc) (with-current-buffer git-blame-file (setq git-blame-proc nil))) ;;(kill-buffer (process-buffer proc)) - (message "git blame finished")) + ;;(message "git blame finished") + ) (defvar in-blame-filter nil) -- cgit v1.2.3 From d2589855df9141ed5885ebd2fe868619d53fa15e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Fri, 9 Feb 2007 09:22:19 +0100 Subject: git-blame.el: Autoupdate while editing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds the support for automatically updating the buffer while editing. A configuration variable git-blame-autoupdate controls whether this should be enabled or not. Signed-off-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 95 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index 7daa4e6993..64ad50b327 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -102,6 +102,9 @@ (defvar git-blame-ancient-color "dark green") +(defvar git-blame-autoupdate t + "*Automatically update the blame display while editing") + (defvar git-blame-proc nil "The running git-blame process") (make-variable-buffer-local 'git-blame-proc) @@ -114,6 +117,14 @@ "A cache of git-blame information for the current buffer") (make-variable-buffer-local 'git-blame-cache) +(defvar git-blame-idle-timer nil + "An idle timer that updates the blame") +(make-variable-buffer-local 'git-blame-cache) + +(defvar git-blame-update-queue nil + "A queue of update requests") +(make-variable-buffer-local 'git-blame-update-queue) + (defvar git-blame-mode nil) (make-variable-buffer-local 'git-blame-mode) (unless (assq 'git-blame-mode minor-mode-alist) @@ -129,6 +140,9 @@ (setq git-blame-mode (eq arg 1)) (setq git-blame-mode (not git-blame-mode))) (make-local-variable 'git-blame-colors) + (if git-blame-autoupdate + (add-hook 'after-change-functions 'git-blame-after-change nil t) + (remove-hook 'after-change-functions 'git-blame-after-change t)) (git-blame-cleanup) (if git-blame-mode (progn @@ -137,7 +151,8 @@ (setq git-blame-colors git-blame-dark-colors) (setq git-blame-colors git-blame-light-colors))) (setq git-blame-cache (make-hash-table :test 'equal)) - (git-blame-run)))) + (git-blame-run)) + (cancel-timer git-blame-idle-timer))) ;;;###autoload (defun git-reblame () @@ -148,18 +163,24 @@ (git-blame-cleanup) (git-blame-run)) -(defun git-blame-run () +(defun git-blame-run (&optional startline endline) (if git-blame-proc ;; Should maybe queue up a new run here (message "Already running git blame") (let ((display-buf (current-buffer)) (blame-buf (get-buffer-create - (concat " git blame for " (buffer-name))))) + (concat " git blame for " (buffer-name)))) + (args '("--incremental" "--contents" "-"))) + (if startline + (setq args (append args + (list "-L" (format "%d,%d" startline endline))))) + (setq args (append args + (list (file-name-nondirectory buffer-file-name)))) (setq git-blame-proc - (start-process "git-blame" blame-buf - "git" "blame" - "--incremental" "--contents" "-" - (file-name-nondirectory buffer-file-name))) + (apply 'start-process + "git-blame" blame-buf + "git" "blame" + args)) (with-current-buffer blame-buf (erase-buffer) (make-local-variable 'git-blame-file) @@ -183,10 +204,28 @@ (setq git-blame-overlays nil) (remove-git-blame-text-properties (point-min) (point-max))) +(defun git-blame-update-region (start end) + "Rerun blame to get updates between START and END" + (let ((overlays (overlays-in start end))) + (while overlays + (let ((overlay (pop overlays))) + (if (< (overlay-start overlay) start) + (setq start (overlay-start overlay))) + (if (> (overlay-end overlay) end) + (setq end (overlay-end overlay))) + (setq git-blame-overlays (delete overlay git-blame-overlays)) + (delete-overlay overlay)))) + (remove-git-blame-text-properties start end) + ;; We can be sure that start and end are at line breaks + (git-blame-run (1+ (count-lines (point-min) start)) + (count-lines (point-min) end))) + (defun git-blame-sentinel (proc status) (with-current-buffer (process-buffer proc) (with-current-buffer git-blame-file - (setq git-blame-proc nil))) + (setq git-blame-proc nil) + (if git-blame-update-queue + (git-blame-delayed-update)))) ;;(kill-buffer (process-buffer proc)) ;;(message "git blame finished") ) @@ -241,7 +280,8 @@ (save-excursion (set-buffer git-blame-file) (let ((info (gethash hash git-blame-cache)) - (inhibit-point-motion-hooks t)) + (inhibit-point-motion-hooks t) + (inhibit-modification-hooks t)) (when (not info) (let ((color (pop git-blame-colors))) (unless color @@ -298,6 +338,43 @@ (message "%s" (nth 4 info)) (setq git-blame-last-identification info)))) +;; (defun git-blame-after-save () +;; (when git-blame-mode +;; (git-blame-cleanup) +;; (git-blame-run))) +;; (add-hook 'after-save-hook 'git-blame-after-save) + +(defun git-blame-after-change (start end length) + (when git-blame-mode + (git-blame-enq-update start end))) + +(defvar git-blame-last-update nil) +(make-variable-buffer-local 'git-blame-last-update) +(defun git-blame-enq-update (start end) + "Mark the region between START and END as needing blame update" + ;; Try to be smart and avoid multiple callouts for sequential + ;; editing + (cond ((and git-blame-last-update + (= start (cdr git-blame-last-update))) + (setcdr git-blame-last-update end)) + ((and git-blame-last-update + (= end (car git-blame-last-update))) + (setcar git-blame-last-update start)) + (t + (setq git-blame-last-update (cons start end)) + (setq git-blame-update-queue (nconc git-blame-update-queue + (list git-blame-last-update))))) + (unless (or git-blame-proc git-blame-idle-timer) + (setq git-blame-idle-timer + (run-with-idle-timer 0.5 nil 'git-blame-delayed-update)))) + +(defun git-blame-delayed-update () + (setq git-blame-idle-timer nil) + (if git-blame-update-queue + (let ((first (pop git-blame-update-queue)) + (inhibit-point-motion-hooks t)) + (git-blame-update-region (car first) (cdr first))))) + (provide 'git-blame) ;;; git-blame.el ends here -- cgit v1.2.3 From ad34a028c126fde97359c9d67e8d634043670a21 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 9 Feb 2007 12:18:28 -0500 Subject: remove mailmap.linux This file is incomplete, unmaintained, and it doesn't belong in the GIT package anyway. A more complete version is already included in the Linux -mm tree and is about to make its way into mainline RSN. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- contrib/mailmap.linux | 42 ------------------------------------------ 1 file changed, 42 deletions(-) delete mode 100644 contrib/mailmap.linux (limited to 'contrib') diff --git a/contrib/mailmap.linux b/contrib/mailmap.linux deleted file mode 100644 index e4907f80f1..0000000000 --- a/contrib/mailmap.linux +++ /dev/null @@ -1,42 +0,0 @@ -# -# Even with git, we don't always have name translations. -# So have an email->real name table to translate the -# (hopefully few) missing names -# -# repo-abbrev: /pub/scm/linux/kernel/git/ -# -Adrian Bunk -Andreas Herrmann -Andrew Morton -Andrew Vasquez -Christoph Hellwig -Corey Minyard -David Woodhouse -Domen Puncer -Douglas Gilbert -Ed L Cashin -Evgeniy Polyakov -Felix Moeller -Frank Zago -Greg Kroah-Hartman -James Bottomley -James Bottomley -Jeff Garzik -Jens Axboe -Kay Sievers -Mitesh shah -Morten Welinder -Morten Welinder -Morten Welinder -Morten Welinder -Nguyen Anh Quynh -Paolo 'Blaisorblade' Giarrusso -Peter A Jonsson -Ralf Wildenhues -Rudolf Marek -Rui Saraiva -Sachin P Sant -Santtu Hyrkk,Av(B -Simon Kelley -Tejun Heo -Tony Luck -- cgit v1.2.3 From b41507a42769c9e40d024593c6924769d9cb961b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Feb 2007 09:25:22 +0100 Subject: Minor code cleanups. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index c44292473a..d1faa7c290 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -183,6 +183,8 @@ tzsign = ("%s" % tz)[0] if tzsign != '+' and tzsign != '-': tz = "+" + ("%s" % tz) +gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") + if len(revision) > 0: print "Doing initial import of %s from revision %s" % (prefix, revision) @@ -207,15 +209,11 @@ if len(revision) > 0: details["change"] = newestRevision - gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") try: commit(details) except: print gitError.read() - gitStream.close() - gitOutput.close() - gitError.close() else: output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() @@ -230,8 +228,6 @@ else: print "no changes to import!" sys.exit(1) - gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") - cnt = 1 for change in changes: description = p4Cmd("describe %s" % change) @@ -246,12 +242,12 @@ else: print gitError.read() sys.exit(1) - gitStream.close() - gitOutput.close() - gitError.close() - print "" +gitStream.close() +gitOutput.close() +gitError.close() + os.popen("git-repo-config p4.depotpath %s" % prefix).read() sys.exit(0) -- cgit v1.2.3 From 8718f3ec9a550c8c2419576a3765c20189029c99 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Feb 2007 10:05:29 +0100 Subject: Avoid the excessive use of git tags for every perforce change and instead just create one git tag for the last imported change. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index d1faa7c290..907a56dc8a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -49,6 +49,9 @@ changeRange = "" revision = "" users = {} initialParent = "" +lastChange = "" +lastCommitter = "" +initialTag = "" if prefix.find("@") != -1: atIdx = prefix.index("@") @@ -94,6 +97,8 @@ def p4Cmd(cmd): def commit(details): global initialParent global users + global lastChange + global lastCommitter epoch = details["time"] author = details["user"] @@ -147,11 +152,8 @@ def commit(details): gitStream.write("\n") - gitStream.write("tag p4/%s\n" % details["change"]) - gitStream.write("from %s\n" % branch); - gitStream.write("tagger %s\n" % committer); - gitStream.write("data 0\n\n") - + lastChange = details["change"] + lastCommitter = committer def getUserMap(): users = {} @@ -173,6 +175,7 @@ if len(changeRange) == 0: rev = int(output[tagIdx + 9 : caretIdx]) + 1 changeRange = "@%s,#head" % rev initialParent = os.popen("git-rev-parse %s" % branch).read()[:-1] + initialTag = "p4/%s" % (int(rev) - 1) except: pass @@ -244,10 +247,17 @@ else: print "" +gitStream.write("tag p4/%s\n" % lastChange) +gitStream.write("from %s\n" % branch); +gitStream.write("tagger %s\n" % lastCommitter); +gitStream.write("data 0\n\n") + gitStream.close() gitOutput.close() gitError.close() os.popen("git-repo-config p4.depotpath %s" % prefix).read() +if len(initialTag) > 0: + os.popen("git tag -d %s" % initialTag).read() sys.exit(0) -- cgit v1.2.3 From fe2193183add84d185c0691166fde4460a333412 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Feb 2007 10:05:51 +0100 Subject: Changed the default git import branch from "p4" to "master". Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 907a56dc8a..1f19cbc560 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -12,7 +12,7 @@ import os, string, sys, time import marshal, popen2, getopt -branch = "refs/heads/p4" +branch = "refs/heads/master" prefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() if len(prefix) != 0: prefix = prefix[:-1] -- cgit v1.2.3 From f7d63b0c99945f4b358df06e4d09837f66db6a88 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Feb 2007 10:26:03 +0100 Subject: Added a little helper script to remove unused tags from the perforce import. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-clean-tags.py | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100755 contrib/fast-import/p4-clean-tags.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-clean-tags.py b/contrib/fast-import/p4-clean-tags.py new file mode 100755 index 0000000000..0be51f6405 --- /dev/null +++ b/contrib/fast-import/p4-clean-tags.py @@ -0,0 +1,40 @@ +#!/usr/bin/python +# +# p4-debug.py +# +# Author: Simon Hausmann +# License: MIT +# +# removes unused p4 import tags +# +import os, string, sys +import popen2, getopt + +branch = "refs/heads/master" + +try: + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=" ]) +except getopt.GetoptError: + print "fixme, syntax error" + sys.exit(1) + +for o, a in opts: + if o == "--branch": + branch = "refs/heads/" + a + +sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) +output = sout.read() +tagIdx = output.index(" tags/p4/") +caretIdx = output.index("^") +rev = int(output[tagIdx + 9 : caretIdx]) + +allTags = os.popen("git tag -l p4/").readlines() +for i in range(len(allTags)): + allTags[i] = int(allTags[i][3:-1]) + +allTags.sort() + +allTags.remove(rev) + +for rev in allTags: + print os.popen("git tag -d p4/%s" % rev).read() -- cgit v1.2.3 From fc21f8a1dab090ceb63c479705104cbb95585a2f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 11 Feb 2007 18:04:39 +0100 Subject: Create lightweight git tags (using the "reset" trick) for the incremental import instead of full-blown ones. Also fix parsing the output of git name-rev for figuring out the last imported p4 change number. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 1f19cbc560..989513888a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -170,9 +170,14 @@ if len(changeRange) == 0: try: sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) output = sout.read() + if output.endswith("\n"): + output = output[:-1] tagIdx = output.index(" tags/p4/") - caretIdx = output.index("^") - rev = int(output[tagIdx + 9 : caretIdx]) + 1 + caretIdx = output.find("^") + endPos = len(output) + if caretIdx != -1: + endPos = caretIdx + rev = int(output[tagIdx + 9 : endPos]) + 1 changeRange = "@%s,#head" % rev initialParent = os.popen("git-rev-parse %s" % branch).read()[:-1] initialTag = "p4/%s" % (int(rev) - 1) @@ -247,10 +252,9 @@ else: print "" -gitStream.write("tag p4/%s\n" % lastChange) -gitStream.write("from %s\n" % branch); -gitStream.write("tagger %s\n" % lastCommitter); -gitStream.write("data 0\n\n") +gitStream.write("reset refs/tags/p4/%s\n" % lastChange) +gitStream.write("from %s\n\n" % branch); + gitStream.close() gitOutput.close() -- cgit v1.2.3 From 12d04ca7da3bcfec123fb56d3d143ccda729a7f4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 11 Feb 2007 21:35:34 +0100 Subject: Cleanups, remove unused variable. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 989513888a..61b9e67e54 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -50,7 +50,6 @@ revision = "" users = {} initialParent = "" lastChange = "" -lastCommitter = "" initialTag = "" if prefix.find("@") != -1: @@ -98,7 +97,6 @@ def commit(details): global initialParent global users global lastChange - global lastCommitter epoch = details["time"] author = details["user"] @@ -153,7 +151,6 @@ def commit(details): gitStream.write("\n") lastChange = details["change"] - lastCommitter = committer def getUserMap(): users = {} -- cgit v1.2.3 From c6ec3b13b81d59272e41d5316689c65dd4cc2e63 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 11 Feb 2007 19:55:22 -0500 Subject: bash: Hide git-fast-import. The new git-fast-import command is not intended to be invoked directly by an end user. So offering it as a possible completion for a subcommand is not very useful. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index eecdaa0e75..5d3d402051 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -270,6 +270,7 @@ __git_commands () cvsserver) : daemon;; daemon) : daemon;; diff-stages) : nobody uses it;; + fast-import) : import;; fsck-objects) : plumbing;; fetch-pack) : plumbing;; fmt-merge-msg) : plumbing;; -- cgit v1.2.3 From d63ea115948a892f94451677c322c5a6f233adc6 Mon Sep 17 00:00:00 2001 From: Michael Loeffler Date: Mon, 12 Feb 2007 15:17:11 +0100 Subject: import-tars: brown paper bag fix for file mode. There is a bug with this $git_mode variable which should be 0644 or 0755, but nothing else I think. Signed-off-by: Michael Loeffler Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 26c42c9780..990c9e70b2 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -83,10 +83,8 @@ EOF foreach my $path (keys %files) { my ($mark, $mode) = @{$files{$path}}; - my $git_mode = 0644; - $git_mode |= 0700 if $mode & 0111; $path =~ s,^([^/]+)/,, if $have_top_dir; - printf FI "M %o :%i %s\n", $git_mode, $mark, $path; + printf FI "M %o :%i %s\n", $mode & 0111 ? 0755 : 0644, $mark, $path; } print FI "\n"; -- cgit v1.2.3 From 44b3add6519d6a85fb75b90173ecd6e7ec1ba650 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 20:28:58 +0100 Subject: Code cleanups. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 61b9e67e54..07d6e53852 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -93,7 +93,20 @@ def p4Cmd(cmd): result.update(entry) return result; -def commit(details): +def extractFilesFromCommit(commit): + files = [] + fnum = 0 + while commit.has_key("depotFile%s" % fnum): + file = {} + file["path"] = commit["depotFile%s" % fnum] + file["rev"] = commit["rev%s" % fnum] + file["action"] = commit["action%s" % fnum] + file["type"] = commit["type%s" % fnum] + files.append(file) + fnum = fnum + 1 + return files + +def commit(details, files, branch): global initialParent global users global lastChange @@ -119,24 +132,22 @@ def commit(details): gitStream.write("from %s\n" % initialParent) initialParent = "" - fnum = 0 - while details.has_key("depotFile%s" % fnum): - path = details["depotFile%s" % fnum] + for file in files: + path = file["path"] if not path.startswith(prefix): print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) - fnum = fnum + 1 continue - rev = details["rev%s" % fnum] + rev = file["rev"] depotPath = path + "#" + rev relPath = path[len(prefix):] - action = details["action%s" % fnum] + action = file["action"] if action == "delete": gitStream.write("D %s\n" % relPath) else: mode = 644 - if details["type%s" % fnum].startswith("x"): + if file["type"].startswith("x"): mode = 755 data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -146,8 +157,6 @@ def commit(details): gitStream.write(data) gitStream.write("\n") - fnum = fnum + 1 - gitStream.write("\n") lastChange = details["change"] @@ -215,7 +224,7 @@ if len(revision) > 0: details["change"] = newestRevision try: - commit(details) + commit(details, extractFilesFromCommit(details), branch) except: print gitError.read() @@ -242,7 +251,7 @@ else: cnt = cnt + 1 try: - commit(description) + commit(description, extractFilesFromCommit(description), branch) except: print gitError.read() sys.exit(1) -- cgit v1.2.3 From 766887e110898d4ec6f08d19061db323a1ac2b27 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 21:04:59 +0100 Subject: Started work on p4 branch detection (experimental!). Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 59 +++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 07d6e53852..01bf5baf9a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -14,11 +14,12 @@ import marshal, popen2, getopt branch = "refs/heads/master" prefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() +detectBranches = False if len(prefix) != 0: prefix = prefix[:-1] try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -26,6 +27,8 @@ except getopt.GetoptError: for o, a in opts: if o == "--branch": branch = "refs/heads/" + a + elif o == "--detect-branches": + detectBranches = True if len(args) == 0 and len(prefix) != 0: print "[using previously specified depot path %s]" % prefix @@ -97,8 +100,13 @@ def extractFilesFromCommit(commit): files = [] fnum = 0 while commit.has_key("depotFile%s" % fnum): + path = commit["depotFile%s" % fnum] + if not path.startswith(prefix): + print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) + continue + file = {} - file["path"] = commit["depotFile%s" % fnum] + file["path"] = path file["rev"] = commit["rev%s" % fnum] file["action"] = commit["action%s" % fnum] file["type"] = commit["type%s" % fnum] @@ -106,7 +114,37 @@ def extractFilesFromCommit(commit): fnum = fnum + 1 return files -def commit(details, files, branch): +def branchesForCommit(files): + branches = set() + + for file in files: + relativePath = file["path"][len(prefix):] + # strip off the filename + relativePath = relativePath[0:relativePath.rfind("/")] + + if len(branches) == 0: + branches.add(relativePath) + continue + + ###### this needs more testing :) + knownBranch = False + for branch in branches: + if relativePath == branch: + knownBranch = True + break + if relativePath.startswith(branch): + knownBranch = True + break + if branch.startswith(relativePath): + branches.remove(branch) + break + + if not knownBranch: + branches.add(relativePath) + + return branches + +def commit(details, files, branch, prefix): global initialParent global users global lastChange @@ -134,10 +172,6 @@ def commit(details, files, branch): for file in files: path = file["path"] - if not path.startswith(prefix): - print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) - continue - rev = file["rev"] depotPath = path + "#" + rev relPath = path[len(prefix):] @@ -224,7 +258,7 @@ if len(revision) > 0: details["change"] = newestRevision try: - commit(details, extractFilesFromCommit(details), branch) + commit(details, extractFilesFromCommit(details), branch, prefix) except: print gitError.read() @@ -251,7 +285,14 @@ else: cnt = cnt + 1 try: - commit(description, extractFilesFromCommit(description), branch) + files = extractFilesFromCommit(description) + if detectBranches: + for branch in branchesForCommit(files): + branchPrefix = prefix + branch + "/" + branch = "refs/heads/" + branch + commit(description, files, branch, branchPrefix) + else: + commit(description, files, branch, prefix) except: print gitError.read() sys.exit(1) -- cgit v1.2.3 From dcacf8b447be74efaa5335f42c502ee1c397c436 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 21:41:45 +0100 Subject: More fixes in heuristic p4 branch detection based on common path components. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 108 +++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 34 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 01bf5baf9a..f9653f1344 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -12,11 +12,12 @@ import os, string, sys, time import marshal, popen2, getopt +knownBranches = set() branch = "refs/heads/master" -prefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() +globalPrefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() detectBranches = False -if len(prefix) != 0: - prefix = prefix[:-1] +if len(globalPrefix) != 0: + globalPrefix = globalPrefix[:-1] try: opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches" ]) @@ -30,8 +31,8 @@ for o, a in opts: elif o == "--detect-branches": detectBranches = True -if len(args) == 0 and len(prefix) != 0: - print "[using previously specified depot path %s]" % prefix +if len(args) == 0 and len(globalPrefix) != 0: + print "[using previously specified depot path %s]" % globalPrefix elif len(args) != 1: print "usage: %s //depot/path[@revRange]" % sys.argv[0] print "\n example:" @@ -43,10 +44,10 @@ elif len(args) != 1: print "" sys.exit(1) else: - if len(prefix) != 0 and prefix != args[0]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (prefix, args[0]) + if len(globalPrefix) != 0 and globalPrefix != args[0]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (globalPrefix, args[0]) sys.exit(1) - prefix = args[0] + globalPrefix = args[0] changeRange = "" revision = "" @@ -55,27 +56,27 @@ initialParent = "" lastChange = "" initialTag = "" -if prefix.find("@") != -1: - atIdx = prefix.index("@") - changeRange = prefix[atIdx:] +if globalPrefix.find("@") != -1: + atIdx = globalPrefix.index("@") + changeRange = globalPrefix[atIdx:] if changeRange == "@all": changeRange = "" elif changeRange.find(",") == -1: revision = changeRange changeRange = "" - prefix = prefix[0:atIdx] -elif prefix.find("#") != -1: - hashIdx = prefix.index("#") - revision = prefix[hashIdx:] - prefix = prefix[0:hashIdx] + globalPrefix = globalPrefix[0:atIdx] +elif globalPrefix.find("#") != -1: + hashIdx = globalPrefix.index("#") + revision = globalPrefix[hashIdx:] + globalPrefix = globalPrefix[0:hashIdx] elif len(previousDepotPath) == 0: revision = "#head" -if prefix.endswith("..."): - prefix = prefix[:-3] +if globalPrefix.endswith("..."): + globalPrefix = globalPrefix[:-3] -if not prefix.endswith("/"): - prefix += "/" +if not globalPrefix.endswith("/"): + globalPrefix += "/" def p4CmdList(cmd): pipe = os.popen("p4 -G %s" % cmd, "rb") @@ -101,8 +102,8 @@ def extractFilesFromCommit(commit): fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] - if not path.startswith(prefix): - print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) + if not path.startswith(globalPrefix): + print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) continue file = {} @@ -118,7 +119,7 @@ def branchesForCommit(files): branches = set() for file in files: - relativePath = file["path"][len(prefix):] + relativePath = file["path"][len(globalPrefix):] # strip off the filename relativePath = relativePath[0:relativePath.rfind("/")] @@ -144,7 +145,7 @@ def branchesForCommit(files): return branches -def commit(details, files, branch, prefix): +def commit(details, files, branch, branchPrefix): global initialParent global users global lastChange @@ -163,7 +164,7 @@ def commit(details, files, branch, prefix): gitStream.write("data < 0: @@ -172,9 +173,47 @@ def commit(details, files, branch, prefix): for file in files: path = file["path"] + if not path.startswith(branchPrefix): + continue + action = file["action"] + if action != "integrate" and action != "branch": + continue + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + if not log["how0,0"].endswith(" from"): + print "eek! file %s was not branched but instead: %s" % (depotPath, log["how0,0"]) + sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + relPath = source[len(globalPrefix):] + + for branch in knownBranches: + if relPath.startswith(branch): + gitStream.write("merge refs/heads/%s\n" % branch) + break + + for file in files: + path = file["path"] + if not path.startswith(branchPrefix): + print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, change) + continue rev = file["rev"] depotPath = path + "#" + rev - relPath = path[len(prefix):] + relPath = path[len(branchPrefix):] action = file["action"] if action == "delete": @@ -234,15 +273,15 @@ if tzsign != '+' and tzsign != '-': gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") if len(revision) > 0: - print "Doing initial import of %s from revision %s" % (prefix, revision) + print "Doing initial import of %s from revision %s" % (globalPrefix, revision) details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = "Initial import of %s from the state at revision %s" % (prefix, revision) + details["desc"] = "Initial import of %s from the state at revision %s" % (globalPrefix, revision) details["change"] = revision newestRevision = 0 fileCnt = 0 - for info in p4CmdList("files %s...%s" % (prefix, revision)): + for info in p4CmdList("files %s...%s" % (globalPrefix, revision)): change = int(info["change"]) if change > newestRevision: newestRevision = change @@ -258,12 +297,12 @@ if len(revision) > 0: details["change"] = newestRevision try: - commit(details, extractFilesFromCommit(details), branch, prefix) + commit(details, extractFilesFromCommit(details), branch, globalPrefix) except: print gitError.read() else: - output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() + output = os.popen("p4 changes %s...%s" % (globalPrefix, changeRange)).readlines() changes = [] for line in output: @@ -288,11 +327,12 @@ else: files = extractFilesFromCommit(description) if detectBranches: for branch in branchesForCommit(files): - branchPrefix = prefix + branch + "/" + knownBranches.add(branch) + branchPrefix = globalPrefix + branch + "/" branch = "refs/heads/" + branch commit(description, files, branch, branchPrefix) else: - commit(description, files, branch, prefix) + commit(description, files, branch, globalPrefix) except: print gitError.read() sys.exit(1) @@ -307,7 +347,7 @@ gitStream.close() gitOutput.close() gitError.close() -os.popen("git-repo-config p4.depotpath %s" % prefix).read() +os.popen("git-repo-config p4.depotpath %s" % globalPrefix).read() if len(initialTag) > 0: os.popen("git tag -d %s" % initialTag).read() -- cgit v1.2.3 From 53b03239aa899677bcadf69f757354b72797e457 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 21:44:02 +0100 Subject: After marking a p4 branch as merged don't ever merge it in git again. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index f9653f1344..5838ca3c68 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -171,6 +171,8 @@ def commit(details, files, branch, branchPrefix): gitStream.write("from %s\n" % initialParent) initialParent = "" + mergedBranches = set() + for file in files: path = file["path"] if not path.startswith(branchPrefix): @@ -202,8 +204,9 @@ def commit(details, files, branch, branchPrefix): relPath = source[len(globalPrefix):] for branch in knownBranches: - if relPath.startswith(branch): + if relPath.startswith(branch) and branch not in mergedBranches: gitStream.write("merge refs/heads/%s\n" % branch) + mergedBranches.add(branch) break for file in files: -- cgit v1.2.3 From 77083daac72612ab8ded6cba82a72924f3a867b4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 21:56:46 +0100 Subject: Set git fast-import marks for every imported change for future use. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 5838ca3c68..488533b07e 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -154,6 +154,7 @@ def commit(details, files, branch, branchPrefix): author = details["user"] gitStream.write("commit %s\n" % branch) + gitStream.write("mark :%s\n" % details["change"]) committer = "" if author in users: committer = "%s %s %s" % (users[author], epoch, tz) -- cgit v1.2.3 From 930d3cc94e98ccecfa8762ef79f7c29af4a72769 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 22:05:21 +0100 Subject: When trying to map p4 integrations to git merges just record it as a single merge with the newest p4 change as secondary parent. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 488533b07e..0a3e5c9934 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -172,7 +172,8 @@ def commit(details, files, branch, branchPrefix): gitStream.write("from %s\n" % initialParent) initialParent = "" - mergedBranches = set() + #mergedBranches = set() + merge = 0 for file in files: path = file["path"] @@ -202,13 +203,28 @@ def commit(details, files, branch, branchPrefix): if source.startswith(branchPrefix): continue - relPath = source[len(globalPrefix):] + lastSourceRev = log["erev0,0"] - for branch in knownBranches: - if relPath.startswith(branch) and branch not in mergedBranches: - gitStream.write("merge refs/heads/%s\n" % branch) - mergedBranches.add(branch) - break + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + change = int(sourceLog["change0"]) + if change > merge: + merge = change + +# relPath = source[len(globalPrefix):] +# +# for branch in knownBranches: +# if relPath.startswith(branch) and branch not in mergedBranches: +# gitStream.write("merge refs/heads/%s\n" % branch) +# mergedBranches.add(branch) +# break + + if merge != 0: + gitStream.write("merge :%s\n" % merge) for file in files: path = file["path"] -- cgit v1.2.3 From 07fef5fc1599d11f5750ae2440e0da6616c297b8 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Mon, 12 Feb 2007 23:00:28 +0530 Subject: blameview: Move the commit info to a pane below the blame window. Also spawn the the new blameview in the background Signed-off-by: Aneesh Kumar K.V Signed-off-by: Junio C Hamano --- contrib/blameview/blameview.perl | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/blameview/blameview.perl b/contrib/blameview/blameview.perl index 807d01fe3d..a9a509febb 100755 --- a/contrib/blameview/blameview.perl +++ b/contrib/blameview/blameview.perl @@ -25,11 +25,12 @@ EOS my $window = Gtk2::Window->new('toplevel'); $window->signal_connect(destroy => sub { Gtk2->main_quit }); +my $vpan = Gtk2::VPaned->new(); +$window->add($vpan); my $scrolled_window = Gtk2::ScrolledWindow->new; -$window->add($scrolled_window); +$vpan->pack1($scrolled_window, 1, 1); my $fileview = Gtk2::SimpleList->new( 'Commit' => 'text', - 'CommitInfo' => 'text', 'FileLine' => 'text', 'Data' => 'text' ); @@ -40,8 +41,27 @@ $fileview->set_rules_hint(1); $fileview->signal_connect (row_activated => sub { my ($sl, $path, $column) = @_; my $row_ref = $sl->get_row_data_from_path ($path); - system("blameview @$row_ref[0] $fn"); - # $row_ref is now an array ref to the double-clicked row's data. + system("blameview @$row_ref[0] $fn &"); + }); + +my $commitwindow = Gtk2::ScrolledWindow->new(); +$commitwindow->set_policy ('GTK_POLICY_AUTOMATIC','GTK_POLICY_AUTOMATIC'); +$vpan->pack2($commitwindow, 1, 1); +my $commit_text = Gtk2::TextView->new(); +my $commit_buffer = Gtk2::TextBuffer->new(); +$commit_text->set_buffer($commit_buffer); +$commitwindow->add($commit_text); + +$fileview->signal_connect (cursor_changed => sub { + my ($sl) = @_; + my ($path, $focus_column) = $sl->get_cursor(); + my $row_ref = $sl->get_row_data_from_path ($path); + my $c_fh; + open($c_fh, '-|', "git cat-file commit @$row_ref[0]") + or die "unable to find commit @$row_ref[0]"; + my @buffer = <$c_fh>; + $commit_buffer->set_text("@buffer"); + close($c_fh); }); my $fh; @@ -50,7 +70,7 @@ open($fh, '-|', "git cat-file blob $hash:$fn") while(<$fh>) { chomp; - $fileview->{data}->[$.] = ['HEAD', '?', "$fn:$.", $_]; + $fileview->{data}->[$.] = ['HEAD', "$fn:$.", $_]; } my $blame; @@ -79,8 +99,7 @@ sub flush_blame_line { for(my $i = 0; $i < $cnt; $i++) { @{$fileview->{data}->[$lno+$i-1]}[0,1,2] = - (substr($commit, 0, 8), $info, - $filename . ':' . ($s_lno+$i)); + (substr($commit, 0, 8), $filename . ':' . ($s_lno+$i)); } } -- cgit v1.2.3 From 207dfa07919baba034291f1d5169f86272839d59 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 7 Feb 2007 10:37:03 -0800 Subject: Remove git-resolve. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 - contrib/examples/git-resolve.sh | 112 +++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100755 contrib/examples/git-resolve.sh (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 5d3d402051..865531a5fd 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -298,7 +298,6 @@ __git_commands () reflog) : plumbing;; repo-config) : plumbing;; rerere) : plumbing;; - resolve) : dead dont use;; rev-list) : plumbing;; rev-parse) : plumbing;; runstatus) : plumbing;; diff --git a/contrib/examples/git-resolve.sh b/contrib/examples/git-resolve.sh new file mode 100755 index 0000000000..36b90e3849 --- /dev/null +++ b/contrib/examples/git-resolve.sh @@ -0,0 +1,112 @@ +#!/bin/sh +# +# Copyright (c) 2005 Linus Torvalds +# +# Resolve two trees. +# + +echo 'WARNING: This command is DEPRECATED and will be removed very soon.' >&2 +echo 'WARNING: Please use git-merge or git-pull instead.' >&2 +sleep 2 + +USAGE=' ' +. git-sh-setup + +dropheads() { + rm -f -- "$GIT_DIR/MERGE_HEAD" \ + "$GIT_DIR/LAST_MERGE" || exit 1 +} + +head=$(git-rev-parse --verify "$1"^0) && +merge=$(git-rev-parse --verify "$2"^0) && +merge_name="$2" && +merge_msg="$3" || usage + +# +# The remote name is just used for the message, +# but we do want it. +# +if [ -z "$head" -o -z "$merge" -o -z "$merge_msg" ]; then + usage +fi + +dropheads +echo $head > "$GIT_DIR"/ORIG_HEAD +echo $merge > "$GIT_DIR"/LAST_MERGE + +common=$(git-merge-base $head $merge) +if [ -z "$common" ]; then + die "Unable to find common commit between" $merge $head +fi + +case "$common" in +"$merge") + echo "Already up-to-date. Yeeah!" + dropheads + exit 0 + ;; +"$head") + echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $merge)" + git-read-tree -u -m $head $merge || exit 1 + git-update-ref -m "resolve $merge_name: Fast forward" \ + HEAD "$merge" "$head" + git-diff-tree -p $head $merge | git-apply --stat + dropheads + exit 0 + ;; +esac + +# We are going to make a new commit. +git var GIT_COMMITTER_IDENT >/dev/null || exit + +# Find an optimum merge base if there are more than one candidates. +LF=' +' +common=$(git-merge-base -a $head $merge) +case "$common" in +?*"$LF"?*) + echo "Trying to find the optimum merge base." + G=.tmp-index$$ + best= + best_cnt=-1 + for c in $common + do + rm -f $G + GIT_INDEX_FILE=$G git-read-tree -m $c $head $merge \ + 2>/dev/null || continue + # Count the paths that are unmerged. + cnt=`GIT_INDEX_FILE=$G git-ls-files --unmerged | wc -l` + if test $best_cnt -le 0 -o $cnt -le $best_cnt + then + best=$c + best_cnt=$cnt + if test "$best_cnt" -eq 0 + then + # Cannot do any better than all trivial merge. + break + fi + fi + done + rm -f $G + common="$best" +esac + +echo "Trying to merge $merge into $head using $common." +git-update-index --refresh 2>/dev/null +git-read-tree -u -m $common $head $merge || exit 1 +result_tree=$(git-write-tree 2> /dev/null) +if [ $? -ne 0 ]; then + echo "Simple merge failed, trying Automatic merge" + git-merge-index -o git-merge-one-file -a + if [ $? -ne 0 ]; then + echo $merge > "$GIT_DIR"/MERGE_HEAD + die "Automatic merge failed, fix up by hand" + fi + result_tree=$(git-write-tree) || exit 1 +fi +result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree -p $head -p $merge) +echo "Committed merge $result_commit" +git-update-ref -m "resolve $merge_name: In-index merge" \ + HEAD "$result_commit" "$head" +git-diff-tree -p $head $result_commit | git-apply --stat +dropheads -- cgit v1.2.3 From 4cc41a16c15a6d73b13f9211eb8bf49ec869705f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 7 Feb 2007 17:03:46 -0800 Subject: Remove git-diff-stages. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 865531a5fd..7c03403484 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -269,7 +269,6 @@ __git_commands () cvsimport) : import;; cvsserver) : daemon;; daemon) : daemon;; - diff-stages) : nobody uses it;; fast-import) : import;; fsck-objects) : plumbing;; fetch-pack) : plumbing;; -- cgit v1.2.3 From 0563a4538a33c7ae9da41628d6f8849c38a2f935 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Feb 2007 17:13:17 +0100 Subject: Make it possible to specify the p4 changes to import through a text file (for debugging) and made various improvements to the branch/merge heuristic detection. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 128 ++++++++++++++++++++++++---------- 1 file changed, 90 insertions(+), 38 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 0a3e5c9934..b5dc6f6767 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -13,14 +13,16 @@ import os, string, sys, time import marshal, popen2, getopt knownBranches = set() +committedChanges = set() branch = "refs/heads/master" globalPrefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() detectBranches = False +changesFile = "" if len(globalPrefix) != 0: globalPrefix = globalPrefix[:-1] try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -30,6 +32,8 @@ for o, a in opts: branch = "refs/heads/" + a elif o == "--detect-branches": detectBranches = True + elif o == "--changesfile": + changesFile = a if len(args) == 0 and len(globalPrefix) != 0: print "[using previously specified depot path %s]" % globalPrefix @@ -53,7 +57,7 @@ changeRange = "" revision = "" users = {} initialParent = "" -lastChange = "" +lastChange = 0 initialTag = "" if globalPrefix.find("@") != -1: @@ -104,6 +108,7 @@ def extractFilesFromCommit(commit): path = commit["depotFile%s" % fnum] if not path.startswith(globalPrefix): print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) + fnum = fnum + 1 continue file = {} @@ -115,7 +120,15 @@ def extractFilesFromCommit(commit): fnum = fnum + 1 return files +def isSubPathOf(first, second): + if not first.startswith(second): + return False + if first == second: + return True + return first[len(second)] == "/" + def branchesForCommit(files): + global knownBranches branches = set() for file in files: @@ -123,9 +136,10 @@ def branchesForCommit(files): # strip off the filename relativePath = relativePath[0:relativePath.rfind("/")] - if len(branches) == 0: - branches.add(relativePath) - continue +# if len(branches) == 0: +# branches.add(relativePath) +# knownBranches.add(relativePath) +# continue ###### this needs more testing :) knownBranch = False @@ -133,15 +147,32 @@ def branchesForCommit(files): if relativePath == branch: knownBranch = True break - if relativePath.startswith(branch): +# if relativePath.startswith(branch): + if isSubPathOf(relativePath, branch): knownBranch = True break - if branch.startswith(relativePath): +# if branch.startswith(relativePath): + if isSubPathOf(branch, relativePath): branches.remove(branch) break - if not knownBranch: - branches.add(relativePath) + if knownBranch: + continue + + for branch in knownBranches: + #if relativePath.startswith(branch): + if isSubPathOf(relativePath, branch): + if len(branches) == 0: + relativePath = branch + else: + knownBranch = True + break + + if knownBranch: + continue + + branches.add(relativePath) + knownBranches.add(relativePath) return branches @@ -149,12 +180,14 @@ def commit(details, files, branch, branchPrefix): global initialParent global users global lastChange + global committedChanges epoch = details["time"] author = details["user"] gitStream.write("commit %s\n" % branch) gitStream.write("mark :%s\n" % details["change"]) + committedChanges.add(int(details["change"])) committer = "" if author in users: committer = "%s %s %s" % (users[author], epoch, tz) @@ -173,9 +206,11 @@ def commit(details, files, branch, branchPrefix): initialParent = "" #mergedBranches = set() - merge = 0 + merges = set() for file in files: + if lastChange == 0: + continue path = file["path"] if not path.startswith(branchPrefix): continue @@ -195,9 +230,14 @@ def commit(details, files, branch, branchPrefix): print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) sys.exit(1); - if not log["how0,0"].endswith(" from"): - print "eek! file %s was not branched but instead: %s" % (depotPath, log["how0,0"]) - sys.exit(1); + branchAction = log["how0,0"] +# if branchAction == "branch into" or branchAction == "ignored": +# continue # ignore for branching + + if not branchAction.endswith(" from"): + continue # ignore for branching +# print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) +# sys.exit(1); source = log["file0,0"] if source.startswith(branchPrefix): @@ -212,8 +252,7 @@ def commit(details, files, branch, branchPrefix): sourceLog = sourceLog[0] change = int(sourceLog["change0"]) - if change > merge: - merge = change + merges.add(change) # relPath = source[len(globalPrefix):] # @@ -223,13 +262,14 @@ def commit(details, files, branch, branchPrefix): # mergedBranches.add(branch) # break - if merge != 0: - gitStream.write("merge :%s\n" % merge) + for merge in merges: + if merge in committedChanges: + gitStream.write("merge :%s\n" % merge) for file in files: path = file["path"] if not path.startswith(branchPrefix): - print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, change) + print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) continue rev = file["rev"] depotPath = path + "#" + rev @@ -252,7 +292,7 @@ def commit(details, files, branch, branchPrefix): gitStream.write("\n") - lastChange = details["change"] + lastChange = int(details["change"]) def getUserMap(): users = {} @@ -322,14 +362,26 @@ if len(revision) > 0: print gitError.read() else: - output = os.popen("p4 changes %s...%s" % (globalPrefix, changeRange)).readlines() - changes = [] - for line in output: - changeNum = line.split(" ")[1] - changes.append(changeNum) - changes.reverse() + if len(changesFile) > 0: + output = open(changesFile).readlines() + changeSet = set() + for line in output: + changeSet.add(int(line)) + + for change in changeSet: + changes.append(change) + + changes.sort() + else: + output = os.popen("p4 changes %s...%s" % (globalPrefix, changeRange)).readlines() + + for line in output: + changeNum = line.split(" ")[1] + changes.append(changeNum) + + changes.reverse() if len(changes) == 0: print "no changes to import!" @@ -343,19 +395,19 @@ else: sys.stdout.flush() cnt = cnt + 1 - try: - files = extractFilesFromCommit(description) - if detectBranches: - for branch in branchesForCommit(files): - knownBranches.add(branch) - branchPrefix = globalPrefix + branch + "/" - branch = "refs/heads/" + branch - commit(description, files, branch, branchPrefix) - else: - commit(description, files, branch, globalPrefix) - except: - print gitError.read() - sys.exit(1) +# try: + files = extractFilesFromCommit(description) + if detectBranches: + for branch in branchesForCommit(files): + knownBranches.add(branch) + branchPrefix = globalPrefix + branch + "/" + branch = "refs/heads/" + branch + commit(description, files, branch, branchPrefix) + else: + commit(description, files, branch, globalPrefix) +# except: +# print gitError.read() +# sys.exit(1) print "" -- cgit v1.2.3 From f1e9b5345efade26961289860ced9e5c428360a4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Feb 2007 02:16:14 +1000 Subject: Use sets.Set() instead of set() to run also with older versions of Python. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index b5dc6f6767..76c4b9d323 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -11,9 +11,10 @@ # import os, string, sys, time import marshal, popen2, getopt +from sets import Set; -knownBranches = set() -committedChanges = set() +knownBranches = Set() +committedChanges = Set() branch = "refs/heads/master" globalPrefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() detectBranches = False @@ -129,7 +130,7 @@ def isSubPathOf(first, second): def branchesForCommit(files): global knownBranches - branches = set() + branches = Set() for file in files: relativePath = file["path"][len(globalPrefix):] @@ -205,8 +206,8 @@ def commit(details, files, branch, branchPrefix): gitStream.write("from %s\n" % initialParent) initialParent = "" - #mergedBranches = set() - merges = set() + #mergedBranches = Set() + merges = Set() for file in files: if lastChange == 0: @@ -366,7 +367,7 @@ else: if len(changesFile) > 0: output = open(changesFile).readlines() - changeSet = set() + changeSet = Set() for line in output: changeSet.add(int(line)) -- cgit v1.2.3 From 90dc3dfdc8ab903f5eb12f7081dd43b67c8c112a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 18 Feb 2007 01:18:22 +1000 Subject: Fix single-branch imports by skipping the branch/merge detection correctly. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 76c4b9d323..f37fdcf96e 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -210,7 +210,7 @@ def commit(details, files, branch, branchPrefix): merges = Set() for file in files: - if lastChange == 0: + if lastChange == 0 or not detectBranches: continue path = file["path"] if not path.startswith(branchPrefix): -- cgit v1.2.3 From c750da256a54f189de28d3deb054328d67f9b9be Mon Sep 17 00:00:00 2001 From: Michael Loeffler Date: Wed, 14 Feb 2007 17:03:12 +0100 Subject: Use gunzip -c over gzcat in import-tars example. Not everyone has gzcat or bzcat installed on their system, but gunzip -c and bunzip2 -c perform the same task and are available if the user has installed gzip support or bzip2 support. Signed-off-by: Michael Loeffler Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 990c9e70b2..5585a8b2c5 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -25,11 +25,14 @@ foreach my $tar_file (@ARGV) my $tar_name = $1; if ($tar_name =~ s/\.(tar\.gz|tgz)$//) { - open(I, '-|', 'gzcat', $tar_file) or die "Unable to gzcat $tar_file: $!\n"; + open(I, '-|', 'gunzip', '-c', $tar_file) + or die "Unable to gunzip -c $tar_file: $!\n"; } elsif ($tar_name =~ s/\.(tar\.bz2|tbz2)$//) { - open(I, '-|', 'bzcat', $tar_file) or die "Unable to bzcat $tar_file: $!\n"; + open(I, '-|', 'bunzip2', '-c', $tar_file) + or die "Unable to bunzip2 -c $tar_file: $!\n"; } elsif ($tar_name =~ s/\.tar\.Z$//) { - open(I, '-|', 'zcat', $tar_file) or die "Unable to zcat $tar_file: $!\n"; + open(I, '-|', 'uncompress', '-c', $tar_file) + or die "Unable to uncompress -c $tar_file: $!\n"; } elsif ($tar_name =~ s/\.tar$//) { open(I, $tar_file) or die "Unable to open $tar_file: $!\n"; } else { -- cgit v1.2.3 From 9c9fec3d28a77240fc7dbf44ba2f0805fed5f012 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Feb 2007 17:51:07 +0100 Subject: Added p4 delete behavioural emulation as todo item. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index f37fdcf96e..6fd86cf735 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -8,6 +8,8 @@ # TODO: # - support integrations (at least p4i) # - support p4 submit (hah!) +# - emulate p4's delete behavior: if a directory becomes empty delete it. continue +# with parent dir until non-empty dir is found. # import os, string, sys, time import marshal, popen2, getopt -- cgit v1.2.3 From 161958cc2f71eefbf514b2265741f7d2eb1a5f0b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Feb 2007 09:03:39 +0100 Subject: Added support for --silent so that p4-fast-export can be called from cronjobs. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 6fd86cf735..b39548917a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -15,6 +15,7 @@ import os, string, sys, time import marshal, popen2, getopt from sets import Set; +silent = False knownBranches = Set() committedChanges = Set() branch = "refs/heads/master" @@ -25,7 +26,7 @@ if len(globalPrefix) != 0: globalPrefix = globalPrefix[:-1] try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -37,6 +38,8 @@ for o, a in opts: detectBranches = True elif o == "--changesfile": changesFile = a + elif o == "--silent": + silent= True if len(args) == 0 and len(globalPrefix) != 0: print "[using previously specified depot path %s]" % globalPrefix @@ -110,7 +113,8 @@ def extractFilesFromCommit(commit): while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] if not path.startswith(globalPrefix): - print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) + if not silent: + print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) fnum = fnum + 1 continue @@ -272,7 +276,8 @@ def commit(details, files, branch, branchPrefix): for file in files: path = file["path"] if not path.startswith(branchPrefix): - print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) + if not silent: + print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) continue rev = file["rev"] depotPath = path + "#" + rev @@ -387,15 +392,17 @@ else: changes.reverse() if len(changes) == 0: - print "no changes to import!" + if not silent: + print "no changes to import!" sys.exit(1) cnt = 1 for change in changes: description = p4Cmd("describe %s" % change) - sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) - sys.stdout.flush() + if not silent: + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() cnt = cnt + 1 # try: @@ -412,7 +419,8 @@ else: # print gitError.read() # sys.exit(1) -print "" +if not silent: + print "" gitStream.write("reset refs/tags/p4/%s\n" % lastChange) gitStream.write("from %s\n\n" % branch); -- cgit v1.2.3 From 47e33ec082a4ee5d2925760cb1150b25f4ef392e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Feb 2007 09:22:36 +0100 Subject: More work in --silent support. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index b39548917a..0541f84188 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -42,7 +42,8 @@ for o, a in opts: silent= True if len(args) == 0 and len(globalPrefix) != 0: - print "[using previously specified depot path %s]" % globalPrefix + if not silent: + print "[using previously specified depot path %s]" % globalPrefix elif len(args) != 1: print "usage: %s //depot/path[@revRange]" % sys.argv[0] print "\n example:" -- cgit v1.2.3 From 5ea919de226177e52f62d1c92208ad9687fd76d7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Feb 2007 10:20:53 +0100 Subject: Don't print a plain newline at the end of the execution (avoids bogus cron error mails). Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 0541f84188..8f5b8fe314 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -332,8 +332,6 @@ if len(changeRange) == 0: except: pass -sys.stderr.write("\n") - tz = - time.timezone / 36 tzsign = ("%s" % tz)[0] if tzsign != '+' and tzsign != '-': -- cgit v1.2.3 From ae648606220c55074dfa12d1a11f60e62a7254ac Mon Sep 17 00:00:00 2001 From: Aneesh Kumar Date: Mon, 26 Feb 2007 14:01:57 +0530 Subject: blameview: Fix the browse behavior in blameview Signed-off-by: Junio C Hamano --- contrib/blameview/blameview.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/blameview/blameview.perl b/contrib/blameview/blameview.perl index a9a509febb..1dec00137b 100755 --- a/contrib/blameview/blameview.perl +++ b/contrib/blameview/blameview.perl @@ -41,7 +41,7 @@ $fileview->set_rules_hint(1); $fileview->signal_connect (row_activated => sub { my ($sl, $path, $column) = @_; my $row_ref = $sl->get_row_data_from_path ($path); - system("blameview @$row_ref[0] $fn &"); + system("blameview @$row_ref[0]~1 $fn &"); }); my $commitwindow = Gtk2::ScrolledWindow->new(); -- cgit v1.2.3 From 14b4f2dbd10c0c5f25ed7804eb5e803637231b32 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 28 Feb 2007 20:59:48 +0100 Subject: git.el: Set the default commit coding system from the repository config. If not otherwise specified, take the default coding system for commits from the 'i18n.commitencoding' repository configuration value. Also set the buffer-file-coding-system variable in the log buffer to make the selected coding system visible on the modeline. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 24629eb3e2..13d198229b 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -75,10 +75,11 @@ then to `add-log-mailing-address' and then to `user-mail-address'." :type '(choice (const :tag "Default" nil) (string :tag "Email"))) -(defcustom git-commits-coding-system 'utf-8 +(defcustom git-commits-coding-system nil "Default coding system for the log message of git commits." :group 'git - :type 'coding-system) + :type '(choice (const :tag "From repository config" nil) + (coding-system))) (defcustom git-append-signed-off-by nil "Whether to append a Signed-off-by line to the commit message before editing." @@ -236,6 +237,15 @@ and returns the process output as a string." (and (fboundp 'user-mail-address) (user-mail-address)) (and (boundp 'user-mail-address) user-mail-address))) +(defun git-get-commits-coding-system () + "Return the coding system to use for commits." + (let ((repo-config (git-config "i18n.commitencoding"))) + (or git-commits-coding-system + (and repo-config + (fboundp 'locale-charset-to-coding-system) + (locale-charset-to-coding-system repo-config)) + 'utf-8))) + (defun git-escape-file-name (name) "Escape a file name if necessary." (if (string-match "[\n\t\"\\]" name) @@ -327,7 +337,7 @@ and returns the process output as a string." "Call git-commit-tree with buffer as input and return the resulting commit SHA1." (let ((author-name (git-get-committer-name)) (author-email (git-get-committer-email)) - author-date log-start log-end args) + author-date log-start log-end args coding-system-for-write) (when head (push "-p" args) (push head args)) @@ -350,12 +360,12 @@ and returns the process output as a string." (push "-p" args) (push (match-string 1) args)))) (setq log-start (point-min))) - (setq log-end (point-max))) + (setq log-end (point-max)) + (setq coding-system-for-write buffer-file-coding-system)) (git-get-string-sha1 (with-output-to-string (with-current-buffer standard-output - (let ((coding-system-for-write git-commits-coding-system) - (env `(("GIT_AUTHOR_NAME" . ,author-name) + (let ((env `(("GIT_AUTHOR_NAME" . ,author-name) ("GIT_AUTHOR_EMAIL" . ,author-email) ("GIT_COMMITTER_NAME" . ,(git-get-committer-name)) ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email))))) @@ -888,6 +898,7 @@ and returns the process output as a string." (let ((buffer (get-buffer-create "*git-commit*")) (merge-heads (git-get-merge-heads)) (dir default-directory) + (coding-system (git-get-commits-coding-system)) (sign-off git-append-signed-off-by)) (with-current-buffer buffer (when (eq 0 (buffer-size)) @@ -912,6 +923,7 @@ and returns the process output as a string." (git-get-committer-name) (git-get-committer-email))))))) (log-edit #'git-do-commit nil #'git-log-edit-files buffer) (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) + (setq buffer-file-coding-system coding-system) (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))) (defun git-find-file () -- cgit v1.2.3 From 5ced0572217f82f20c4a3460492768e07c08aeea Mon Sep 17 00:00:00 2001 From: Xavier Maillard Date: Mon, 5 Mar 2007 09:23:42 +0100 Subject: contrib/emacs: Use non-interactive function to byte-compile files Add git-blame as a candidate to the byte-compilation. batch-byte-compile is the prefered way to byte-compile files in batch mode. Use it instead of the interactive function. Signed-off-by: Xavier Maillard Signed-off-by: Junio C Hamano --- contrib/emacs/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile index 350846de90..8554e3967c 100644 --- a/contrib/emacs/Makefile +++ b/contrib/emacs/Makefile @@ -2,7 +2,7 @@ EMACS = emacs -ELC = git.elc vc-git.elc +ELC = git.elc vc-git.elc git-blame.elc INSTALL ?= install INSTALL_ELC = $(INSTALL) -m 644 prefix ?= $(HOME) @@ -15,6 +15,6 @@ install: all $(INSTALL_ELC) $(ELC) $(emacsdir) %.elc: %.el - $(EMACS) --batch --eval '(byte-compile-file "$<")' + $(EMACS) -batch -f batch-byte-compile $< clean:; rm -f $(ELC) -- cgit v1.2.3 From 6392a40e5ec1cc1190f5870f6d7c9cc3710dfd46 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Mar 2007 19:58:54 +0100 Subject: Adjust the output parsing of git name-rev to handle the output of the latest git version. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-clean-tags.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-clean-tags.py b/contrib/fast-import/p4-clean-tags.py index 0be51f6405..924ff89cc6 100755 --- a/contrib/fast-import/p4-clean-tags.py +++ b/contrib/fast-import/p4-clean-tags.py @@ -25,7 +25,10 @@ for o, a in opts: sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) output = sout.read() tagIdx = output.index(" tags/p4/") -caretIdx = output.index("^") +try: + caretIdx = output.index("^") +except: + caretIdx = len(output) - 1 rev = int(output[tagIdx + 9 : caretIdx]) allTags = os.popen("git tag -l p4/").readlines() -- cgit v1.2.3 From 3ef674bd4b2f651d769cd5b464ba26313eab290a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Mar 2007 21:27:59 +0100 Subject: Work in progress on detecting branches. Added a disk-cache p4 output so debugging imports is faster. Added --known-branches commandline option for pre-defining branches. Various other fixes... Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 160 ++++++++++++++++++++++------------ 1 file changed, 103 insertions(+), 57 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 8f5b8fe314..a2cca31173 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -11,12 +11,15 @@ # - emulate p4's delete behavior: if a directory becomes empty delete it. continue # with parent dir until non-empty dir is found. # -import os, string, sys, time -import marshal, popen2, getopt +import os, string, sys, time, os.path +import marshal, popen2, getopt, sha from sets import Set; +cacheDebug = False + silent = False knownBranches = Set() +createdBranches = Set() committedChanges = Set() branch = "refs/heads/master" globalPrefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() @@ -26,7 +29,7 @@ if len(globalPrefix) != 0: globalPrefix = globalPrefix[:-1] try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent", "known-branches=" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -40,6 +43,9 @@ for o, a in opts: changesFile = a elif o == "--silent": silent= True + elif o == "--known-branches": + for branch in o.split(","): + knownBranches.add(branch) if len(args) == 0 and len(globalPrefix) != 0: if not silent: @@ -89,8 +95,37 @@ if globalPrefix.endswith("..."): if not globalPrefix.endswith("/"): globalPrefix += "/" +def p4File(depotPath): + cacheKey = "/tmp/p4cache/data-" + sha.new(depotPath).hexdigest() + + data = 0 + try: + if not cacheDebug: + raise + data = open(cacheKey, "rb").read() + except: + data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + if cacheDebug: + open(cacheKey, "wb").write(data) + + return data + def p4CmdList(cmd): - pipe = os.popen("p4 -G %s" % cmd, "rb") + fullCmd = "p4 -G %s" % cmd; + + cacheKey = sha.new(fullCmd).hexdigest() + cacheKey = "/tmp/p4cache/cmd-" + cacheKey + + cached = True + pipe = 0 + try: + if not cacheDebug: + raise + pipe = open(cacheKey, "rb") + except: + cached = False + pipe = os.popen(fullCmd, "rb") + result = [] try: while True: @@ -99,6 +134,13 @@ def p4CmdList(cmd): except EOFError: pass pipe.close() + + if not cached and cacheDebug: + pipe = open(cacheKey, "wb") + for r in result: + marshal.dump(r, pipe) + pipe.close() + return result def p4Cmd(cmd): @@ -114,8 +156,8 @@ def extractFilesFromCommit(commit): while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] if not path.startswith(globalPrefix): - if not silent: - print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) +# if not silent: +# print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) fnum = fnum + 1 continue @@ -184,41 +226,8 @@ def branchesForCommit(files): return branches -def commit(details, files, branch, branchPrefix): - global initialParent - global users - global lastChange - global committedChanges - - epoch = details["time"] - author = details["user"] - - gitStream.write("commit %s\n" % branch) - gitStream.write("mark :%s\n" % details["change"]) - committedChanges.add(int(details["change"])) - committer = "" - if author in users: - committer = "%s %s %s" % (users[author], epoch, tz) - else: - committer = "%s %s %s" % (author, epoch, tz) - - gitStream.write("committer %s\n" % committer) - - gitStream.write("data < 0: - gitStream.write("from %s\n" % initialParent) - initialParent = "" - - #mergedBranches = Set() - merges = Set() - +def findBranchParent(branchPrefix, files): for file in files: - if lastChange == 0 or not detectBranches: - continue path = file["path"] if not path.startswith(branchPrefix): continue @@ -259,26 +268,51 @@ def commit(details, files, branch, branchPrefix): sys.exit(1); sourceLog = sourceLog[0] - change = int(sourceLog["change0"]) - merges.add(change) + relPath = source[len(globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] -# relPath = source[len(globalPrefix):] -# -# for branch in knownBranches: -# if relPath.startswith(branch) and branch not in mergedBranches: -# gitStream.write("merge refs/heads/%s\n" % branch) -# mergedBranches.add(branch) -# break + for branch in knownBranches: + if isSubPathOf(relPath, branch): +# print "determined parent branch branch %s due to change in file %s" % (branch, source) + return "refs/heads/%s" % branch +# else: +# print "%s is not a subpath of branch %s" % (relPath, branch) + + return "" + +def commit(details, files, branch, branchPrefix, parent): + global users + global lastChange + global committedChanges + + epoch = details["time"] + author = details["user"] + + gitStream.write("commit %s\n" % branch) + gitStream.write("mark :%s\n" % details["change"]) + committedChanges.add(int(details["change"])) + committer = "" + if author in users: + committer = "%s %s %s" % (users[author], epoch, tz) + else: + committer = "%s %s %s" % (author, epoch, tz) - for merge in merges: - if merge in committedChanges: - gitStream.write("merge :%s\n" % merge) + gitStream.write("committer %s\n" % committer) + + gitStream.write("data < 0: + gitStream.write("from %s\n" % parent) for file in files: path = file["path"] if not path.startswith(branchPrefix): - if not silent: - print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) +# if not silent: +# print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) continue rev = file["rev"] depotPath = path + "#" + rev @@ -292,7 +326,7 @@ def commit(details, files, branch, branchPrefix): if file["type"].startswith("x"): mode = 755 - data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + data = p4File(depotPath) gitStream.write("M %s inline %s\n" % (mode, relPath)) gitStream.write("data %s\n" % len(data)) @@ -410,10 +444,22 @@ else: for branch in branchesForCommit(files): knownBranches.add(branch) branchPrefix = globalPrefix + branch + "/" + + parent = "" + ########### remove cnt!!! + if branch not in createdBranches and cnt > 2: + createdBranches.add(branch) + parent = findBranchParent(branchPrefix, files) + if parent == branch: + parent = "" +# elif len(parent) > 0: +# print "%s branched off of %s" % (branch, parent) + branch = "refs/heads/" + branch - commit(description, files, branch, branchPrefix) + commit(description, files, branch, branchPrefix, parent) else: - commit(description, files, branch, globalPrefix) + commit(description, files, branch, globalPrefix, initialParent) + initialParent = "" # except: # print gitError.read() # sys.exit(1) -- cgit v1.2.3 From 934371385c6e2e26132190542f4082c1655587e7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Mar 2007 21:34:40 +0100 Subject: Changed --known-branches to take a file as argument instead of a comma separated list. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index a2cca31173..5d4ed5cf9c 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -44,8 +44,8 @@ for o, a in opts: elif o == "--silent": silent= True elif o == "--known-branches": - for branch in o.split(","): - knownBranches.add(branch) + for branch in open(a).readlines(): + knownBranches.add(branch[:-1]) if len(args) == 0 and len(globalPrefix) != 0: if not silent: -- cgit v1.2.3 From a0f22e996c9d2f0e2a4a8feae852478f4aa667b5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 09:49:19 +0100 Subject: Fixed p4-debug file extension. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-debug.p4 | 25 ------------------------- contrib/fast-import/p4-debug.py | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 25 deletions(-) delete mode 100755 contrib/fast-import/p4-debug.p4 create mode 100755 contrib/fast-import/p4-debug.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-debug.p4 b/contrib/fast-import/p4-debug.p4 deleted file mode 100755 index 8fb159fd6a..0000000000 --- a/contrib/fast-import/p4-debug.p4 +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/python -# -# p4-debug.py -# -# Author: Simon Hausmann -# License: MIT -# -# executes a p4 command with -G and prints the resulting python dicts -# -import os, string, sys -import marshal, popen2 - -cmd = "" -for arg in sys.argv[1:]: - cmd += arg + " " - -pipe = os.popen("p4 -G %s" % cmd, "rb") -try: - while True: - entry = marshal.load(pipe) - print entry -except EOFError: - pass -pipe.close() - diff --git a/contrib/fast-import/p4-debug.py b/contrib/fast-import/p4-debug.py new file mode 100755 index 0000000000..8fb159fd6a --- /dev/null +++ b/contrib/fast-import/p4-debug.py @@ -0,0 +1,25 @@ +#!/usr/bin/python +# +# p4-debug.py +# +# Author: Simon Hausmann +# License: MIT +# +# executes a p4 command with -G and prints the resulting python dicts +# +import os, string, sys +import marshal, popen2 + +cmd = "" +for arg in sys.argv[1:]: + cmd += arg + " " + +pipe = os.popen("p4 -G %s" % cmd, "rb") +try: + while True: + entry = marshal.load(pipe) + print entry +except EOFError: + pass +pipe.close() + -- cgit v1.2.3 From 59f1d2b52d71c5082359f4caa9af5752f90d26a9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 10:25:34 +0100 Subject: Make the p4 data/command cache configurable through the --cache-debug commandline option. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 5d4ed5cf9c..3d2b42b636 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -29,7 +29,8 @@ if len(globalPrefix) != 0: globalPrefix = globalPrefix[:-1] try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent", "known-branches=" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent", "known-branches=", + "cache-debug" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -46,6 +47,8 @@ for o, a in opts: elif o == "--known-branches": for branch in open(a).readlines(): knownBranches.add(branch[:-1]) + elif o == "--cache-debug": + cacheDebug = True if len(args) == 0 and len(globalPrefix) != 0: if not silent: -- cgit v1.2.3 From 478764bc8261857e7eca2deac409e34c2778347a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 10:53:07 +0100 Subject: Minor code cleanups. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 3d2b42b636..bd2f03064b 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -340,6 +340,16 @@ def commit(details, files, branch, branchPrefix, parent): lastChange = int(details["change"]) +def extractFilesInCommitToBranch(files, branchPrefix): + newFiles = [] + + for file in files: + path = file["path"] + if path.startswith(branchPrefix): + newFiles.append(file) + + return newFiles + def getUserMap(): users = {} @@ -448,6 +458,8 @@ else: knownBranches.add(branch) branchPrefix = globalPrefix + branch + "/" + filesForCommit = extractFilesInCommitToBranch(files, branchPrefix) + parent = "" ########### remove cnt!!! if branch not in createdBranches and cnt > 2: @@ -458,10 +470,11 @@ else: # elif len(parent) > 0: # print "%s branched off of %s" % (branch, parent) + branch = "refs/heads/" + branch commit(description, files, branch, branchPrefix, parent) else: - commit(description, files, branch, globalPrefix, initialParent) + commit(description, filesForCommit, branch, globalPrefix, initialParent) initialParent = "" # except: # print gitError.read() -- cgit v1.2.3 From 85a8f1ac3ba10e20fab0e7f38e0d74213535c7ae Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 11:46:26 +0100 Subject: More code cleanups and preparations for more branch detection heuristics. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 61 ++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index bd2f03064b..65b7fca4b6 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -278,7 +278,7 @@ def findBranchParent(branchPrefix, files): for branch in knownBranches: if isSubPathOf(relPath, branch): # print "determined parent branch branch %s due to change in file %s" % (branch, source) - return "refs/heads/%s" % branch + return branch # else: # print "%s is not a subpath of branch %s" % (relPath, branch) @@ -350,6 +350,57 @@ def extractFilesInCommitToBranch(files, branchPrefix): return newFiles +def findBranchSourceHeuristic(files, branch, branchPrefix): + for file in files: + action = file["action"] + if action != "integrate" and action != "branch": + continue + path = file["path"] + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + branchAction = log["how0,0"] + + if not branchAction.endswith(" from"): + continue # ignore for branching +# print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) +# sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + lastSourceRev = log["erev0,0"] + + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + relPath = source[len(globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] + + for candidate in knownBranches: + if isSubPathOf(relPath, candidate) and candidate != branch: + return candidate + + return "" + +def changeIsBranchMerge(sourceBranch, destinationBranch, change): + return False + def getUserMap(): users = {} @@ -470,8 +521,16 @@ else: # elif len(parent) > 0: # print "%s branched off of %s" % (branch, parent) + if len(parent) == 0: + parent = findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) + if len(parent) > 0: + print "change %s could be a merge from %s into %s" % (description["change"], parent, branch) + if not changeIsBranchMerge(parent, branch, description["change"]): + parent = "" branch = "refs/heads/" + branch + if len(parent) > 0: + parent = "refs/heads/" + parent commit(description, files, branch, branchPrefix, parent) else: commit(description, filesForCommit, branch, globalPrefix, initialParent) -- cgit v1.2.3 From 43cc31e8a252d3ed326faa28d9a5d406c96d7b0d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 17:46:49 +0100 Subject: More work on branch detection by implementing changeIsBranchMerge(). Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 135 ++++++++++++++++++++++++---------- 1 file changed, 98 insertions(+), 37 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 65b7fca4b6..d0832e8c3d 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -284,7 +284,7 @@ def findBranchParent(branchPrefix, files): return "" -def commit(details, files, branch, branchPrefix, parent): +def commit(details, files, branch, branchPrefix, parent, merged = ""): global users global lastChange global committedChanges @@ -293,7 +293,7 @@ def commit(details, files, branch, branchPrefix, parent): author = details["user"] gitStream.write("commit %s\n" % branch) - gitStream.write("mark :%s\n" % details["change"]) +# gitStream.write("mark :%s\n" % details["change"]) committedChanges.add(int(details["change"])) committer = "" if author in users: @@ -311,6 +311,9 @@ def commit(details, files, branch, branchPrefix, parent): if len(parent) > 0: gitStream.write("from %s\n" % parent) + if len(merged) > 0: + gitStream.write("merge %s\n" % merged) + for file in files: path = file["path"] if not path.startswith(branchPrefix): @@ -399,7 +402,62 @@ def findBranchSourceHeuristic(files, branch, branchPrefix): return "" def changeIsBranchMerge(sourceBranch, destinationBranch, change): - return False + sourceFiles = {} + for file in p4CmdList("files %s...@%s" % (globalPrefix + sourceBranch + "/", change)): + if file["action"] == "delete": + continue + sourceFiles[file["depotFile"]] = file + + destinationFiles = {} + for file in p4CmdList("files %s...@%s" % (globalPrefix + destinationBranch + "/", change)): + destinationFiles[file["depotFile"]] = file + + for fileName in sourceFiles.keys(): + integrations = [] + deleted = False + for integration in p4CmdList("integrated \"%s\"" % fileName): + toFile = integration["fromFile"] # yes, it's true, it's fromFile + if not toFile in destinationFiles: + continue + destFile = destinationFiles[toFile] + if destFile["action"] == "delete": +# print "file %s has been deleted in %s" % (fileName, toFile) + deleted = True + break + + if int(integration["change"]) == change: + integrations.append(integration) + continue + + destRev = int(destFile["rev"]) + + startRev = integration["startFromRev"][1:] + if startRev == "none": + startRev = 0 + else: + startRev = int(startRev) + + endRev = integration["endFromRev"][1:] + if endRev == "none": + endRev = 0 + else: + endRev = int(endRev) + + initialBranch = (destRev == 1 and integration["how"] != "branch into") + inRange = (destRev >= startRev and destRev <= endRev) + newer = (destRev > startRev and destRev > endRev) + + if initialBranch or inRange or newer: + integrations.append(integration) + + if deleted: + continue + + if len(integrations) == 0: + print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) + return False + + return True def getUserMap(): users = {} @@ -502,42 +560,45 @@ else: sys.stdout.flush() cnt = cnt + 1 -# try: - files = extractFilesFromCommit(description) - if detectBranches: - for branch in branchesForCommit(files): - knownBranches.add(branch) - branchPrefix = globalPrefix + branch + "/" - - filesForCommit = extractFilesInCommitToBranch(files, branchPrefix) - - parent = "" - ########### remove cnt!!! - if branch not in createdBranches and cnt > 2: - createdBranches.add(branch) - parent = findBranchParent(branchPrefix, files) - if parent == branch: - parent = "" -# elif len(parent) > 0: -# print "%s branched off of %s" % (branch, parent) - - if len(parent) == 0: - parent = findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) - if len(parent) > 0: - print "change %s could be a merge from %s into %s" % (description["change"], parent, branch) - if not changeIsBranchMerge(parent, branch, description["change"]): + try: + files = extractFilesFromCommit(description) + if detectBranches: + for branch in branchesForCommit(files): + knownBranches.add(branch) + branchPrefix = globalPrefix + branch + "/" + + filesForCommit = extractFilesInCommitToBranch(files, branchPrefix) + + merged = "" + parent = "" + ########### remove cnt!!! + if branch not in createdBranches and cnt > 2: + createdBranches.add(branch) + parent = findBranchParent(branchPrefix, files) + if parent == branch: parent = "" + # elif len(parent) > 0: + # print "%s branched off of %s" % (branch, parent) - branch = "refs/heads/" + branch - if len(parent) > 0: - parent = "refs/heads/" + parent - commit(description, files, branch, branchPrefix, parent) - else: - commit(description, filesForCommit, branch, globalPrefix, initialParent) - initialParent = "" -# except: -# print gitError.read() -# sys.exit(1) + if len(parent) == 0: + merged = findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) + if len(merged) > 0: + print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) + if not changeIsBranchMerge(merged, branch, int(description["change"])): + merged = "" + + branch = "refs/heads/" + branch + if len(parent) > 0: + parent = "refs/heads/" + parent + if len(merged) > 0: + merged = "refs/heads/" + merged + commit(description, files, branch, branchPrefix, parent, merged) + else: + commit(description, filesForCommit, branch, globalPrefix, initialParent) + initialParent = "" + except IOError: + print gitError.read() + sys.exit(1) if not silent: print "" -- cgit v1.2.3 From dd87020bd3969dbac5ae26c527bcf50fe0ebb845 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 21:23:49 +0100 Subject: Reduce the number of false "merges" by skipping "branch from" entries in the integrated output as well as by ignoring integrations of future (newer) changes. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index d0832e8c3d..a45068d362 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -415,6 +415,7 @@ def changeIsBranchMerge(sourceBranch, destinationBranch, change): for fileName in sourceFiles.keys(): integrations = [] deleted = False + integrationCount = 0 for integration in p4CmdList("integrated \"%s\"" % fileName): toFile = integration["fromFile"] # yes, it's true, it's fromFile if not toFile in destinationFiles: @@ -424,10 +425,15 @@ def changeIsBranchMerge(sourceBranch, destinationBranch, change): # print "file %s has been deleted in %s" % (fileName, toFile) deleted = True break + integrationCount += 1 + if integration["how"] == "branch from": + continue if int(integration["change"]) == change: integrations.append(integration) continue + if int(integration["change"]) > change: + continue destRev = int(destFile["rev"]) @@ -453,7 +459,7 @@ def changeIsBranchMerge(sourceBranch, destinationBranch, change): if deleted: continue - if len(integrations) == 0: + if len(integrations) == 0 and integrationCount > 1: print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) return False -- cgit v1.2.3 From 4fe2ca17f779e14554ebca1208310ae4eb0806b5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 21:30:24 +0100 Subject: Split up the cache commandline options into (command) cache and data cache. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index a45068d362..26cd648ef7 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -15,7 +15,8 @@ import os, string, sys, time, os.path import marshal, popen2, getopt, sha from sets import Set; -cacheDebug = False +dataCache = False +commandCache = False silent = False knownBranches = Set() @@ -30,7 +31,7 @@ if len(globalPrefix) != 0: try: opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent", "known-branches=", - "cache-debug" ]) + "cache", "command-cache" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -47,8 +48,11 @@ for o, a in opts: elif o == "--known-branches": for branch in open(a).readlines(): knownBranches.add(branch[:-1]) - elif o == "--cache-debug": - cacheDebug = True + elif o == "--cache": + dataCache = True + commandCache = True + elif o == "--command-cache": + commandCache = True if len(args) == 0 and len(globalPrefix) != 0: if not silent: @@ -103,12 +107,12 @@ def p4File(depotPath): data = 0 try: - if not cacheDebug: + if not dataCache: raise data = open(cacheKey, "rb").read() except: data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() - if cacheDebug: + if dataCache: open(cacheKey, "wb").write(data) return data @@ -122,7 +126,7 @@ def p4CmdList(cmd): cached = True pipe = 0 try: - if not cacheDebug: + if not commandCache: raise pipe = open(cacheKey, "rb") except: @@ -138,7 +142,7 @@ def p4CmdList(cmd): pass pipe.close() - if not cached and cacheDebug: + if not cached and commandCache: pipe = open(cacheKey, "wb") for r in result: marshal.dump(r, pipe) -- cgit v1.2.3 From 38448147553917c584a353ed6c39ad55b037f694 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 10 Mar 2007 19:21:25 +0100 Subject: git.el: Avoid appending a signoff line that is already present. Also avoid inserting an extra newline if other signoff lines are present. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 13d198229b..427f89b0e1 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -891,6 +891,18 @@ and returns the process output as a string." (with-current-buffer log-edit-parent-buffer (git-get-filenames (git-marked-files-state 'added 'deleted 'modified)))) +(defun git-append-sign-off (name email) + "Append a Signed-off-by entry to the current buffer, avoiding duplicates." + (let ((sign-off (format "Signed-off-by: %s <%s>" name email)) + (case-fold-search t)) + (goto-char (point-min)) + (unless (re-search-forward (concat "^" (regexp-quote sign-off)) nil t) + (goto-char (point-min)) + (unless (re-search-forward "^Signed-off-by: " nil t) + (setq sign-off (concat "\n" sign-off))) + (goto-char (point-max)) + (insert sign-off "\n")))) + (defun git-commit-file () "Commit the marked file(s), asking for a commit message." (interactive) @@ -899,6 +911,8 @@ and returns the process output as a string." (merge-heads (git-get-merge-heads)) (dir default-directory) (coding-system (git-get-commits-coding-system)) + (committer-name (git-get-committer-name)) + (committer-email (git-get-committer-email)) (sign-off git-append-signed-off-by)) (with-current-buffer buffer (when (eq 0 (buffer-size)) @@ -907,7 +921,7 @@ and returns the process output as a string." (insert (propertize (format "Author: %s <%s>\n%s" - (git-get-committer-name) (git-get-committer-email) + committer-name committer-email (if merge-heads (format "Parent: %s\n%s\n" (git-rev-parse "HEAD") @@ -916,11 +930,9 @@ and returns the process output as a string." 'face 'git-header-face) (propertize git-log-msg-separator 'face 'git-separator-face) "\n") - (cond ((file-readable-p ".git/MERGE_MSG") - (insert-file-contents ".git/MERGE_MSG")) - (sign-off - (insert (format "\n\nSigned-off-by: %s <%s>\n" - (git-get-committer-name) (git-get-committer-email))))))) + (when (file-readable-p ".git/MERGE_MSG") + (insert-file-contents ".git/MERGE_MSG")) + (when sign-off (git-append-sign-off committer-name committer-email)))) (log-edit #'git-do-commit nil #'git-log-edit-files buffer) (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) (setq buffer-file-coding-system coding-system) -- cgit v1.2.3 From 60fa08ed617dd148a9843bfdef2dfecf2ef60123 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 10 Mar 2007 19:22:26 +0100 Subject: git.el: Retrieve commit log information from .dotest directory. If a git-am or git-rebase is in progress, fill the commit log buffer from the commit information found in the various files in the .dotest directory. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 77 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 24 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 427f89b0e1..db87a37895 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -903,36 +903,65 @@ and returns the process output as a string." (goto-char (point-max)) (insert sign-off "\n")))) -(defun git-commit-file () - "Commit the marked file(s), asking for a commit message." - (interactive) +(defun git-setup-log-buffer (buffer &optional author-name author-email subject date msg) + "Setup the log buffer for a commit." (unless git-status (error "Not in git-status buffer.")) - (let ((buffer (get-buffer-create "*git-commit*")) - (merge-heads (git-get-merge-heads)) + (let ((merge-heads (git-get-merge-heads)) (dir default-directory) - (coding-system (git-get-commits-coding-system)) (committer-name (git-get-committer-name)) (committer-email (git-get-committer-email)) (sign-off git-append-signed-off-by)) (with-current-buffer buffer - (when (eq 0 (buffer-size)) - (cd dir) - (erase-buffer) - (insert - (propertize - (format "Author: %s <%s>\n%s" - committer-name committer-email - (if merge-heads - (format "Parent: %s\n%s\n" - (git-rev-parse "HEAD") - (mapconcat (lambda (str) (concat "Parent: " str)) merge-heads "\n")) - "")) - 'face 'git-header-face) - (propertize git-log-msg-separator 'face 'git-separator-face) - "\n") - (when (file-readable-p ".git/MERGE_MSG") - (insert-file-contents ".git/MERGE_MSG")) - (when sign-off (git-append-sign-off committer-name committer-email)))) + (cd dir) + (erase-buffer) + (insert + (propertize + (format "Author: %s <%s>\n%s%s" + (or author-name committer-name) + (or author-email committer-email) + (if date (format "Date: %s\n" date) "") + (if merge-heads + (format "Parent: %s\n%s\n" + (git-rev-parse "HEAD") + (mapconcat (lambda (str) (concat "Parent: " str)) merge-heads "\n")) + "")) + 'face 'git-header-face) + (propertize git-log-msg-separator 'face 'git-separator-face) + "\n") + (when subject (insert subject "\n\n")) + (cond (msg (insert msg "\n")) + ((file-readable-p ".dotest/msg") + (insert-file-contents ".dotest/msg")) + ((file-readable-p ".git/MERGE_MSG") + (insert-file-contents ".git/MERGE_MSG"))) + ; delete empty lines at end + (goto-char (point-min)) + (when (re-search-forward "\n+\\'" nil t) + (replace-match "\n" t t)) + (when sign-off (git-append-sign-off committer-name committer-email))))) + +(defun git-commit-file () + "Commit the marked file(s), asking for a commit message." + (interactive) + (unless git-status (error "Not in git-status buffer.")) + (let ((buffer (get-buffer-create "*git-commit*")) + (coding-system (git-get-commits-coding-system)) + author-name author-email subject date) + (when (eq 0 (buffer-size buffer)) + (when (file-readable-p ".dotest/info") + (with-temp-buffer + (insert-file-contents ".dotest/info") + (goto-char (point-min)) + (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) + (setq author-name (match-string 1)) + (setq author-email (match-string 2))) + (goto-char (point-min)) + (when (re-search-forward "^Subject: \\(.*\\)$" nil t) + (setq subject (match-string 1))) + (goto-char (point-min)) + (when (re-search-forward "^Date: \\(.*\\)$" nil t) + (setq date (match-string 1))))) + (git-setup-log-buffer buffer author-name author-email subject date)) (log-edit #'git-do-commit nil #'git-log-edit-files buffer) (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) (setq buffer-file-coding-system coding-system) -- cgit v1.2.3 From 0bcff6121d68d8733e9b572eba357bcd8e91e23e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Mar 2007 23:00:34 +0100 Subject: First version of a new script to submit changes back to perforce from git repositories. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 208 +++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100755 contrib/fast-import/p4-git-sync.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py new file mode 100755 index 0000000000..8982e45345 --- /dev/null +++ b/contrib/fast-import/p4-git-sync.py @@ -0,0 +1,208 @@ +#!/usr/bin/python +# +# p4-git-sync.py +# +# Author: Simon Hausmann +# License: MIT +# + +import os, string, shelve, stat +import getopt, sys, marshal + +def p4CmdList(cmd): + cmd = "p4 -G %s" % cmd + pipe = os.popen(cmd, "rb") + + result = [] + try: + while True: + entry = marshal.load(pipe) + result.append(entry) + except EOFError: + pass + pipe.close() + + return result + +def p4Cmd(cmd): + list = p4CmdList(cmd) + result = {} + for entry in list: + result.update(entry) + return result; + +try: + opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "--git-dir=", "origin=", "reset", "master=", + "submit-log-subst=" ]) +except getopt.GetoptError: + print "fixme, syntax error" + sys.exit(1) + +logSubstitutions = {} +logSubstitutions[""] = "%log%" +logSubstitutions["\tDetails:"] = "\tDetails: %log%" +gitdir = os.environ.get("GIT_DIR", "") +origin = "origin" +master = "master" +firstTime = True +reset = False + +for o, a in opts: + if o == "--git-dir": + gitdir = a + elif o == "--origin": + origin = a + elif o == "--master": + master = a + elif o == "--continue": + firstTime = False + elif o == "--reset": + reset = True + firstTime = True + elif o == "--submit-log-subst": + key = a.split("%")[0] + value = a.split("%")[1] + logSubstitutions[key] = value + +if len(gitdir) == 0: + gitdir = ".git" +else: + os.environ["GIT_DIR"] = gitdir + +configFile = gitdir + "/p4-git-sync.cfg" + +origin = "origin" +if len(args) == 1: + origin = args[0] + +def die(msg): + sys.stderr.write(msg + "\n") + sys.exit(1) + +def system(cmd): + if os.system(cmd) != 0: + die("command failed: %s" % cmd) + +def check(): + return + if len(p4CmdList("opened ...")) > 0: + die("You have files opened with perforce! Close them before starting the sync.") + +def start(config): + if len(config) > 0 and not reset: + die("Cannot start sync. Previous sync config found at %s" % configFile) + + #if len(os.popen("git-update-index --refresh").read()) > 0: + # die("Your working tree is not clean. Check with git status!") + + commits = [] + for line in os.popen("git-rev-list --no-merges %s..%s" % (origin, master)).readlines(): + commits.append(line[:-1]) + commits.reverse() + + config["commits"] = commits + +# print "Cleaning index..." +# system("git checkout -f") + +def prepareLogMessage(template, message): + result = "" + + substs = logSubstitutions + for k in substs.keys(): + substs[k] = substs[k].replace("%log%", message) + + for line in template.split("\n"): + if line.startswith("#"): + result += line + "\n" + continue + + substituted = False + for key in substs.keys(): + if line.find(key) != -1: + value = substs[key] + if value != "@remove@": + result += line.replace(key, value) + "\n" + substituted = True + break + + if not substituted: + result += line + "\n" + + return result + +def apply(id): + print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) + diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + filesToAdd = set() + filesToDelete = set() + for line in diff: + modifier = line[0] + path = line[1:].strip() + if modifier == "M": + system("p4 edit %s" % path) + elif modifier == "A": + filesToAdd.add(path) + if path in filesToDelete: + filesToDelete.remove(path) + elif modifier == "D": + filesToDelete.add(path) + if path in filesToAdd: + filesToAdd.remove(path) + else: + die("unknown modifier %s for %s" % (modifier, path)) + + system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") + system("git cherry-pick --no-commit \"%s\"" % id) + + for f in filesToAdd: + system("p4 add %s" % f) + for f in filesToDelete: + system("p4 revert %s" % f) + system("p4 delete %s" % f) + + logMessage = "" + foundTitle = False + for log in os.popen("git-cat-file commit %s" % id).readlines(): + log = log[:-1] + if not foundTitle: + if len(log) == 0: + foundTitle = 1 + continue + + if len(logMessage) > 0: + logMessage += "\t" + logMessage += log + "\n" + + template = os.popen("p4 change -o").read() + fileName = "submit.txt" + file = open(fileName, "w+") + file.write(prepareLogMessage(template, logMessage)) + file.close() + print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) + +check() + +config = shelve.open(configFile, writeback=True) + +if firstTime: + start(config) + +commits = config.get("commits", []) + +if len(commits) > 0: + firstTime = False + commit = commits[0] + commits = commits[1:] + config["commits"] = commits + apply(commit) + +config.close() + +if len(commits) == 0: + if firstTime: + print "No changes found to apply between %s and current HEAD" % origin + else: + print "All changes applied!" + os.remove(configFile) + -- cgit v1.2.3 From 5aba82fd5056fa57f467f3c5fe81fe71ee0e8b99 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 13 Mar 2007 09:14:45 +0100 Subject: Fix git-dir option and allow reading log substitutions from a file Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index 8982e45345..0c0f629a1d 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -32,8 +32,8 @@ def p4Cmd(cmd): return result; try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "--git-dir=", "origin=", "reset", "master=", - "submit-log-subst=" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", + "submit-log-subst=", "log-substitutions=" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -63,6 +63,10 @@ for o, a in opts: key = a.split("%")[0] value = a.split("%")[1] logSubstitutions[key] = value + elif o == "--log-substitutions": + for line in open(a, "r").readlines(): + tokens = line[:-1].split("=") + logSubstitutions[tokens[0]] = tokens[1] if len(gitdir) == 0: gitdir = ".git" -- cgit v1.2.3 From 09a14fb52410cfda029b51832316616b44348505 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 13 Mar 2007 16:36:10 +0100 Subject: Lots of bugfixes to p4-git-sync. Added interactive and dry-run mode. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 90 +++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index 0c0f629a1d..5e307af8c6 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -3,11 +3,13 @@ # p4-git-sync.py # # Author: Simon Hausmann +# Copyright: 2007 Simon Hausmann +# 2007 Trolltech ASA # License: MIT # import os, string, shelve, stat -import getopt, sys, marshal +import getopt, sys, marshal, tempfile def p4CmdList(cmd): cmd = "p4 -G %s" % cmd @@ -33,7 +35,8 @@ def p4Cmd(cmd): try: opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", - "submit-log-subst=", "log-substitutions=" ]) + "submit-log-subst=", "log-substitutions=", "interactive", + "dry-run" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -46,6 +49,8 @@ origin = "origin" master = "master" firstTime = True reset = False +interactive = False +dryRun = False for o, a in opts: if o == "--git-dir": @@ -67,6 +72,10 @@ for o, a in opts: for line in open(a, "r").readlines(): tokens = line[:-1].split("=") logSubstitutions[tokens[0]] = tokens[1] + elif o == "--interactive": + interactive = True + elif o == "--dry-run": + dryRun = True if len(gitdir) == 0: gitdir = ".git" @@ -112,19 +121,16 @@ def start(config): def prepareLogMessage(template, message): result = "" - substs = logSubstitutions - for k in substs.keys(): - substs[k] = substs[k].replace("%log%", message) - for line in template.split("\n"): if line.startswith("#"): result += line + "\n" continue substituted = False - for key in substs.keys(): + for key in logSubstitutions.keys(): if line.find(key) != -1: - value = substs[key] + value = logSubstitutions[key] + value = value.replace("%log%", message) if value != "@remove@": result += line.replace(key, value) + "\n" substituted = True @@ -136,6 +142,7 @@ def prepareLogMessage(template, message): return result def apply(id): + global interactive print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() filesToAdd = set() @@ -158,6 +165,9 @@ def apply(id): system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") system("git cherry-pick --no-commit \"%s\"" % id) + #system("git format-patch --stdout -k \"%s^\"..\"%s\" | git-am -k" % (id, id)) + #system("git branch -D tmp") + #system("git checkout -f -b tmp \"%s^\"" % id) for f in filesToAdd: system("p4 add %s" % f) @@ -168,22 +178,66 @@ def apply(id): logMessage = "" foundTitle = False for log in os.popen("git-cat-file commit %s" % id).readlines(): - log = log[:-1] if not foundTitle: - if len(log) == 0: + if len(log) == 1: foundTitle = 1 continue if len(logMessage) > 0: logMessage += "\t" - logMessage += log + "\n" + logMessage += log template = os.popen("p4 change -o").read() - fileName = "submit.txt" - file = open(fileName, "w+") - file.write(prepareLogMessage(template, logMessage)) - file.close() - print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) + + if interactive: + submitTemplate = prepareLogMessage(template, logMessage) + diff = os.popen("p4 diff -du ...").read() + + for newFile in filesToAdd: + diff += "==== new file ====\n" + diff += "--- /dev/null\n" + diff += "+++ %s\n" % newFile + f = open(newFile, "r") + for line in f.readlines(): + diff += "+" + line + f.close() + + pipe = os.popen("less", "w") + pipe.write(submitTemplate + diff) + pipe.close() + + response = "e" + while response == "e": + response = raw_input("Do you want to submit this change (y/e/n)? ") + if response == "e": + [handle, fileName] = tempfile.mkstemp() + tmpFile = os.fdopen(handle, "w+") + tmpFile.write(submitTemplate) + tmpFile.close() + editor = os.environ.get("EDITOR", "vi") + system(editor + " " + fileName) + tmpFile = open(fileName, "r") + submitTemplate = tmpFile.read() + tmpFile.close() + os.remove(fileName) + + if response == "y" or response == "yes": + if dryRun: + print submitTemplate + raw_input("Press return to continue...") + else: + pipe = os.popen("p4 submit -i", "w") + pipe.write(submitTemplate) + pipe.close() + else: + print "Not submitting!" + interactive = False + else: + fileName = "submit.txt" + file = open(fileName, "w+") + file.write(prepareLogMessage(template, logMessage)) + file.close() + print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) check() @@ -194,12 +248,14 @@ if firstTime: commits = config.get("commits", []) -if len(commits) > 0: +while len(commits) > 0: firstTime = False commit = commits[0] commits = commits[1:] config["commits"] = commits apply(commit) + if not interactive: + break config.close() -- cgit v1.2.3 From 794a913a0096a383560eb06e0fdf1d8331e12c0a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Mar 2007 17:29:46 +0100 Subject: Automatically operate on a temporary branch, needed for cherry-pick to work when applying changes to files that are deleted in the future. Also do some Perforce cleaning Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index 5e307af8c6..02f4b36af3 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -97,7 +97,6 @@ def system(cmd): die("command failed: %s" % cmd) def check(): - return if len(p4CmdList("opened ...")) > 0: die("You have files opened with perforce! Close them before starting the sync.") @@ -115,6 +114,9 @@ def start(config): config["commits"] = commits + print "Creating temporary p4-sync branch from %s ..." % origin + system("git checkout -f -b p4-sync %s" % origin) + # print "Cleaning index..." # system("git checkout -f") @@ -264,5 +266,11 @@ if len(commits) == 0: print "No changes found to apply between %s and current HEAD" % origin else: print "All changes applied!" + print "Deleting temporary p4-sync branch and going back to %s" % master + system("git checkout %s" % master) + system("git branch -D p4-sync") + print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert -a ..." + system("p4 edit ...") + system("p4 revert -a ...") os.remove(configFile) -- cgit v1.2.3 From d7873afdf426b2f430f5635ff460012eb9c00d05 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Mar 2007 17:33:46 +0100 Subject: Be nice and use /usr/bin/env python for the git-p4 scripts Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 +- contrib/fast-import/p4-git-sync.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 26cd648ef7..6f7bbb0f32 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # p4-fast-export.py # diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index 02f4b36af3..a4d126fd46 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # p4-git-sync.py # -- cgit v1.2.3 From 4d9e5fcea65f22c6bd9e1f3e5ae2f79c3af613db Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Mar 2007 19:03:16 +0100 Subject: Ignore Apple resource files when importing from perforce to git. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 6f7bbb0f32..a6dbd3b98d 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -329,6 +329,10 @@ def commit(details, files, branch, branchPrefix, parent, merged = ""): relPath = path[len(branchPrefix):] action = file["action"] + if file["type"] == "apple": + print "\nfile %s is a strange apple file that forks. Ignoring!" %s path + continue + if action == "delete": gitStream.write("D %s\n" % relPath) else: -- cgit v1.2.3 From d566209e7fecffb61a3209766cc937bf027e2c6c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Mar 2007 23:30:23 +0100 Subject: Auto-detect the current git branch before submitting back to perforce. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index a4d126fd46..4f02079895 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -33,6 +33,10 @@ def p4Cmd(cmd): result.update(entry) return result; +def die(msg): + sys.stderr.write(msg + "\n") + sys.exit(1) + try: opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", "submit-log-subst=", "log-substitutions=", "interactive", @@ -46,7 +50,7 @@ logSubstitutions[""] = "%log%" logSubstitutions["\tDetails:"] = "\tDetails: %log%" gitdir = os.environ.get("GIT_DIR", "") origin = "origin" -master = "master" +master = "" firstTime = True reset = False interactive = False @@ -88,9 +92,12 @@ origin = "origin" if len(args) == 1: origin = args[0] -def die(msg): - sys.stderr.write(msg + "\n") - sys.exit(1) +if len(master) == 0: + sys.stdout.write("Auto-detecting current branch: ") + master = os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] + if len(master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, master)): + die("\nFailed to detect current branch! Aborting!"); + sys.stdout.write("%s\n" % master) def system(cmd): if os.system(cmd) != 0: -- cgit v1.2.3 From f72537f97e81398b597c05f12c112c52e8201b63 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Mar 2007 19:07:06 +0100 Subject: Use p4 revert ... instead of revert -a ... after submitting, to make sure the p4 checkout is clean. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index 4f02079895..e07fcee42c 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -276,8 +276,8 @@ if len(commits) == 0: print "Deleting temporary p4-sync branch and going back to %s" % master system("git checkout %s" % master) system("git branch -D p4-sync") - print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert -a ..." - system("p4 edit ...") - system("p4 revert -a ...") + print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." + system("p4 edit ... >/dev/null") + system("p4 revert ... >/dev/null") os.remove(configFile) -- cgit v1.2.3 From 228d36c92b022ae7941b542731c89db5d9de3eac Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 16 Mar 2007 13:47:46 +0100 Subject: Default to interactive syncing Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index e07fcee42c..ed88debd45 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -39,7 +39,7 @@ def die(msg): try: opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", - "submit-log-subst=", "log-substitutions=", "interactive", + "submit-log-subst=", "log-substitutions=", "noninteractive", "dry-run" ]) except getopt.GetoptError: print "fixme, syntax error" @@ -53,7 +53,7 @@ origin = "origin" master = "" firstTime = True reset = False -interactive = False +interactive = True dryRun = False for o, a in opts: @@ -76,8 +76,8 @@ for o, a in opts: for line in open(a, "r").readlines(): tokens = line[:-1].split("=") logSubstitutions[tokens[0]] = tokens[1] - elif o == "--interactive": - interactive = True + elif o == "--noninteractive": + interactive = False elif o == "--dry-run": dryRun = True -- cgit v1.2.3 From 6757ada403bf0eb0fb1fddcffbbeb74d91cbbb51 Mon Sep 17 00:00:00 2001 From: James Bowes Date: Tue, 13 Mar 2007 21:58:22 -0400 Subject: Make gc a builtin. Signed-off-by: James Bowes Signed-off-by: Junio C Hamano --- contrib/examples/git-gc.sh | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100755 contrib/examples/git-gc.sh (limited to 'contrib') diff --git a/contrib/examples/git-gc.sh b/contrib/examples/git-gc.sh new file mode 100755 index 0000000000..436d7caff5 --- /dev/null +++ b/contrib/examples/git-gc.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# +# Copyright (c) 2006, Shawn O. Pearce +# +# Cleanup unreachable files and optimize the repository. + +USAGE='[--prune]' +SUBDIRECTORY_OK=Yes +. git-sh-setup + +no_prune=: +while case $# in 0) break ;; esac +do + case "$1" in + --prune) + no_prune= + ;; + --) + usage + ;; + esac + shift +done + +case "$(git config --get gc.packrefs)" in +notbare|"") + test $(is_bare_repository) = true || pack_refs=true;; +*) + pack_refs=$(git config --bool --get gc.packrefs) +esac + +test "true" != "$pack_refs" || +git-pack-refs --prune && +git-reflog expire --all && +git-repack -a -d -l && +$no_prune git-prune && +git-rerere gc || exit -- cgit v1.2.3 From d55552f6e3b63ab6f33dd61071760bee42b9bc5e Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 17 Mar 2007 20:40:12 +0100 Subject: git.el: Add support for commit hooks. Run the pre-commit and post-commit hooks at appropriate places, and display their output if any. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 81 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 24 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index db87a37895..5f22dec5f7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1,6 +1,6 @@ ;;; git.el --- A user interface for git -;; Copyright (C) 2005, 2006 Alexandre Julliard +;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard ;; Version: 1.0 @@ -213,6 +213,23 @@ and returns the process output as a string." (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))) (message "Running git %s...done" (car args))) +(defun git-run-hook (hook env &rest args) + "Run a git hook and display its output if any." + (let ((dir default-directory) + (hook-name (expand-file-name (concat ".git/hooks/" hook)))) + (or (not (file-executable-p hook-name)) + (let (status (buffer (get-buffer-create "*Git Hook Output*"))) + (with-current-buffer buffer + (erase-buffer) + (cd dir) + (setq status + (if env + (apply #'call-process "env" nil (list buffer t) nil + (append (git-get-env-strings env) (list hook-name) args)) + (apply #'call-process hook-name nil (list buffer t) nil args)))) + (display-message-or-buffer buffer) + (eq 0 status))))) + (defun git-get-string-sha1 (string) "Read a SHA1 from the specified string." (and string @@ -590,6 +607,20 @@ and returns the process output as a string." (when modified (apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified))))) +(defun git-run-pre-commit-hook () + "Run the pre-commit hook if any." + (unless git-status (error "Not in git-status buffer.")) + (let ((files (git-marked-files-state 'added 'deleted 'modified))) + (or (not files) + (not (file-executable-p ".git/hooks/pre-commit")) + (let ((index-file (make-temp-file "gitidx"))) + (unwind-protect + (let ((head-tree (unless (git-empty-db-p) (git-rev-parse "HEAD^{tree}")))) + (git-read-tree head-tree index-file) + (git-update-index index-file files) + (git-run-hook "pre-commit" `(("GIT_INDEX_FILE" . ,index-file)))) + (delete-file index-file)))))) + (defun git-do-commit () "Perform the actual commit using the current buffer as log message." (interactive) @@ -622,7 +653,8 @@ and returns the process output as a string." (git-run-command nil nil "rerere")) (git-refresh-files) (git-refresh-ewoc-hf git-status) - (message "Committed %s." commit)) + (message "Committed %s." commit) + (git-run-hook "post-commit" nil)) (message "Commit aborted.")))) (message "No files to commit."))) (delete-file index-file)))))) @@ -944,28 +976,29 @@ and returns the process output as a string." "Commit the marked file(s), asking for a commit message." (interactive) (unless git-status (error "Not in git-status buffer.")) - (let ((buffer (get-buffer-create "*git-commit*")) - (coding-system (git-get-commits-coding-system)) - author-name author-email subject date) - (when (eq 0 (buffer-size buffer)) - (when (file-readable-p ".dotest/info") - (with-temp-buffer - (insert-file-contents ".dotest/info") - (goto-char (point-min)) - (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) - (setq author-name (match-string 1)) - (setq author-email (match-string 2))) - (goto-char (point-min)) - (when (re-search-forward "^Subject: \\(.*\\)$" nil t) - (setq subject (match-string 1))) - (goto-char (point-min)) - (when (re-search-forward "^Date: \\(.*\\)$" nil t) - (setq date (match-string 1))))) - (git-setup-log-buffer buffer author-name author-email subject date)) - (log-edit #'git-do-commit nil #'git-log-edit-files buffer) - (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) - (setq buffer-file-coding-system coding-system) - (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))) + (when (git-run-pre-commit-hook) + (let ((buffer (get-buffer-create "*git-commit*")) + (coding-system (git-get-commits-coding-system)) + author-name author-email subject date) + (when (eq 0 (buffer-size buffer)) + (when (file-readable-p ".dotest/info") + (with-temp-buffer + (insert-file-contents ".dotest/info") + (goto-char (point-min)) + (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) + (setq author-name (match-string 1)) + (setq author-email (match-string 2))) + (goto-char (point-min)) + (when (re-search-forward "^Subject: \\(.*\\)$" nil t) + (setq subject (match-string 1))) + (goto-char (point-min)) + (when (re-search-forward "^Date: \\(.*\\)$" nil t) + (setq date (match-string 1))))) + (git-setup-log-buffer buffer author-name author-email subject date)) + (log-edit #'git-do-commit nil #'git-log-edit-files buffer) + (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) + (setq buffer-file-coding-system coding-system) + (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) (defun git-find-file () "Visit the current file in its own buffer." -- cgit v1.2.3 From 09e16455e028ae264347067a8c86a7b9a1e5889c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 11:57:07 +0100 Subject: Improved the git dir detection. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index ed88debd45..5a3bf90485 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -37,6 +37,11 @@ def die(msg): sys.stderr.write(msg + "\n") sys.exit(1) +def tryGitDir(path): + if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): + return True; + return False + try: opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", "submit-log-subst=", "log-substitutions=", "noninteractive", @@ -86,6 +91,14 @@ if len(gitdir) == 0: else: os.environ["GIT_DIR"] = gitdir +if not tryGitDir(gitdir): + if tryGitDir(gitdir + "/.git"): + gitdir += "/.git" + os.environ["GIT_DIR"] = gitdir + else: + die("fatal: %s seems not to be a git repository." % gitdir) + + configFile = gitdir + "/p4-git-sync.cfg" origin = "origin" -- cgit v1.2.3 From 95d27cb75d70331cc775f37d1f3396cd01e5e238 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 12:04:37 +0100 Subject: Pass the right number of arguments to commit, fixes single-branch imports. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index a6dbd3b98d..9adb88fade 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -330,7 +330,7 @@ def commit(details, files, branch, branchPrefix, parent, merged = ""): action = file["action"] if file["type"] == "apple": - print "\nfile %s is a strange apple file that forks. Ignoring!" %s path + print "\nfile %s is a strange apple file that forks. Ignoring!" % path continue if action == "delete": @@ -608,7 +608,7 @@ else: merged = "refs/heads/" + merged commit(description, files, branch, branchPrefix, parent, merged) else: - commit(description, filesForCommit, branch, globalPrefix, initialParent) + commit(description, files, branch, globalPrefix, initialParent) initialParent = "" except IOError: print gitError.read() -- cgit v1.2.3 From 86949eef4088d7b57fe7433568d573a926816f5c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 20:59:12 +0100 Subject: Start moving the git-p4 tools into one single script. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 115 +++++++++++++++++++++++++++++++++++ contrib/fast-import/p4-clean-tags.py | 43 ------------- contrib/fast-import/p4-debug.py | 25 -------- 3 files changed, 115 insertions(+), 68 deletions(-) create mode 100755 contrib/fast-import/git-p4.py delete mode 100755 contrib/fast-import/p4-clean-tags.py delete mode 100755 contrib/fast-import/p4-debug.py (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py new file mode 100755 index 0000000000..8008156043 --- /dev/null +++ b/contrib/fast-import/git-p4.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# +# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git. +# +# Author: Simon Hausmann +# License: MIT +# + +import optparse, sys, os, marshal, popen2 + +def p4CmdList(cmd): + cmd = "p4 -G %s" % cmd + pipe = os.popen(cmd, "rb") + + result = [] + try: + while True: + entry = marshal.load(pipe) + result.append(entry) + except EOFError: + pass + pipe.close() + + return result + +def p4Cmd(cmd): + list = p4CmdList(cmd) + result = {} + for entry in list: + result.update(entry) + return result; + +def die(msg): + sys.stderr.write(msg + "\n") + sys.exit(1) + +def currentGitBranch(): + return os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] + +class P4Debug: + def __init__(self): + self.options = [ + ] + + def run(self, args): + for output in p4CmdList(" ".join(args)): + print output + +class P4CleanTags: + def __init__(self): + self.options = [ +# optparse.make_option("--branch", dest="branch", default="refs/heads/master") + ] + def run(self, args): + branch = currentGitBranch() + print "Cleaning out stale p4 import tags..." + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) + output = sout.read() + try: + tagIdx = output.index(" tags/p4/") + except: + print "Cannot find any p4/* tag. Nothing to do." + sys.exit(0) + + try: + caretIdx = output.index("^") + except: + caretIdx = len(output) - 1 + rev = int(output[tagIdx + 9 : caretIdx]) + + allTags = os.popen("git tag -l p4/").readlines() + for i in range(len(allTags)): + allTags[i] = int(allTags[i][3:-1]) + + allTags.sort() + + allTags.remove(rev) + + for rev in allTags: + print os.popen("git tag -d p4/%s" % rev).read() + + print "%s tags removed." % len(allTags) + +def printUsage(commands): + print "usage: %s [options]" % sys.argv[0] + print "" + print "valid commands: %s" % ", ".join(commands) + print "" + print "Try %s --help for command specific help." % sys.argv[0] + print "" + +commands = { + "debug" : P4Debug(), + "clean-tags" : P4CleanTags() +} + +if len(sys.argv[1:]) == 0: + printUsage(commands.keys()) + sys.exit(2) + +cmd = "" +cmdName = sys.argv[1] +try: + cmd = commands[cmdName] +except KeyError: + print "unknown command %s" % cmdName + print "" + printUsage(commands.keys()) + sys.exit(2) + +parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", cmd.options) + +(cmd, args) = parser.parse_args(sys.argv[2:], cmd); + +cmd.run(args) diff --git a/contrib/fast-import/p4-clean-tags.py b/contrib/fast-import/p4-clean-tags.py deleted file mode 100755 index 924ff89cc6..0000000000 --- a/contrib/fast-import/p4-clean-tags.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/python -# -# p4-debug.py -# -# Author: Simon Hausmann -# License: MIT -# -# removes unused p4 import tags -# -import os, string, sys -import popen2, getopt - -branch = "refs/heads/master" - -try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=" ]) -except getopt.GetoptError: - print "fixme, syntax error" - sys.exit(1) - -for o, a in opts: - if o == "--branch": - branch = "refs/heads/" + a - -sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) -output = sout.read() -tagIdx = output.index(" tags/p4/") -try: - caretIdx = output.index("^") -except: - caretIdx = len(output) - 1 -rev = int(output[tagIdx + 9 : caretIdx]) - -allTags = os.popen("git tag -l p4/").readlines() -for i in range(len(allTags)): - allTags[i] = int(allTags[i][3:-1]) - -allTags.sort() - -allTags.remove(rev) - -for rev in allTags: - print os.popen("git tag -d p4/%s" % rev).read() diff --git a/contrib/fast-import/p4-debug.py b/contrib/fast-import/p4-debug.py deleted file mode 100755 index 8fb159fd6a..0000000000 --- a/contrib/fast-import/p4-debug.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/python -# -# p4-debug.py -# -# Author: Simon Hausmann -# License: MIT -# -# executes a p4 command with -G and prints the resulting python dicts -# -import os, string, sys -import marshal, popen2 - -cmd = "" -for arg in sys.argv[1:]: - cmd += arg + " " - -pipe = os.popen("p4 -G %s" % cmd, "rb") -try: - while True: - entry = marshal.load(pipe) - print entry -except EOFError: - pass -pipe.close() - -- cgit v1.2.3 From c8c3911685de1185ca7c0afe62bb3964ebc82d38 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 21:02:30 +0100 Subject: Provide a little bit of help description for the git-p4 "tools". Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 8008156043..42efc7d9aa 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -41,6 +41,7 @@ class P4Debug: def __init__(self): self.options = [ ] + self.description = "A tool to debug the output of p4 -G." def run(self, args): for output in p4CmdList(" ".join(args)): @@ -51,6 +52,7 @@ class P4CleanTags: self.options = [ # optparse.make_option("--branch", dest="branch", default="refs/heads/master") ] + self.description = "A tool to remove stale unused tags from incremental perforce imports." def run(self, args): branch = currentGitBranch() print "Cleaning out stale p4 import tags..." @@ -108,7 +110,8 @@ except KeyError: printUsage(commands.keys()) sys.exit(2) -parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", cmd.options) +parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", cmd.options, + description = cmd.description) (cmd, args) = parser.parse_args(sys.argv[2:], cmd); -- cgit v1.2.3 From 4f5cf76a55ecb3252b0924d0c4a16f3b037908cd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 22:25:17 +0100 Subject: First (untested) attempt at migrating p4-git-sync into the final git-p4 script Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 243 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 240 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 42efc7d9aa..593bda822f 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -6,7 +6,10 @@ # License: MIT # -import optparse, sys, os, marshal, popen2 +import optparse, sys, os, marshal, popen2, shelve +import tempfile + +gitdir = os.environ.get("GIT_DIR", "") def p4CmdList(cmd): cmd = "p4 -G %s" % cmd @@ -37,6 +40,15 @@ def die(msg): def currentGitBranch(): return os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] +def isValidGitDir(path): + if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): + return True; + return False + +def system(cmd): + if os.system(cmd) != 0: + die("command failed: %s" % cmd) + class P4Debug: def __init__(self): self.options = [ @@ -83,6 +95,214 @@ class P4CleanTags: print "%s tags removed." % len(allTags) +class P4Sync: + def __init__(self): + self.options = [ + optparse.make_option("--continue", action="store_false", dest="firstTime"), + optparse.make_option("--origin", dest="origin"), + optparse.make_option("--reset", action="store_true", dest="reset"), + optparse.make_option("--master", dest="master"), + optparse.make_option("--log-substitutions", dest="substFile"), + optparse.make_option("--noninteractive", action="store_false"), + optparse.make_option("--dry-run", action="store_true") + ] + self.description = "Submit changes from git to the perforce depot." + self.firstTime = True + self.reset = False + self.interactive = True + self.dryRun = False + self.substFile = "" + self.firstTime = True + self.origin = "origin" + self.master = "" + + self.logSubstitutions = {} + self.logSubstitutions[""] = "%log%" + self.logSubstitutions["\tDetails:"] = "\tDetails: %log%" + + def check(self): + if len(p4CmdList("opened ...")) > 0: + die("You have files opened with perforce! Close them before starting the sync.") + + def start(self): + if len(self.config) > 0 and not self.reset: + die("Cannot start sync. Previous sync config found at %s" % self.configFile) + + commits = [] + for line in os.popen("git-rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + commits.append(line[:-1]) + commits.reverse() + + self.config["commits"] = commits + + print "Creating temporary p4-sync branch from %s ..." % self.origin + system("git checkout -f -b p4-sync %s" % self.origin) + + def prepareLogMessage(self, template, message): + result = "" + + for line in template.split("\n"): + if line.startswith("#"): + result += line + "\n" + continue + + substituted = False + for key in self.logSubstitutions.keys(): + if line.find(key) != -1: + value = self.logSubstitutions[key] + value = value.replace("%log%", message) + if value != "@remove@": + result += line.replace(key, value) + "\n" + substituted = True + break + + if not substituted: + result += line + "\n" + + return result + + def apply(self, id): + print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) + diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + filesToAdd = set() + filesToDelete = set() + for line in diff: + modifier = line[0] + path = line[1:].strip() + if modifier == "M": + system("p4 edit %s" % path) + elif modifier == "A": + filesToAdd.add(path) + if path in filesToDelete: + filesToDelete.remove(path) + elif modifier == "D": + filesToDelete.add(path) + if path in filesToAdd: + filesToAdd.remove(path) + else: + die("unknown modifier %s for %s" % (modifier, path)) + + system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") + system("git cherry-pick --no-commit \"%s\"" % id) + + for f in filesToAdd: + system("p4 add %s" % f) + for f in filesToDelete: + system("p4 revert %s" % f) + system("p4 delete %s" % f) + + logMessage = "" + foundTitle = False + for log in os.popen("git-cat-file commit %s" % id).readlines(): + if not foundTitle: + if len(log) == 1: + foundTitle = 1 + continue + + if len(logMessage) > 0: + logMessage += "\t" + logMessage += log + + template = os.popen("p4 change -o").read() + + if self.interactive: + submitTemplate = self.prepareLogMessage(template, logMessage) + diff = os.popen("p4 diff -du ...").read() + + for newFile in filesToAdd: + diff += "==== new file ====\n" + diff += "--- /dev/null\n" + diff += "+++ %s\n" % newFile + f = open(newFile, "r") + for line in f.readlines(): + diff += "+" + line + f.close() + + pipe = os.popen("less", "w") + pipe.write(submitTemplate + diff) + pipe.close() + + response = "e" + while response == "e": + response = raw_input("Do you want to submit this change (y/e/n)? ") + if response == "e": + [handle, fileName] = tempfile.mkstemp() + tmpFile = os.fdopen(handle, "w+") + tmpFile.write(submitTemplate) + tmpFile.close() + editor = os.environ.get("EDITOR", "vi") + system(editor + " " + fileName) + tmpFile = open(fileName, "r") + submitTemplate = tmpFile.read() + tmpFile.close() + os.remove(fileName) + + if response == "y" or response == "yes": + if self.dryRun: + print submitTemplate + raw_input("Press return to continue...") + else: + pipe = os.popen("p4 submit -i", "w") + pipe.write(submitTemplate) + pipe.close() + else: + print "Not submitting!" + self.interactive = False + else: + fileName = "submit.txt" + file = open(fileName, "w+") + file.write(self.prepareLogMessage(template, logMessage)) + file.close() + print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) + + def run(self, args): + if self.reset: + self.firstTime = True + + if len(self.substFile) > 0: + for line in open(self.substFile, "r").readlines(): + tokens = line[:-1].split("=") + self.logSubstitutions[tokens[0]] = tokens[1] + + if len(self.master) == 0: + self.master = currentGitBranch() + if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): + die("Detecting current git branch failed!") + + self.check() + self.configFile = gitdir + "/p4-git-sync.cfg" + self.config = shelve.open(self.configFile, writeback=True) + + if self.firstTime: + self.start() + + commits = self.config.get("commits", []) + + while len(commits) > 0: + self.firstTime = False + commit = commits[0] + commits = commits[1:] + self.config["commits"] = commits + self.apply(commit) + if not self.interactive: + break + + self.config.close() + + if len(commits) == 0: + if self.firstTime: + print "No changes found to apply between %s and current HEAD" % self.origin + else: + print "All changes applied!" + print "Deleting temporary p4-sync branch and going back to %s" % self.master + system("git checkout %s" % self.master) + system("git branch -D p4-sync") + print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." + system("p4 edit ... >/dev/null") + system("p4 revert ... >/dev/null") + os.remove(self.configFile) + + def printUsage(commands): print "usage: %s [options]" % sys.argv[0] print "" @@ -93,7 +313,8 @@ def printUsage(commands): commands = { "debug" : P4Debug(), - "clean-tags" : P4CleanTags() + "clean-tags" : P4CleanTags(), + "sync-to-perforce" : P4Sync() } if len(sys.argv[1:]) == 0: @@ -110,9 +331,25 @@ except KeyError: printUsage(commands.keys()) sys.exit(2) -parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", cmd.options, +options = cmd.options +cmd.gitdir = gitdir +options.append(optparse.make_option("--git-dir", dest="gitdir")) + +parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", options, description = cmd.description) (cmd, args) = parser.parse_args(sys.argv[2:], cmd); +gitdir = cmd.gitdir +if len(gitdir) == 0: + gitdir = ".git" + +if not isValidGitDir(gitdir): + if isValidGitDir(gitdir + "/.git"): + gitdir += "/.git" + else: + dir("fatal: cannot locate git repository at %s" % gitdir) + +os.environ["GIT_DIR"] = gitdir + cmd.run(args) -- cgit v1.2.3 From 83dce55af3c792134787bdc807f932dc8a67ea74 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 22:26:36 +0100 Subject: Part of the code is copyright by Trolltech ASA. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 593bda822f..fa1b19fbbc 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -3,6 +3,8 @@ # git-p4.py -- A tool for bidirectional operation between a Perforce depot and git. # # Author: Simon Hausmann +# Copyright: 2007 Simon Hausmann +# 2007 Trolltech ASA # License: MIT # -- cgit v1.2.3 From 3e993bb657daad31d8a7b1ef0a6a9f73bdd5f950 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 20 Mar 2007 00:33:41 -0400 Subject: contrib/continuous: a continuous integration build manager This is a simple but powerful continuous integration build system for Git. It works by receiving push events from repositories through the post-receive hook, aggregates them on a per-branch basis into a first-come-first-serve build queue, and lets a background build daemon perform builds one at a time. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/continuous/cidaemon | 503 +++++++++++++++++++++++++++++++ contrib/continuous/post-receive-cinotify | 104 +++++++ 2 files changed, 607 insertions(+) create mode 100644 contrib/continuous/cidaemon create mode 100644 contrib/continuous/post-receive-cinotify (limited to 'contrib') diff --git a/contrib/continuous/cidaemon b/contrib/continuous/cidaemon new file mode 100644 index 0000000000..4009a151de --- /dev/null +++ b/contrib/continuous/cidaemon @@ -0,0 +1,503 @@ +#!/usr/bin/perl +# +# A daemon that waits for update events sent by its companion +# post-receive-cinotify hook, checks out a new copy of source, +# compiles it, and emails the guilty parties if the compile +# (and optionally test suite) fails. +# +# To use this daemon, configure it and run it. It will disconnect +# from your terminal and fork into the background. The daemon must +# have local filesystem access to the source repositories, as it +# uses objects/info/alternates to avoid copying objects. +# +# Add its companion post-receive-cinotify hook as the post-receive +# hook to each repository that the daemon should monitor. Yes, a +# single daemon can monitor more than one repository. +# +# To use multiple daemons on the same system, give them each a +# unique queue file and tmpdir. +# +# Global Config +# ------------- +# Reads from a Git style configuration file. This will be +# ~/.gitconfig by default but can be overridden by setting +# the GIT_CONFIG_FILE environment variable before starting. +# +# cidaemon.smtpHost +# Hostname of the SMTP server the daemon will send email +# through. Defaults to 'localhost'. +# +# cidaemon.smtpUser +# Username to authenticate to the SMTP server as. This +# variable is optional; if it is not supplied then no +# authentication will be performed. +# +# cidaemon.smtpPassword +# Password to authenticate to the SMTP server as. This +# variable is optional. If not supplied but smtpUser was, +# the daemon prompts for the password before forking into +# the background. +# +# cidaemon.smtpAuth +# Type of authentication to perform with the SMTP server. +# If set to 'login' and smtpUser was defined, this will +# use the AUTH LOGIN command, which is suitable for use +# with at least one version of Microsoft Exchange Server. +# If not set the daemon will use whatever auth methods +# are supported by your version of Net::SMTP. +# +# cidaemon.email +# Email address that daemon generated emails will be sent +# from. This should be a useful email address within your +# organization. Required. +# +# cidaemon.name +# Human friendly name that the daemon will send emails as. +# Defaults to 'cidaemon'. +# +# cidaemon.scanDelay +# Number of seconds to sleep between polls of the queue file. +# Defaults to 60. +# +# cidaemon.recentCache +# Number of recent commit SHA-1s per repository to cache and +# skip building if they appear again. This is useful to avoid +# rebuilding the same commit multiple times just because it was +# pushed into more than one branch. Defaults to 100. +# +# cidaemon.tmpdir +# Scratch directory to create the builds within. The daemon +# makes a new subdirectory for each build, then deletes it when +# the build has finished. The pid file is also placed here. +# Defaults to '/tmp'. +# +# cidaemon.queue +# Path to the queue file that the post-receive-cinotify hook +# appends events to. This file is polled by the daemon. It +# must not be on an NFS mount (uses flock). Required. +# +# cidaemon.nocc +# Perl regex patterns to match against author and committer +# lines. If a pattern matches, that author or committer will +# not be notified of a build failure. +# +# Per Repository Config +# ---------------------- +# Read from the source repository's config file. +# +# builder.command +# Shell command to execute the build. This command must +# return 0 on "success" and non-zero on failure. If you +# also want to run a test suite, make sure your command +# does that too. Required. +# +# builder.queue +# Queue file to notify the cidaemon through. Should match +# cidaemon.queue. If not set the hook will not notify the +# cidaemon. +# +# builder.skip +# Perl regex patterns of refs that should not be sent to +# cidaemon. Updates of these refs will be ignored. +# +# builder.newBranchBase +# Glob patterns of refs that should be used to form the +# 'old' revions of a newly created ref. This should set +# to be globs that match your 'mainline' branches. This +# way a build failure of a brand new topic branch does not +# attempt to email everyone since the beginning of time; +# instead it only emails those authors of commits not in +# these 'mainline' branches. + +local $ENV{PATH} = join ':', qw( + /opt/git/bin + /usr/bin + /bin + ); + +use strict; +use warnings; +use FindBin qw($RealBin); +use File::Spec; +use lib File::Spec->catfile($RealBin, '..', 'perl5'); +use Storable qw(retrieve nstore); +use Fcntl ':flock'; +use POSIX qw(strftime); +use Getopt::Long qw(:config no_auto_abbrev auto_help); + +sub git_config ($;$) +{ + my $var = shift; + my $required = shift || 0; + local *GIT; + open GIT, '-|','git','config','--get',$var; + my $r = ; + chop $r if $r; + close GIT; + die "error: $var not set.\n" if ($required && !$r); + return $r; +} + +package EXCHANGE_NET_SMTP; + +# Microsoft Exchange Server requires an 'AUTH LOGIN' +# style of authentication. This is different from +# the default supported by Net::SMTP so we subclass +# and override the auth method to support that. + +use Net::SMTP; +use Net::Cmd; +use MIME::Base64 qw(encode_base64); +our @ISA = qw(Net::SMTP); +our $auth_type = ::git_config 'cidaemon.smtpAuth'; + +sub new +{ + my $self = shift; + my $type = ref($self) || $self; + $type->SUPER::new(@_); +} + +sub auth +{ + my $self = shift; + return $self->SUPER::auth(@_) unless $auth_type eq 'login'; + + my $user = encode_base64 shift, ''; + my $pass = encode_base64 shift, ''; + return 0 unless CMD_MORE == $self->command("AUTH LOGIN")->response; + return 0 unless CMD_MORE == $self->command($user)->response; + CMD_OK == $self->command($pass)->response; +} + +package main; + +my ($debug_flag, %recent); + +my $ex_host = git_config('cidaemon.smtpHost') || 'localhost'; +my $ex_user = git_config('cidaemon.smtpUser'); +my $ex_pass = git_config('cidaemon.smtpPassword'); + +my $ex_from_addr = git_config('cidaemon.email', 1); +my $ex_from_name = git_config('cidaemon.name') || 'cidaemon'; + +my $scan_delay = git_config('cidaemon.scanDelay') || 60; +my $recent_size = git_config('cidaemon.recentCache') || 100; +my $tmpdir = git_config('cidaemon.tmpdir') || '/tmp'; +my $queue_name = git_config('cidaemon.queue', 1); +my $queue_lock = "$queue_name.lock"; + +my @nocc_list; +open GIT,'git config --get-all cidaemon.nocc|'; +while () { + chop; + push @nocc_list, $_; +} +close GIT; + +sub nocc_author ($) +{ + local $_ = shift; + foreach my $pat (@nocc_list) { + return 1 if /$pat/; + } + 0; +} + +sub input_echo ($) +{ + my $prompt = shift; + + local $| = 1; + print $prompt; + my $input = ; + chop $input; + return $input; +} + +sub input_noecho ($) +{ + my $prompt = shift; + + my $end = sub {system('stty','echo');print "\n";exit}; + local $SIG{TERM} = $end; + local $SIG{INT} = $end; + system('stty','-echo'); + + local $| = 1; + print $prompt; + my $input = ; + system('stty','echo'); + print "\n"; + chop $input; + return $input; +} + +sub rfc2822_date () +{ + strftime("%a, %d %b %Y %H:%M:%S %Z", localtime); +} + +sub send_email ($$$) +{ + my ($subj, $body, $to) = @_; + my $now = rfc2822_date; + my $to_str = ''; + my @rcpt_to; + foreach (@$to) { + my $s = $_; + $s =~ s/^/"/; + $s =~ s/(\s+<)/"$1/; + $to_str .= ', ' if $to_str; + $to_str .= $s; + push @rcpt_to, $1 if $s =~ /<(.*)>/; + } + die "Nobody to send to.\n" unless @rcpt_to; + my $msg = < +To: $to_str +Date: $now +Subject: $subj + +$body +EOF + + my $smtp = EXCHANGE_NET_SMTP->new(Host => $ex_host) + or die "Cannot connect to $ex_host: $!\n"; + if ($ex_user && $ex_pass) { + $smtp->auth($ex_user,$ex_pass) + or die "$ex_host rejected $ex_user\n"; + } + $smtp->mail($ex_from_addr) + or die "$ex_host rejected $ex_from_addr\n"; + scalar($smtp->recipient(@rcpt_to, { SkipBad => 1 })) + or die "$ex_host did not accept any addresses.\n"; + $smtp->data($msg) + or die "$ex_host rejected message data\n"; + $smtp->quit; +} + +sub pop_queue () +{ + open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!"; + flock LOCK, LOCK_EX; + + my $queue = -f $queue_name ? retrieve $queue_name : []; + my $ent = shift @$queue; + nstore $queue, $queue_name; + + flock LOCK, LOCK_UN; + close LOCK; + $ent; +} + +sub git_exec (@) +{ + system('git',@_) == 0 or die "Cannot git " . join(' ', @_) . "\n"; +} + +sub git_val (@) +{ + open(C, '-|','git',@_); + my $r = ; + chop $r if $r; + close C; + $r; +} + +sub do_build ($$) +{ + my ($git_dir, $new) = @_; + + my $tmp = File::Spec->catfile($tmpdir, "builder$$"); + system('rm','-rf',$tmp) == 0 or die "Cannot clear $tmp\n"; + die "Cannot clear $tmp.\n" if -e $tmp; + + my $result = 1; + eval { + my $command; + { + local $ENV{GIT_DIR} = $git_dir; + $command = git_val 'config','builder.command'; + } + die "No builder.command for $git_dir.\n" unless $command; + + git_exec 'clone','-n','-l','-s',$git_dir,$tmp; + chmod 0700, $tmp or die "Cannot lock $tmp\n"; + chdir $tmp or die "Cannot enter $tmp\n"; + + git_exec 'update-ref','HEAD',$new; + git_exec 'read-tree','-m','-u','HEAD','HEAD'; + system $command; + if ($? == -1) { + print STDERR "failed to execute '$command': $!\n"; + $result = 1; + } elsif ($? & 127) { + my $sig = $? & 127; + print STDERR "'$command' died from signal $sig\n"; + $result = 1; + } else { + my $r = $? >> 8; + print STDERR "'$command' exited with $r\n" if $r; + $result = $r; + } + }; + if ($@) { + $result = 2; + print STDERR "$@\n"; + } + + chdir '/'; + system('rm','-rf',$tmp); + rmdir $tmp; + $result; +} + +sub build_failed ($$$$$) +{ + my ($git_dir, $ref, $old, $new, $msg) = @_; + + $git_dir =~ m,/([^/]+)$,; + my $repo_name = $1; + $ref =~ s,^refs/(heads|tags)/,,; + + my %authors; + my $shortlog; + my $revstr; + { + local $ENV{GIT_DIR} = $git_dir; + my @revs = ($new); + push @revs, '--not', @$old if @$old; + open LOG,'-|','git','rev-list','--pretty=raw',@revs; + while () { + if (s/^(author|committer) //) { + chomp; + s/>.*$/>/; + $authors{$_} = 1 unless nocc_author $_; + } + } + close LOG; + open LOG,'-|','git','shortlog',@revs; + $shortlog .= $_ while ; + close LOG; + $revstr = join(' ', @revs); + } + + my @to = sort keys %authors; + unless (@to) { + print STDERR "error: No authors in $revstr\n"; + return; + } + + my $subject = "[$repo_name] $ref : Build Failed"; + my $body = <&W'); + open(STDERR, '>&W'); + exit do_build $git_dir, $new; + } else { + close W; + my $out = ''; + $out .= $_ while ; + close R; + waitpid $builder, 0; + build_failed $git_dir, $ref, $old, $new, $out if $?; + } + + print "DONE\n\n" if $debug_flag; +} + +sub daemon_loop () +{ + my $run = 1; + my $stop_sub = sub {$run = 0}; + $SIG{HUP} = $stop_sub; + $SIG{INT} = $stop_sub; + $SIG{TERM} = $stop_sub; + + mkdir $tmpdir, 0755; + my $pidfile = File::Spec->catfile($tmpdir, "cidaemon.pid"); + open(O, ">$pidfile"); print O "$$\n"; close O; + + while ($run) { + my $ent = pop_queue; + if ($ent) { + my ($git_dir, $ref, $old, $new) = @$ent; + + $ent = $recent{$git_dir}; + $recent{$git_dir} = $ent = [[], {}] unless $ent; + my ($rec_arr, $rec_hash) = @$ent; + next if $rec_hash->{$new}++; + while (@$rec_arr >= $recent_size) { + my $to_kill = shift @$rec_arr; + delete $rec_hash->{$to_kill}; + } + push @$rec_arr, $new; + + run_build $git_dir, $ref, $old, $new; + } else { + sleep $scan_delay; + } + } + + unlink $pidfile; +} + +$debug_flag = 0; +GetOptions( + 'debug|d' => \$debug_flag, + 'smtp-user=s' => \$ex_user, +) or die "usage: $0 [--debug] [--smtp-user=user]\n"; + +$ex_pass = input_noecho("$ex_user SMTP password: ") + if ($ex_user && !$ex_pass); + +if ($debug_flag) { + daemon_loop; + exit 0; +} + +my $daemon = fork(); +if (!defined $daemon) { + die "cannot fork daemon: $!"; +} elsif (0 == $daemon) { + close STDIN;open(STDIN, '/dev/null'); + close STDOUT;open(STDOUT, '>/dev/null'); + close STDERR;open(STDERR, '>/dev/null'); + daemon_loop; + exit 0; +} else { + print "Daemon $daemon running in the background.\n"; +} diff --git a/contrib/continuous/post-receive-cinotify b/contrib/continuous/post-receive-cinotify new file mode 100644 index 0000000000..b8f5a609af --- /dev/null +++ b/contrib/continuous/post-receive-cinotify @@ -0,0 +1,104 @@ +#!/usr/bin/perl +# +# A hook that notifies its companion cidaemon through a simple +# queue file that a ref has been updated via a push (actually +# by a receive-pack running on the server). +# +# See cidaemon for per-repository configuration details. +# +# To use this hook, add it as the post-receive hook, make it +# executable, and set its configuration options. +# + +local $ENV{PATH} = '/opt/git/bin'; + +use strict; +use warnings; +use File::Spec; +use Storable qw(retrieve nstore); +use Fcntl ':flock'; + +my $git_dir = File::Spec->rel2abs($ENV{GIT_DIR}); +my $queue_name = `git config --get builder.queue`;chop $queue_name; +$queue_name =~ m,^([^\s]+)$,; $queue_name = $1; # untaint +unless ($queue_name) { + 1 while ; + print STDERR "\nerror: builder.queue not set. Not enqueing.\n\n"; + exit; +} +my $queue_lock = "$queue_name.lock"; + +my @skip; +open S, "git config --get-all builder.skip|"; +while () { + chop; + push @skip, $_; +} +close S; + +my @new_branch_base; +open S, "git config --get-all builder.newBranchBase|"; +while () { + chop; + push @new_branch_base, $_; +} +close S; + +sub skip ($) +{ + local $_ = shift; + foreach my $p (@skip) { + return 1 if /^$p/; + } + 0; +} + +open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!"; +flock LOCK, LOCK_EX; + +my $queue = -f $queue_name ? retrieve $queue_name : []; +my %existing; +foreach my $r (@$queue) { + my ($gd, $ref) = @$r; + $existing{$gd}{$ref} = $r; +} + +my @new_branch_commits; +my $loaded_new_branch_commits = 0; + +while () { + chop; + my ($old, $new, $ref) = split / /, $_, 3; + + next if $old eq $new; + next if $new =~ /^0{40}$/; + next if skip $ref; + + my $r = $existing{$git_dir}{$ref}; + if ($r) { + $r->[3] = $new; + } else { + if ($old =~ /^0{40}$/) { + if (!$loaded_new_branch_commits && @new_branch_base) { + open M,'-|','git','show-ref',@new_branch_base; + while () { + ($_) = split / /, $_; + push @new_branch_commits, $_; + } + close M; + $loaded_new_branch_commits = 1; + } + $old = [@new_branch_commits]; + } else { + $old = [$old]; + } + + $r = [$git_dir, $ref, $old, $new]; + $existing{$git_dir}{$ref} = $r; + push @$queue, $r; + } +} +nstore $queue, $queue_name; + +flock LOCK, LOCK_UN; +close LOCK; -- cgit v1.2.3 From 05140f342e1df7319dd3da2ef8157bfd5760fee6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 18:32:47 +0100 Subject: sync-to-perforce is now called submit and fixed the gitdir check a little bit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index fa1b19fbbc..aca1bcaab2 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -316,7 +316,7 @@ def printUsage(commands): commands = { "debug" : P4Debug(), "clean-tags" : P4CleanTags(), - "sync-to-perforce" : P4Sync() + "submit" : P4Sync() } if len(sys.argv[1:]) == 0: @@ -350,7 +350,7 @@ if not isValidGitDir(gitdir): if isValidGitDir(gitdir + "/.git"): gitdir += "/.git" else: - dir("fatal: cannot locate git repository at %s" % gitdir) + die("fatal: cannot locate git repository at %s" % gitdir) os.environ["GIT_DIR"] = gitdir -- cgit v1.2.3 From b984733c8048423537540fdca681ddc448b60fcc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 20:54:23 +0100 Subject: Completely untested "merge" of p4-fast-export.py into git-p4.py Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 590 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 582 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index aca1bcaab2..477238fa1c 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -9,7 +9,8 @@ # import optparse, sys, os, marshal, popen2, shelve -import tempfile +import tempfile, getopt, sha, os.path, time +from sets import Set; gitdir = os.environ.get("GIT_DIR", "") @@ -51,7 +52,11 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) -class P4Debug: +class Command: + def __init__(self): + self.usage = "usage: %prog [options]" + +class P4Debug(Command): def __init__(self): self.options = [ ] @@ -60,9 +65,11 @@ class P4Debug: def run(self, args): for output in p4CmdList(" ".join(args)): print output + return True -class P4CleanTags: +class P4CleanTags(Command): def __init__(self): + Command.__init__(self) self.options = [ # optparse.make_option("--branch", dest="branch", default="refs/heads/master") ] @@ -96,9 +103,11 @@ class P4CleanTags: print os.popen("git tag -d p4/%s" % rev).read() print "%s tags removed." % len(allTags) + return True -class P4Sync: +class P4Sync(Command): def __init__(self): + Command.__init__(self) self.options = [ optparse.make_option("--continue", action="store_false", dest="firstTime"), optparse.make_option("--origin", dest="origin"), @@ -304,6 +313,566 @@ class P4Sync: system("p4 revert ... >/dev/null") os.remove(self.configFile) + return True + +class GitSync(Command): + def __init__(self): + Command.__init__(self) + self.options = [ + optparse.make_option("--branch", dest="branch"), + optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"), + optparse.make_option("--changesfile", dest="changesFile"), + optparse.make_option("--silent", dest="silent", action="store_true"), + optparse.make_option("--known-branches", dest="knownBranches"), + optparse.make_option("--cache", dest="doCache", action="store_true"), + optparse.make_option("--command-cache", dest="commandCache", action="store_true") + ] + self.description = """Imports from Perforce into a git repository.\n + example: + //depot/my/project/ -- to import the current head + //depot/my/project/@all -- to import everything + //depot/my/project/@1,6 -- to import only from revision 1 to 6 + + (a ... is not needed in the path p4 specification, it's added implicitly)""" + + self.usage += " //depot/path[@revRange]" + + self.dataCache = False + self.commandCache = False + self.silent = False + self.knownBranches = Set() + self.createdBranches = Set() + self.committedChanges = Set() + self.branch = "master" + self.detectBranches = False + self.changesFile = "" + + def p4File(self, depotPath): + return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + + def extractFilesFromCommit(self, commit): + files = [] + fnum = 0 + while commit.has_key("depotFile%s" % fnum): + path = commit["depotFile%s" % fnum] + if not path.startswith(self.globalPrefix): + # if not self.silent: + # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.globalPrefix, change) + fnum = fnum + 1 + continue + + file = {} + file["path"] = path + file["rev"] = commit["rev%s" % fnum] + file["action"] = commit["action%s" % fnum] + file["type"] = commit["type%s" % fnum] + files.append(file) + fnum = fnum + 1 + return files + + def isSubPathOf(self, first, second): + if not first.startswith(second): + return False + if first == second: + return True + return first[len(second)] == "/" + + def branchesForCommit(self, files): + branches = Set() + + for file in files: + relativePath = file["path"][len(self.globalPrefix):] + # strip off the filename + relativePath = relativePath[0:relativePath.rfind("/")] + + # if len(branches) == 0: + # branches.add(relativePath) + # knownBranches.add(relativePath) + # continue + + ###### this needs more testing :) + knownBranch = False + for branch in branches: + if relativePath == branch: + knownBranch = True + break + # if relativePath.startswith(branch): + if self.isSubPathOf(relativePath, branch): + knownBranch = True + break + # if branch.startswith(relativePath): + if self.isSubPathOf(branch, relativePath): + branches.remove(branch) + break + + if knownBranch: + continue + + for branch in knownBranches: + #if relativePath.startswith(branch): + if self.isSubPathOf(relativePath, branch): + if len(branches) == 0: + relativePath = branch + else: + knownBranch = True + break + + if knownBranch: + continue + + branches.add(relativePath) + self.knownBranches.add(relativePath) + + return branches + + def findBranchParent(self, branchPrefix, files): + for file in files: + path = file["path"] + if not path.startswith(branchPrefix): + continue + action = file["action"] + if action != "integrate" and action != "branch": + continue + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + branchAction = log["how0,0"] + # if branchAction == "branch into" or branchAction == "ignored": + # continue # ignore for branching + + if not branchAction.endswith(" from"): + continue # ignore for branching + # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) + # sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + lastSourceRev = log["erev0,0"] + + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + relPath = source[len(self.globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] + + for branch in self.knownBranches: + if self.isSubPathOf(relPath, branch): + # print "determined parent branch branch %s due to change in file %s" % (branch, source) + return branch + # else: + # print "%s is not a subpath of branch %s" % (relPath, branch) + + return "" + + def commit(self, details, files, branch, branchPrefix, parent, merged = ""): + epoch = details["time"] + author = details["user"] + + self.gitStream.write("commit %s\n" % branch) + # gitStream.write("mark :%s\n" % details["change"]) + self.committedChanges.add(int(details["change"])) + committer = "" + if author in self.users: + committer = "%s %s %s" % (self.users[author], epoch, tz) + else: + committer = "%s %s %s" % (author, epoch, tz) + + self.gitStream.write("committer %s\n" % committer) + + self.gitStream.write("data < 0: + self.gitStream.write("from %s\n" % parent) + + if len(merged) > 0: + self.gitStream.write("merge %s\n" % merged) + + for file in files: + path = file["path"] + if not path.startswith(branchPrefix): + # if not silent: + # print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) + continue + rev = file["rev"] + depotPath = path + "#" + rev + relPath = path[len(branchPrefix):] + action = file["action"] + + if file["type"] == "apple": + print "\nfile %s is a strange apple file that forks. Ignoring!" % path + continue + + if action == "delete": + self.gitStream.write("D %s\n" % relPath) + else: + mode = 644 + if file["type"].startswith("x"): + mode = 755 + + data = self.p4File(depotPath) + + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) + self.gitStream.write("data %s\n" % len(data)) + self.gitStream.write(data) + self.gitStream.write("\n") + + self.gitStream.write("\n") + + self.lastChange = int(details["change"]) + + def extractFilesInCommitToBranch(self, files, branchPrefix): + newFiles = [] + + for file in files: + path = file["path"] + if path.startswith(branchPrefix): + newFiles.append(file) + + return newFiles + + def findBranchSourceHeuristic(self, files, branch, branchPrefix): + for file in files: + action = file["action"] + if action != "integrate" and action != "branch": + continue + path = file["path"] + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + branchAction = log["how0,0"] + + if not branchAction.endswith(" from"): + continue # ignore for branching + # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) + # sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + lastSourceRev = log["erev0,0"] + + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + relPath = source[len(self.globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] + + for candidate in self.knownBranches: + if self.isSubPathOf(relPath, candidate) and candidate != branch: + return candidate + + return "" + + def changeIsBranchMerge(self, sourceBranch, destinationBranch, change): + sourceFiles = {} + for file in p4CmdList("files %s...@%s" % (self.globalPrefix + sourceBranch + "/", change)): + if file["action"] == "delete": + continue + sourceFiles[file["depotFile"]] = file + + destinationFiles = {} + for file in p4CmdList("files %s...@%s" % (self.globalPrefix + destinationBranch + "/", change)): + destinationFiles[file["depotFile"]] = file + + for fileName in sourceFiles.keys(): + integrations = [] + deleted = False + integrationCount = 0 + for integration in p4CmdList("integrated \"%s\"" % fileName): + toFile = integration["fromFile"] # yes, it's true, it's fromFile + if not toFile in destinationFiles: + continue + destFile = destinationFiles[toFile] + if destFile["action"] == "delete": + # print "file %s has been deleted in %s" % (fileName, toFile) + deleted = True + break + integrationCount += 1 + if integration["how"] == "branch from": + continue + + if int(integration["change"]) == change: + integrations.append(integration) + continue + if int(integration["change"]) > change: + continue + + destRev = int(destFile["rev"]) + + startRev = integration["startFromRev"][1:] + if startRev == "none": + startRev = 0 + else: + startRev = int(startRev) + + endRev = integration["endFromRev"][1:] + if endRev == "none": + endRev = 0 + else: + endRev = int(endRev) + + initialBranch = (destRev == 1 and integration["how"] != "branch into") + inRange = (destRev >= startRev and destRev <= endRev) + newer = (destRev > startRev and destRev > endRev) + + if initialBranch or inRange or newer: + integrations.append(integration) + + if deleted: + continue + + if len(integrations) == 0 and integrationCount > 1: + print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) + return False + + return True + + def getUserMap(self): + self.users = {} + + for output in p4CmdList("users"): + if not output.has_key("User"): + continue + self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" + + def run(self, args): + self.branch = "refs/heads/" + self.branch + self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() + if len(self.globalPrefix) != 0: + self.globalPrefix = self.globalPrefix[:-1] + + if len(args) == 0 and len(self.globalPrefix) != 0: + if not self.silent: + print "[using previously specified depot path %s]" % self.globalPrefix + elif len(args) != 1: + return False + else: + if len(self.globalPrefix) != 0 and self.globalPrefix != args[0]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.globalPrefix, args[0]) + sys.exit(1) + self.globalPrefix = args[0] + + self.changeRange = "" + self.revision = "" + self.users = {} + self.initialParent = "" + self.lastChange = 0 + self.initialTag = "" + + if self.globalPrefix.find("@") != -1: + atIdx = self.globalPrefix.index("@") + self.changeRange = self.globalPrefix[atIdx:] + if self.changeRange == "@all": + self.changeRange = "" + elif self.changeRange.find(",") == -1: + self.revision = self.changeRange + self.changeRange = "" + self.globalPrefix = self.globalPrefix[0:atIdx] + elif self.globalPrefix.find("#") != -1: + hashIdx = self.globalPrefix.index("#") + self.revision = self.globalPrefix[hashIdx:] + self.globalPrefix = self.globalPrefix[0:hashIdx] + elif len(self.previousDepotPath) == 0: + self.revision = "#head" + + if self.globalPrefix.endswith("..."): + self.globalPrefix = self.globalPrefix[:-3] + + if not self.globalPrefix.endswith("/"): + self.globalPrefix += "/" + + self.getUserMap() + + if len(self.changeRange) == 0: + try: + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % self.branch) + output = sout.read() + if output.endswith("\n"): + output = output[:-1] + tagIdx = output.index(" tags/p4/") + caretIdx = output.find("^") + endPos = len(output) + if caretIdx != -1: + endPos = caretIdx + self.rev = int(output[tagIdx + 9 : endPos]) + 1 + self.changeRange = "@%s,#head" % self.rev + self.initialParent = os.popen("git-rev-parse %s" % self.branch).read()[:-1] + self.initialTag = "p4/%s" % (int(self.rev) - 1) + except: + pass + + tz = - time.timezone / 36 + tzsign = ("%s" % tz)[0] + if tzsign != '+' and tzsign != '-': + tz = "+" + ("%s" % tz) + + self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git-fast-import") + + if len(self.revision) > 0: + print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) + + details = { "user" : "git perforce import user", "time" : int(time.time()) } + details["desc"] = "Initial import of %s from the state at revision %s" % (self.globalPrefix, self.revision) + details["change"] = self.revision + newestRevision = 0 + + fileCnt = 0 + for info in p4CmdList("files %s...%s" % (self.globalPrefix, self.revision)): + change = int(info["change"]) + if change > newestRevision: + newestRevision = change + + if info["action"] == "delete": + continue + + for prop in [ "depotFile", "rev", "action", "type" ]: + details["%s%s" % (prop, fileCnt)] = info[prop] + + fileCnt = fileCnt + 1 + + details["change"] = newestRevision + + try: + self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) + except: + print self.gitError.read() + + else: + changes = [] + + if len(changesFile) > 0: + output = open(self.changesFile).readlines() + changeSet = Set() + for line in output: + changeSet.add(int(line)) + + for change in changeSet: + changes.append(change) + + changes.sort() + else: + output = os.popen("p4 changes %s...%s" % (self.globalPrefix, self.changeRange)).readlines() + + for line in output: + changeNum = line.split(" ")[1] + changes.append(changeNum) + + changes.reverse() + + if len(changes) == 0: + if not silent: + print "no changes to import!" + sys.exit(1) + + cnt = 1 + for change in changes: + description = p4Cmd("describe %s" % change) + + if not silent: + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() + cnt = cnt + 1 + + try: + files = self.extractFilesFromCommit(description) + if self.detectBranches: + for branch in self.branchesForCommit(files): + self.knownBranches.add(branch) + branchPrefix = self.globalPrefix + branch + "/" + + filesForCommit = self.extractFilesInCommitToBranch(files, branchPrefix) + + merged = "" + parent = "" + ########### remove cnt!!! + if branch not in self.createdBranches and cnt > 2: + self.createdBranches.add(branch) + parent = self.findBranchParent(branchPrefix, files) + if parent == branch: + parent = "" + # elif len(parent) > 0: + # print "%s branched off of %s" % (branch, parent) + + if len(parent) == 0: + merged = self.findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) + if len(merged) > 0: + print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) + if not self.changeIsBranchMerge(merged, branch, int(description["change"])): + merged = "" + + branch = "refs/heads/" + branch + if len(parent) > 0: + parent = "refs/heads/" + parent + if len(merged) > 0: + merged = "refs/heads/" + merged + self.commit(description, files, branch, branchPrefix, parent, merged) + else: + self.commit(description, files, branch, globalPrefix, initialParent) + self.initialParent = "" + except IOError: + print self.gitError.read() + sys.exit(1) + + if not self.silent: + print "" + + self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) + self.gitStream.write("from %s\n\n" % self.branch); + + + self.gitStream.close() + self.gitOutput.close() + self.gitError.close() + + os.popen("git-repo-config p4.depotpath %s" % self.globalPrefix).read() + if len(self.initialTag) > 0: + os.popen("git tag -d %s" % self.initialTag).read() + + return True + +class HelpFormatter(optparse.IndentedHelpFormatter): + def __init__(self): + optparse.IndentedHelpFormatter.__init__(self) + + def format_description(self, description): + if description: + return description + "\n" + else: + return "" def printUsage(commands): print "usage: %s [options]" % sys.argv[0] @@ -316,7 +885,8 @@ def printUsage(commands): commands = { "debug" : P4Debug(), "clean-tags" : P4CleanTags(), - "submit" : P4Sync() + "submit" : P4Sync(), + "sync" : GitSync() } if len(sys.argv[1:]) == 0: @@ -337,8 +907,10 @@ options = cmd.options cmd.gitdir = gitdir options.append(optparse.make_option("--git-dir", dest="gitdir")) -parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", options, - description = cmd.description) +parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), + options, + description = cmd.description, + formatter = HelpFormatter()) (cmd, args) = parser.parse_args(sys.argv[2:], cmd); @@ -354,4 +926,6 @@ if not isValidGitDir(gitdir): os.environ["GIT_DIR"] = gitdir -cmd.run(args) +if not cmd.run(args): + parser.print_help() + -- cgit v1.2.3 From 0828ab140360c4c831e112f8d244923a7f032e74 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 20:59:30 +0100 Subject: Added missing "self"s to make the script evaluate correctly. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 477238fa1c..8cb63f9540 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -489,9 +489,9 @@ class GitSync(Command): self.committedChanges.add(int(details["change"])) committer = "" if author in self.users: - committer = "%s %s %s" % (self.users[author], epoch, tz) + committer = "%s %s %s" % (self.users[author], epoch, self.tz) else: - committer = "%s %s %s" % (author, epoch, tz) + committer = "%s %s %s" % (author, epoch, self.tz) self.gitStream.write("committer %s\n" % committer) @@ -735,10 +735,10 @@ class GitSync(Command): except: pass - tz = - time.timezone / 36 - tzsign = ("%s" % tz)[0] + self.tz = - time.timezone / 36 + tzsign = ("%s" % self.tz)[0] if tzsign != '+' and tzsign != '-': - tz = "+" + ("%s" % tz) + self.tz = "+" + ("%s" % self.tz) self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git-fast-import") @@ -774,7 +774,7 @@ class GitSync(Command): else: changes = [] - if len(changesFile) > 0: + if len(self.changesFile) > 0: output = open(self.changesFile).readlines() changeSet = Set() for line in output: @@ -794,7 +794,7 @@ class GitSync(Command): changes.reverse() if len(changes) == 0: - if not silent: + if not self.silent: print "no changes to import!" sys.exit(1) @@ -802,7 +802,7 @@ class GitSync(Command): for change in changes: description = p4Cmd("describe %s" % change) - if not silent: + if not self.silent: sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) sys.stdout.flush() cnt = cnt + 1 @@ -841,7 +841,7 @@ class GitSync(Command): merged = "refs/heads/" + merged self.commit(description, files, branch, branchPrefix, parent, merged) else: - self.commit(description, files, branch, globalPrefix, initialParent) + self.commit(description, files, self.branch, self.globalPrefix, self.initialParent) self.initialParent = "" except IOError: print self.gitError.read() -- cgit v1.2.3 From c715706b15426c7b106677868ce48e4bd78b94d6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 21:13:49 +0100 Subject: Fixed the initial version import by getting the file index correct by correctly skipping deleted files. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 8cb63f9540..54ddf73dac 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -480,7 +480,7 @@ class GitSync(Command): return "" - def commit(self, details, files, branch, branchPrefix, parent, merged = ""): + def commit(self, details, files, branch, branchPrefix, parent = "", merged = ""): epoch = details["time"] author = details["user"] @@ -757,6 +757,7 @@ class GitSync(Command): newestRevision = change if info["action"] == "delete": + fileCnt = fileCnt + 1 continue for prop in [ "depotFile", "rev", "action", "type" ]: @@ -768,7 +769,7 @@ class GitSync(Command): try: self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) - except: + except IOError: print self.gitError.read() else: -- cgit v1.2.3 From c5fdcbcc20ebf0716fd17e3ac4f73baacf38a699 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 22:09:27 +0100 Subject: Removed p4-fast-export and p4-git-sync as they've been integrated into git-p4 now. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 632 ---------------------------------- contrib/fast-import/p4-git-sync.py | 296 ---------------- 2 files changed, 928 deletions(-) delete mode 100755 contrib/fast-import/p4-fast-export.py delete mode 100755 contrib/fast-import/p4-git-sync.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py deleted file mode 100755 index 9adb88fade..0000000000 --- a/contrib/fast-import/p4-fast-export.py +++ /dev/null @@ -1,632 +0,0 @@ -#!/usr/bin/env python -# -# p4-fast-export.py -# -# Author: Simon Hausmann -# License: MIT -# -# TODO: -# - support integrations (at least p4i) -# - support p4 submit (hah!) -# - emulate p4's delete behavior: if a directory becomes empty delete it. continue -# with parent dir until non-empty dir is found. -# -import os, string, sys, time, os.path -import marshal, popen2, getopt, sha -from sets import Set; - -dataCache = False -commandCache = False - -silent = False -knownBranches = Set() -createdBranches = Set() -committedChanges = Set() -branch = "refs/heads/master" -globalPrefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() -detectBranches = False -changesFile = "" -if len(globalPrefix) != 0: - globalPrefix = globalPrefix[:-1] - -try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent", "known-branches=", - "cache", "command-cache" ]) -except getopt.GetoptError: - print "fixme, syntax error" - sys.exit(1) - -for o, a in opts: - if o == "--branch": - branch = "refs/heads/" + a - elif o == "--detect-branches": - detectBranches = True - elif o == "--changesfile": - changesFile = a - elif o == "--silent": - silent= True - elif o == "--known-branches": - for branch in open(a).readlines(): - knownBranches.add(branch[:-1]) - elif o == "--cache": - dataCache = True - commandCache = True - elif o == "--command-cache": - commandCache = True - -if len(args) == 0 and len(globalPrefix) != 0: - if not silent: - print "[using previously specified depot path %s]" % globalPrefix -elif len(args) != 1: - print "usage: %s //depot/path[@revRange]" % sys.argv[0] - print "\n example:" - print " %s //depot/my/project/ -- to import the current head" - print " %s //depot/my/project/@all -- to import everything" - print " %s //depot/my/project/@1,6 -- to import only from revision 1 to 6" - print "" - print " (a ... is not needed in the path p4 specification, it's added implicitly)" - print "" - sys.exit(1) -else: - if len(globalPrefix) != 0 and globalPrefix != args[0]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (globalPrefix, args[0]) - sys.exit(1) - globalPrefix = args[0] - -changeRange = "" -revision = "" -users = {} -initialParent = "" -lastChange = 0 -initialTag = "" - -if globalPrefix.find("@") != -1: - atIdx = globalPrefix.index("@") - changeRange = globalPrefix[atIdx:] - if changeRange == "@all": - changeRange = "" - elif changeRange.find(",") == -1: - revision = changeRange - changeRange = "" - globalPrefix = globalPrefix[0:atIdx] -elif globalPrefix.find("#") != -1: - hashIdx = globalPrefix.index("#") - revision = globalPrefix[hashIdx:] - globalPrefix = globalPrefix[0:hashIdx] -elif len(previousDepotPath) == 0: - revision = "#head" - -if globalPrefix.endswith("..."): - globalPrefix = globalPrefix[:-3] - -if not globalPrefix.endswith("/"): - globalPrefix += "/" - -def p4File(depotPath): - cacheKey = "/tmp/p4cache/data-" + sha.new(depotPath).hexdigest() - - data = 0 - try: - if not dataCache: - raise - data = open(cacheKey, "rb").read() - except: - data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() - if dataCache: - open(cacheKey, "wb").write(data) - - return data - -def p4CmdList(cmd): - fullCmd = "p4 -G %s" % cmd; - - cacheKey = sha.new(fullCmd).hexdigest() - cacheKey = "/tmp/p4cache/cmd-" + cacheKey - - cached = True - pipe = 0 - try: - if not commandCache: - raise - pipe = open(cacheKey, "rb") - except: - cached = False - pipe = os.popen(fullCmd, "rb") - - result = [] - try: - while True: - entry = marshal.load(pipe) - result.append(entry) - except EOFError: - pass - pipe.close() - - if not cached and commandCache: - pipe = open(cacheKey, "wb") - for r in result: - marshal.dump(r, pipe) - pipe.close() - - return result - -def p4Cmd(cmd): - list = p4CmdList(cmd) - result = {} - for entry in list: - result.update(entry) - return result; - -def extractFilesFromCommit(commit): - files = [] - fnum = 0 - while commit.has_key("depotFile%s" % fnum): - path = commit["depotFile%s" % fnum] - if not path.startswith(globalPrefix): -# if not silent: -# print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) - fnum = fnum + 1 - continue - - file = {} - file["path"] = path - file["rev"] = commit["rev%s" % fnum] - file["action"] = commit["action%s" % fnum] - file["type"] = commit["type%s" % fnum] - files.append(file) - fnum = fnum + 1 - return files - -def isSubPathOf(first, second): - if not first.startswith(second): - return False - if first == second: - return True - return first[len(second)] == "/" - -def branchesForCommit(files): - global knownBranches - branches = Set() - - for file in files: - relativePath = file["path"][len(globalPrefix):] - # strip off the filename - relativePath = relativePath[0:relativePath.rfind("/")] - -# if len(branches) == 0: -# branches.add(relativePath) -# knownBranches.add(relativePath) -# continue - - ###### this needs more testing :) - knownBranch = False - for branch in branches: - if relativePath == branch: - knownBranch = True - break -# if relativePath.startswith(branch): - if isSubPathOf(relativePath, branch): - knownBranch = True - break -# if branch.startswith(relativePath): - if isSubPathOf(branch, relativePath): - branches.remove(branch) - break - - if knownBranch: - continue - - for branch in knownBranches: - #if relativePath.startswith(branch): - if isSubPathOf(relativePath, branch): - if len(branches) == 0: - relativePath = branch - else: - knownBranch = True - break - - if knownBranch: - continue - - branches.add(relativePath) - knownBranches.add(relativePath) - - return branches - -def findBranchParent(branchPrefix, files): - for file in files: - path = file["path"] - if not path.startswith(branchPrefix): - continue - action = file["action"] - if action != "integrate" and action != "branch": - continue - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] -# if branchAction == "branch into" or branchAction == "ignored": -# continue # ignore for branching - - if not branchAction.endswith(" from"): - continue # ignore for branching -# print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) -# sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(globalPrefix):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for branch in knownBranches: - if isSubPathOf(relPath, branch): -# print "determined parent branch branch %s due to change in file %s" % (branch, source) - return branch -# else: -# print "%s is not a subpath of branch %s" % (relPath, branch) - - return "" - -def commit(details, files, branch, branchPrefix, parent, merged = ""): - global users - global lastChange - global committedChanges - - epoch = details["time"] - author = details["user"] - - gitStream.write("commit %s\n" % branch) -# gitStream.write("mark :%s\n" % details["change"]) - committedChanges.add(int(details["change"])) - committer = "" - if author in users: - committer = "%s %s %s" % (users[author], epoch, tz) - else: - committer = "%s %s %s" % (author, epoch, tz) - - gitStream.write("committer %s\n" % committer) - - gitStream.write("data < 0: - gitStream.write("from %s\n" % parent) - - if len(merged) > 0: - gitStream.write("merge %s\n" % merged) - - for file in files: - path = file["path"] - if not path.startswith(branchPrefix): -# if not silent: -# print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) - continue - rev = file["rev"] - depotPath = path + "#" + rev - relPath = path[len(branchPrefix):] - action = file["action"] - - if file["type"] == "apple": - print "\nfile %s is a strange apple file that forks. Ignoring!" % path - continue - - if action == "delete": - gitStream.write("D %s\n" % relPath) - else: - mode = 644 - if file["type"].startswith("x"): - mode = 755 - - data = p4File(depotPath) - - gitStream.write("M %s inline %s\n" % (mode, relPath)) - gitStream.write("data %s\n" % len(data)) - gitStream.write(data) - gitStream.write("\n") - - gitStream.write("\n") - - lastChange = int(details["change"]) - -def extractFilesInCommitToBranch(files, branchPrefix): - newFiles = [] - - for file in files: - path = file["path"] - if path.startswith(branchPrefix): - newFiles.append(file) - - return newFiles - -def findBranchSourceHeuristic(files, branch, branchPrefix): - for file in files: - action = file["action"] - if action != "integrate" and action != "branch": - continue - path = file["path"] - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] - - if not branchAction.endswith(" from"): - continue # ignore for branching -# print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) -# sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(globalPrefix):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for candidate in knownBranches: - if isSubPathOf(relPath, candidate) and candidate != branch: - return candidate - - return "" - -def changeIsBranchMerge(sourceBranch, destinationBranch, change): - sourceFiles = {} - for file in p4CmdList("files %s...@%s" % (globalPrefix + sourceBranch + "/", change)): - if file["action"] == "delete": - continue - sourceFiles[file["depotFile"]] = file - - destinationFiles = {} - for file in p4CmdList("files %s...@%s" % (globalPrefix + destinationBranch + "/", change)): - destinationFiles[file["depotFile"]] = file - - for fileName in sourceFiles.keys(): - integrations = [] - deleted = False - integrationCount = 0 - for integration in p4CmdList("integrated \"%s\"" % fileName): - toFile = integration["fromFile"] # yes, it's true, it's fromFile - if not toFile in destinationFiles: - continue - destFile = destinationFiles[toFile] - if destFile["action"] == "delete": -# print "file %s has been deleted in %s" % (fileName, toFile) - deleted = True - break - integrationCount += 1 - if integration["how"] == "branch from": - continue - - if int(integration["change"]) == change: - integrations.append(integration) - continue - if int(integration["change"]) > change: - continue - - destRev = int(destFile["rev"]) - - startRev = integration["startFromRev"][1:] - if startRev == "none": - startRev = 0 - else: - startRev = int(startRev) - - endRev = integration["endFromRev"][1:] - if endRev == "none": - endRev = 0 - else: - endRev = int(endRev) - - initialBranch = (destRev == 1 and integration["how"] != "branch into") - inRange = (destRev >= startRev and destRev <= endRev) - newer = (destRev > startRev and destRev > endRev) - - if initialBranch or inRange or newer: - integrations.append(integration) - - if deleted: - continue - - if len(integrations) == 0 and integrationCount > 1: - print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) - return False - - return True - -def getUserMap(): - users = {} - - for output in p4CmdList("users"): - if not output.has_key("User"): - continue - users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" - return users - -users = getUserMap() - -if len(changeRange) == 0: - try: - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) - output = sout.read() - if output.endswith("\n"): - output = output[:-1] - tagIdx = output.index(" tags/p4/") - caretIdx = output.find("^") - endPos = len(output) - if caretIdx != -1: - endPos = caretIdx - rev = int(output[tagIdx + 9 : endPos]) + 1 - changeRange = "@%s,#head" % rev - initialParent = os.popen("git-rev-parse %s" % branch).read()[:-1] - initialTag = "p4/%s" % (int(rev) - 1) - except: - pass - -tz = - time.timezone / 36 -tzsign = ("%s" % tz)[0] -if tzsign != '+' and tzsign != '-': - tz = "+" + ("%s" % tz) - -gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") - -if len(revision) > 0: - print "Doing initial import of %s from revision %s" % (globalPrefix, revision) - - details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = "Initial import of %s from the state at revision %s" % (globalPrefix, revision) - details["change"] = revision - newestRevision = 0 - - fileCnt = 0 - for info in p4CmdList("files %s...%s" % (globalPrefix, revision)): - change = int(info["change"]) - if change > newestRevision: - newestRevision = change - - if info["action"] == "delete": - continue - - for prop in [ "depotFile", "rev", "action", "type" ]: - details["%s%s" % (prop, fileCnt)] = info[prop] - - fileCnt = fileCnt + 1 - - details["change"] = newestRevision - - try: - commit(details, extractFilesFromCommit(details), branch, globalPrefix) - except: - print gitError.read() - -else: - changes = [] - - if len(changesFile) > 0: - output = open(changesFile).readlines() - changeSet = Set() - for line in output: - changeSet.add(int(line)) - - for change in changeSet: - changes.append(change) - - changes.sort() - else: - output = os.popen("p4 changes %s...%s" % (globalPrefix, changeRange)).readlines() - - for line in output: - changeNum = line.split(" ")[1] - changes.append(changeNum) - - changes.reverse() - - if len(changes) == 0: - if not silent: - print "no changes to import!" - sys.exit(1) - - cnt = 1 - for change in changes: - description = p4Cmd("describe %s" % change) - - if not silent: - sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) - sys.stdout.flush() - cnt = cnt + 1 - - try: - files = extractFilesFromCommit(description) - if detectBranches: - for branch in branchesForCommit(files): - knownBranches.add(branch) - branchPrefix = globalPrefix + branch + "/" - - filesForCommit = extractFilesInCommitToBranch(files, branchPrefix) - - merged = "" - parent = "" - ########### remove cnt!!! - if branch not in createdBranches and cnt > 2: - createdBranches.add(branch) - parent = findBranchParent(branchPrefix, files) - if parent == branch: - parent = "" - # elif len(parent) > 0: - # print "%s branched off of %s" % (branch, parent) - - if len(parent) == 0: - merged = findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) - if len(merged) > 0: - print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) - if not changeIsBranchMerge(merged, branch, int(description["change"])): - merged = "" - - branch = "refs/heads/" + branch - if len(parent) > 0: - parent = "refs/heads/" + parent - if len(merged) > 0: - merged = "refs/heads/" + merged - commit(description, files, branch, branchPrefix, parent, merged) - else: - commit(description, files, branch, globalPrefix, initialParent) - initialParent = "" - except IOError: - print gitError.read() - sys.exit(1) - -if not silent: - print "" - -gitStream.write("reset refs/tags/p4/%s\n" % lastChange) -gitStream.write("from %s\n\n" % branch); - - -gitStream.close() -gitOutput.close() -gitError.close() - -os.popen("git-repo-config p4.depotpath %s" % globalPrefix).read() -if len(initialTag) > 0: - os.popen("git tag -d %s" % initialTag).read() - -sys.exit(0) diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py deleted file mode 100755 index 5a3bf90485..0000000000 --- a/contrib/fast-import/p4-git-sync.py +++ /dev/null @@ -1,296 +0,0 @@ -#!/usr/bin/env python -# -# p4-git-sync.py -# -# Author: Simon Hausmann -# Copyright: 2007 Simon Hausmann -# 2007 Trolltech ASA -# License: MIT -# - -import os, string, shelve, stat -import getopt, sys, marshal, tempfile - -def p4CmdList(cmd): - cmd = "p4 -G %s" % cmd - pipe = os.popen(cmd, "rb") - - result = [] - try: - while True: - entry = marshal.load(pipe) - result.append(entry) - except EOFError: - pass - pipe.close() - - return result - -def p4Cmd(cmd): - list = p4CmdList(cmd) - result = {} - for entry in list: - result.update(entry) - return result; - -def die(msg): - sys.stderr.write(msg + "\n") - sys.exit(1) - -def tryGitDir(path): - if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): - return True; - return False - -try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", - "submit-log-subst=", "log-substitutions=", "noninteractive", - "dry-run" ]) -except getopt.GetoptError: - print "fixme, syntax error" - sys.exit(1) - -logSubstitutions = {} -logSubstitutions[""] = "%log%" -logSubstitutions["\tDetails:"] = "\tDetails: %log%" -gitdir = os.environ.get("GIT_DIR", "") -origin = "origin" -master = "" -firstTime = True -reset = False -interactive = True -dryRun = False - -for o, a in opts: - if o == "--git-dir": - gitdir = a - elif o == "--origin": - origin = a - elif o == "--master": - master = a - elif o == "--continue": - firstTime = False - elif o == "--reset": - reset = True - firstTime = True - elif o == "--submit-log-subst": - key = a.split("%")[0] - value = a.split("%")[1] - logSubstitutions[key] = value - elif o == "--log-substitutions": - for line in open(a, "r").readlines(): - tokens = line[:-1].split("=") - logSubstitutions[tokens[0]] = tokens[1] - elif o == "--noninteractive": - interactive = False - elif o == "--dry-run": - dryRun = True - -if len(gitdir) == 0: - gitdir = ".git" -else: - os.environ["GIT_DIR"] = gitdir - -if not tryGitDir(gitdir): - if tryGitDir(gitdir + "/.git"): - gitdir += "/.git" - os.environ["GIT_DIR"] = gitdir - else: - die("fatal: %s seems not to be a git repository." % gitdir) - - -configFile = gitdir + "/p4-git-sync.cfg" - -origin = "origin" -if len(args) == 1: - origin = args[0] - -if len(master) == 0: - sys.stdout.write("Auto-detecting current branch: ") - master = os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] - if len(master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, master)): - die("\nFailed to detect current branch! Aborting!"); - sys.stdout.write("%s\n" % master) - -def system(cmd): - if os.system(cmd) != 0: - die("command failed: %s" % cmd) - -def check(): - if len(p4CmdList("opened ...")) > 0: - die("You have files opened with perforce! Close them before starting the sync.") - -def start(config): - if len(config) > 0 and not reset: - die("Cannot start sync. Previous sync config found at %s" % configFile) - - #if len(os.popen("git-update-index --refresh").read()) > 0: - # die("Your working tree is not clean. Check with git status!") - - commits = [] - for line in os.popen("git-rev-list --no-merges %s..%s" % (origin, master)).readlines(): - commits.append(line[:-1]) - commits.reverse() - - config["commits"] = commits - - print "Creating temporary p4-sync branch from %s ..." % origin - system("git checkout -f -b p4-sync %s" % origin) - -# print "Cleaning index..." -# system("git checkout -f") - -def prepareLogMessage(template, message): - result = "" - - for line in template.split("\n"): - if line.startswith("#"): - result += line + "\n" - continue - - substituted = False - for key in logSubstitutions.keys(): - if line.find(key) != -1: - value = logSubstitutions[key] - value = value.replace("%log%", message) - if value != "@remove@": - result += line.replace(key, value) + "\n" - substituted = True - break - - if not substituted: - result += line + "\n" - - return result - -def apply(id): - global interactive - print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) - diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() - filesToAdd = set() - filesToDelete = set() - for line in diff: - modifier = line[0] - path = line[1:].strip() - if modifier == "M": - system("p4 edit %s" % path) - elif modifier == "A": - filesToAdd.add(path) - if path in filesToDelete: - filesToDelete.remove(path) - elif modifier == "D": - filesToDelete.add(path) - if path in filesToAdd: - filesToAdd.remove(path) - else: - die("unknown modifier %s for %s" % (modifier, path)) - - system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") - system("git cherry-pick --no-commit \"%s\"" % id) - #system("git format-patch --stdout -k \"%s^\"..\"%s\" | git-am -k" % (id, id)) - #system("git branch -D tmp") - #system("git checkout -f -b tmp \"%s^\"" % id) - - for f in filesToAdd: - system("p4 add %s" % f) - for f in filesToDelete: - system("p4 revert %s" % f) - system("p4 delete %s" % f) - - logMessage = "" - foundTitle = False - for log in os.popen("git-cat-file commit %s" % id).readlines(): - if not foundTitle: - if len(log) == 1: - foundTitle = 1 - continue - - if len(logMessage) > 0: - logMessage += "\t" - logMessage += log - - template = os.popen("p4 change -o").read() - - if interactive: - submitTemplate = prepareLogMessage(template, logMessage) - diff = os.popen("p4 diff -du ...").read() - - for newFile in filesToAdd: - diff += "==== new file ====\n" - diff += "--- /dev/null\n" - diff += "+++ %s\n" % newFile - f = open(newFile, "r") - for line in f.readlines(): - diff += "+" + line - f.close() - - pipe = os.popen("less", "w") - pipe.write(submitTemplate + diff) - pipe.close() - - response = "e" - while response == "e": - response = raw_input("Do you want to submit this change (y/e/n)? ") - if response == "e": - [handle, fileName] = tempfile.mkstemp() - tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate) - tmpFile.close() - editor = os.environ.get("EDITOR", "vi") - system(editor + " " + fileName) - tmpFile = open(fileName, "r") - submitTemplate = tmpFile.read() - tmpFile.close() - os.remove(fileName) - - if response == "y" or response == "yes": - if dryRun: - print submitTemplate - raw_input("Press return to continue...") - else: - pipe = os.popen("p4 submit -i", "w") - pipe.write(submitTemplate) - pipe.close() - else: - print "Not submitting!" - interactive = False - else: - fileName = "submit.txt" - file = open(fileName, "w+") - file.write(prepareLogMessage(template, logMessage)) - file.close() - print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) - -check() - -config = shelve.open(configFile, writeback=True) - -if firstTime: - start(config) - -commits = config.get("commits", []) - -while len(commits) > 0: - firstTime = False - commit = commits[0] - commits = commits[1:] - config["commits"] = commits - apply(commit) - if not interactive: - break - -config.close() - -if len(commits) == 0: - if firstTime: - print "No changes found to apply between %s and current HEAD" % origin - else: - print "All changes applied!" - print "Deleting temporary p4-sync branch and going back to %s" % master - system("git checkout %s" % master) - system("git branch -D p4-sync") - print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." - system("p4 edit ... >/dev/null") - system("p4 revert ... >/dev/null") - os.remove(configFile) - -- cgit v1.2.3 From 0b69b469257424bcf015b7d3d03303c015c133f8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 22:41:00 +0100 Subject: Start of the git-p4 documentation. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 74 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 contrib/fast-import/git-p4.txt (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt new file mode 100644 index 0000000000..b87efc6af9 --- /dev/null +++ b/contrib/fast-import/git-p4.txt @@ -0,0 +1,74 @@ +git-p4.py - Perforce <-> Git converter using git-fast-import + +Usage +===== + +git-p4 supports two main modes: Importing from Perforce to a Git repository is +done using "git-p4.py sync". Submitting changes from Git back to Perforce is +done using "git-p4.py submit". + +Importing +========= + +The procedure is simple: + + mkdir repo-git + cd repo-git + git init + git-p4.py sync //path/in/your/perforce/depot + +This will import the current head revision of the specified depot path into the +master branch of your git repository. You can use the --branch=mybranch option +to let git-p4 import from Perforce into a git branch of your choice. + +If you want to import the entire history of a given depot path just use + + git-p4.py sync //path/in/depot@all + + +Support for Perforce integrations is still work in progress. Don't bother +trying it unless you want to hack on it :) + + +Incremental Imports +=================== + +After an initial import you can easily synchronize your git repository with +newer changes from the Perforce depot by just calling + + git-p4.p4 sync + +in your git repository. git-p4 stores the depot path of the original import in +the .git/config file and remembers the last imported p4 revision as a git tag +called p4/ . + +Submitting +========== + +git-p4 has EXPERIMENTAL support for submitting changes from a git repository +back to a Perforce depot. This requires a Perforce checkout separate to your +git repository. This is the basic procedure: + + cd path/to/your/perforce/checkout + git-p4.py submit --git-dir=/path/to/your/git/repository + +This will create a temporary git branch, use git-rev-list to find out which git +commits are in your current branch but not in the "origin" branch. You can +override the name of the "origin" branch by using the --origin=mybranch option. +The "origin" branch has to be the branch populated with git-p4's sync +operation. + +After some preparations (which might take a while) git-p4 enters a loop where +it will first show a Perforce submit template and a diff of the change to +apply. After quitting the pager with 'q' git-p4 asks for confirmation for +issuing the "p4 submit" command and also gives you the option of editing the +submit template using "e". + +If a submit fails you may have to "p4 resolve" and submit manually. You can +continue importing the remaining changes with + + git-p4.py submit --git-dir=/path/to/your/git/repository --continue + +After submitting you should sync your origin branch from Perforce using +git-p4's sync command. + -- cgit v1.2.3 From b4aa8d12b48af84242a4a484673bc41326566ac9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 08:27:33 +0100 Subject: Documentation enhancements. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index b87efc6af9..4b4fcde72b 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -25,6 +25,8 @@ If you want to import the entire history of a given depot path just use git-p4.py sync //path/in/depot@all +To achieve optimal compression you may want to run 'git repack -a -d -f' after +a big import. This may take a while. Support for Perforce integrations is still work in progress. Don't bother trying it unless you want to hack on it :) @@ -42,6 +44,10 @@ in your git repository. git-p4 stores the depot path of the original import in the .git/config file and remembers the last imported p4 revision as a git tag called p4/ . +It is recommended to run 'git repack -a -d -f' from time to time when using +incremental imports to optimally combine the individual git packs that each +incremental import creates through the use of git-fast-import. + Submitting ========== -- cgit v1.2.3 From 04219c04b7554d13b049d4aba534ce02a12abeb4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 10:11:20 +0100 Subject: Added experimental but super-fast --apply-as-patch option to git-p4 submit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 54ddf73dac..2009dce23b 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -115,7 +115,8 @@ class P4Sync(Command): optparse.make_option("--master", dest="master"), optparse.make_option("--log-substitutions", dest="substFile"), optparse.make_option("--noninteractive", action="store_false"), - optparse.make_option("--dry-run", action="store_true") + optparse.make_option("--dry-run", action="store_true"), + optparse.make_option("--apply-as-patch", action="store_true", dest="applyAsPatch") ] self.description = "Submit changes from git to the perforce depot." self.firstTime = True @@ -126,6 +127,7 @@ class P4Sync(Command): self.firstTime = True self.origin = "origin" self.master = "" + self.applyAsPatch = False self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -146,8 +148,9 @@ class P4Sync(Command): self.config["commits"] = commits - print "Creating temporary p4-sync branch from %s ..." % self.origin - system("git checkout -f -b p4-sync %s" % self.origin) + if not self.applyAsPatch: + print "Creating temporary p4-sync branch from %s ..." % self.origin + system("git checkout -f -b p4-sync %s" % self.origin) def prepareLogMessage(self, template, message): result = "" @@ -193,8 +196,11 @@ class P4Sync(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") - system("git cherry-pick --no-commit \"%s\"" % id) + if self.applyAsPatch: + system("git-diff-tree -p \"%s^\" \"%s\" | patch -p1" % (id, id)) + else: + system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") + system("git cherry-pick --no-commit \"%s\"" % id) for f in filesToAdd: system("p4 add %s" % f) @@ -305,12 +311,13 @@ class P4Sync(Command): print "No changes found to apply between %s and current HEAD" % self.origin else: print "All changes applied!" - print "Deleting temporary p4-sync branch and going back to %s" % self.master - system("git checkout %s" % self.master) - system("git branch -D p4-sync") - print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." - system("p4 edit ... >/dev/null") - system("p4 revert ... >/dev/null") + if not self.applyAsPatch: + print "Deleting temporary p4-sync branch and going back to %s" % self.master + system("git checkout %s" % self.master) + system("git branch -D p4-sync") + print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." + system("p4 edit ... >/dev/null") + system("p4 revert ... >/dev/null") os.remove(self.configFile) return True -- cgit v1.2.3 From 5d0b6042d4a33dbd78840a8e0d8957441e2cfa72 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 10:57:54 +0100 Subject: Fix support for deletions in git-p4 submit when using --apply-as-patch by filtering out deletions in the diff-tree output. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 2009dce23b..336c3eab52 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -197,7 +197,7 @@ class P4Sync(Command): die("unknown modifier %s for %s" % (modifier, path)) if self.applyAsPatch: - system("git-diff-tree -p \"%s^\" \"%s\" | patch -p1" % (id, id)) + system("git-diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) else: system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") system("git cherry-pick --no-commit \"%s\"" % id) -- cgit v1.2.3 From 1932a6ac7c3725aec20574942e5eed9b1e1c5dee Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 11:01:18 +0100 Subject: Made --apply-as-patch the default for git-p4 submit as it's significantly faster. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 336c3eab52..a07534058c 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -127,7 +127,7 @@ class P4Sync(Command): self.firstTime = True self.origin = "origin" self.master = "" - self.applyAsPatch = False + self.applyAsPatch = True self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" -- cgit v1.2.3 From 20618650058741117d909e086c2e78c003e5cfcd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 13:05:30 +0100 Subject: Make it possible to invoke git-p4 from within subdirectories of a git working tree. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index a07534058c..f940e93bad 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -925,6 +925,10 @@ parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), gitdir = cmd.gitdir if len(gitdir) == 0: gitdir = ".git" + if not isValidGitDir(gitdir): + cdup = os.popen("git-rev-parse --show-cdup").read()[:-1] + if isValidGitDir(cdup + "/" + gitdir): + os.chdir(cdup) if not isValidGitDir(gitdir): if isValidGitDir(gitdir + "/.git"): -- cgit v1.2.3 From 53150250b13e7621c8ade1d59d19c946b745dd39 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 21:04:12 +0100 Subject: Don't show the submit template and the diff first in less but show it in $editor right away Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index f940e93bad..06858844e5 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -235,24 +235,26 @@ class P4Sync(Command): diff += "+" + line f.close() - pipe = os.popen("less", "w") - pipe.write(submitTemplate + diff) - pipe.close() + separatorLine = "######## everything below this line is just the diff #######\n" response = "e" + firstIteration = True while response == "e": - response = raw_input("Do you want to submit this change (y/e/n)? ") + if not firstIteration: + response = raw_input("Do you want to submit this change (y/e/n)? ") + firstIteration = False if response == "e": [handle, fileName] = tempfile.mkstemp() tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate) + tmpFile.write(submitTemplate + separatorLine + diff) tmpFile.close() editor = os.environ.get("EDITOR", "vi") system(editor + " " + fileName) tmpFile = open(fileName, "r") - submitTemplate = tmpFile.read() + message = tmpFile.read() tmpFile.close() os.remove(fileName) + submitTemplate = message[:message.index(separatorLine)] if response == "y" or response == "yes": if self.dryRun: -- cgit v1.2.3 From e7f0d0d9b9dc99a3f748ed686625cc9f87d9a267 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 09:13:01 +0100 Subject: Removed the .py extension from git-p4 as it's annoying to type every time. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 945 +++++++++++++++++++++++++++++++++++++++++ contrib/fast-import/git-p4.py | 945 ----------------------------------------- contrib/fast-import/git-p4.txt | 14 +- 3 files changed, 952 insertions(+), 952 deletions(-) create mode 100755 contrib/fast-import/git-p4 delete mode 100755 contrib/fast-import/git-p4.py (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 new file mode 100755 index 0000000000..06858844e5 --- /dev/null +++ b/contrib/fast-import/git-p4 @@ -0,0 +1,945 @@ +#!/usr/bin/env python +# +# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git. +# +# Author: Simon Hausmann +# Copyright: 2007 Simon Hausmann +# 2007 Trolltech ASA +# License: MIT +# + +import optparse, sys, os, marshal, popen2, shelve +import tempfile, getopt, sha, os.path, time +from sets import Set; + +gitdir = os.environ.get("GIT_DIR", "") + +def p4CmdList(cmd): + cmd = "p4 -G %s" % cmd + pipe = os.popen(cmd, "rb") + + result = [] + try: + while True: + entry = marshal.load(pipe) + result.append(entry) + except EOFError: + pass + pipe.close() + + return result + +def p4Cmd(cmd): + list = p4CmdList(cmd) + result = {} + for entry in list: + result.update(entry) + return result; + +def die(msg): + sys.stderr.write(msg + "\n") + sys.exit(1) + +def currentGitBranch(): + return os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] + +def isValidGitDir(path): + if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): + return True; + return False + +def system(cmd): + if os.system(cmd) != 0: + die("command failed: %s" % cmd) + +class Command: + def __init__(self): + self.usage = "usage: %prog [options]" + +class P4Debug(Command): + def __init__(self): + self.options = [ + ] + self.description = "A tool to debug the output of p4 -G." + + def run(self, args): + for output in p4CmdList(" ".join(args)): + print output + return True + +class P4CleanTags(Command): + def __init__(self): + Command.__init__(self) + self.options = [ +# optparse.make_option("--branch", dest="branch", default="refs/heads/master") + ] + self.description = "A tool to remove stale unused tags from incremental perforce imports." + def run(self, args): + branch = currentGitBranch() + print "Cleaning out stale p4 import tags..." + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) + output = sout.read() + try: + tagIdx = output.index(" tags/p4/") + except: + print "Cannot find any p4/* tag. Nothing to do." + sys.exit(0) + + try: + caretIdx = output.index("^") + except: + caretIdx = len(output) - 1 + rev = int(output[tagIdx + 9 : caretIdx]) + + allTags = os.popen("git tag -l p4/").readlines() + for i in range(len(allTags)): + allTags[i] = int(allTags[i][3:-1]) + + allTags.sort() + + allTags.remove(rev) + + for rev in allTags: + print os.popen("git tag -d p4/%s" % rev).read() + + print "%s tags removed." % len(allTags) + return True + +class P4Sync(Command): + def __init__(self): + Command.__init__(self) + self.options = [ + optparse.make_option("--continue", action="store_false", dest="firstTime"), + optparse.make_option("--origin", dest="origin"), + optparse.make_option("--reset", action="store_true", dest="reset"), + optparse.make_option("--master", dest="master"), + optparse.make_option("--log-substitutions", dest="substFile"), + optparse.make_option("--noninteractive", action="store_false"), + optparse.make_option("--dry-run", action="store_true"), + optparse.make_option("--apply-as-patch", action="store_true", dest="applyAsPatch") + ] + self.description = "Submit changes from git to the perforce depot." + self.firstTime = True + self.reset = False + self.interactive = True + self.dryRun = False + self.substFile = "" + self.firstTime = True + self.origin = "origin" + self.master = "" + self.applyAsPatch = True + + self.logSubstitutions = {} + self.logSubstitutions[""] = "%log%" + self.logSubstitutions["\tDetails:"] = "\tDetails: %log%" + + def check(self): + if len(p4CmdList("opened ...")) > 0: + die("You have files opened with perforce! Close them before starting the sync.") + + def start(self): + if len(self.config) > 0 and not self.reset: + die("Cannot start sync. Previous sync config found at %s" % self.configFile) + + commits = [] + for line in os.popen("git-rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + commits.append(line[:-1]) + commits.reverse() + + self.config["commits"] = commits + + if not self.applyAsPatch: + print "Creating temporary p4-sync branch from %s ..." % self.origin + system("git checkout -f -b p4-sync %s" % self.origin) + + def prepareLogMessage(self, template, message): + result = "" + + for line in template.split("\n"): + if line.startswith("#"): + result += line + "\n" + continue + + substituted = False + for key in self.logSubstitutions.keys(): + if line.find(key) != -1: + value = self.logSubstitutions[key] + value = value.replace("%log%", message) + if value != "@remove@": + result += line.replace(key, value) + "\n" + substituted = True + break + + if not substituted: + result += line + "\n" + + return result + + def apply(self, id): + print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) + diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + filesToAdd = set() + filesToDelete = set() + for line in diff: + modifier = line[0] + path = line[1:].strip() + if modifier == "M": + system("p4 edit %s" % path) + elif modifier == "A": + filesToAdd.add(path) + if path in filesToDelete: + filesToDelete.remove(path) + elif modifier == "D": + filesToDelete.add(path) + if path in filesToAdd: + filesToAdd.remove(path) + else: + die("unknown modifier %s for %s" % (modifier, path)) + + if self.applyAsPatch: + system("git-diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) + else: + system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") + system("git cherry-pick --no-commit \"%s\"" % id) + + for f in filesToAdd: + system("p4 add %s" % f) + for f in filesToDelete: + system("p4 revert %s" % f) + system("p4 delete %s" % f) + + logMessage = "" + foundTitle = False + for log in os.popen("git-cat-file commit %s" % id).readlines(): + if not foundTitle: + if len(log) == 1: + foundTitle = 1 + continue + + if len(logMessage) > 0: + logMessage += "\t" + logMessage += log + + template = os.popen("p4 change -o").read() + + if self.interactive: + submitTemplate = self.prepareLogMessage(template, logMessage) + diff = os.popen("p4 diff -du ...").read() + + for newFile in filesToAdd: + diff += "==== new file ====\n" + diff += "--- /dev/null\n" + diff += "+++ %s\n" % newFile + f = open(newFile, "r") + for line in f.readlines(): + diff += "+" + line + f.close() + + separatorLine = "######## everything below this line is just the diff #######\n" + + response = "e" + firstIteration = True + while response == "e": + if not firstIteration: + response = raw_input("Do you want to submit this change (y/e/n)? ") + firstIteration = False + if response == "e": + [handle, fileName] = tempfile.mkstemp() + tmpFile = os.fdopen(handle, "w+") + tmpFile.write(submitTemplate + separatorLine + diff) + tmpFile.close() + editor = os.environ.get("EDITOR", "vi") + system(editor + " " + fileName) + tmpFile = open(fileName, "r") + message = tmpFile.read() + tmpFile.close() + os.remove(fileName) + submitTemplate = message[:message.index(separatorLine)] + + if response == "y" or response == "yes": + if self.dryRun: + print submitTemplate + raw_input("Press return to continue...") + else: + pipe = os.popen("p4 submit -i", "w") + pipe.write(submitTemplate) + pipe.close() + else: + print "Not submitting!" + self.interactive = False + else: + fileName = "submit.txt" + file = open(fileName, "w+") + file.write(self.prepareLogMessage(template, logMessage)) + file.close() + print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) + + def run(self, args): + if self.reset: + self.firstTime = True + + if len(self.substFile) > 0: + for line in open(self.substFile, "r").readlines(): + tokens = line[:-1].split("=") + self.logSubstitutions[tokens[0]] = tokens[1] + + if len(self.master) == 0: + self.master = currentGitBranch() + if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): + die("Detecting current git branch failed!") + + self.check() + self.configFile = gitdir + "/p4-git-sync.cfg" + self.config = shelve.open(self.configFile, writeback=True) + + if self.firstTime: + self.start() + + commits = self.config.get("commits", []) + + while len(commits) > 0: + self.firstTime = False + commit = commits[0] + commits = commits[1:] + self.config["commits"] = commits + self.apply(commit) + if not self.interactive: + break + + self.config.close() + + if len(commits) == 0: + if self.firstTime: + print "No changes found to apply between %s and current HEAD" % self.origin + else: + print "All changes applied!" + if not self.applyAsPatch: + print "Deleting temporary p4-sync branch and going back to %s" % self.master + system("git checkout %s" % self.master) + system("git branch -D p4-sync") + print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." + system("p4 edit ... >/dev/null") + system("p4 revert ... >/dev/null") + os.remove(self.configFile) + + return True + +class GitSync(Command): + def __init__(self): + Command.__init__(self) + self.options = [ + optparse.make_option("--branch", dest="branch"), + optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"), + optparse.make_option("--changesfile", dest="changesFile"), + optparse.make_option("--silent", dest="silent", action="store_true"), + optparse.make_option("--known-branches", dest="knownBranches"), + optparse.make_option("--cache", dest="doCache", action="store_true"), + optparse.make_option("--command-cache", dest="commandCache", action="store_true") + ] + self.description = """Imports from Perforce into a git repository.\n + example: + //depot/my/project/ -- to import the current head + //depot/my/project/@all -- to import everything + //depot/my/project/@1,6 -- to import only from revision 1 to 6 + + (a ... is not needed in the path p4 specification, it's added implicitly)""" + + self.usage += " //depot/path[@revRange]" + + self.dataCache = False + self.commandCache = False + self.silent = False + self.knownBranches = Set() + self.createdBranches = Set() + self.committedChanges = Set() + self.branch = "master" + self.detectBranches = False + self.changesFile = "" + + def p4File(self, depotPath): + return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + + def extractFilesFromCommit(self, commit): + files = [] + fnum = 0 + while commit.has_key("depotFile%s" % fnum): + path = commit["depotFile%s" % fnum] + if not path.startswith(self.globalPrefix): + # if not self.silent: + # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.globalPrefix, change) + fnum = fnum + 1 + continue + + file = {} + file["path"] = path + file["rev"] = commit["rev%s" % fnum] + file["action"] = commit["action%s" % fnum] + file["type"] = commit["type%s" % fnum] + files.append(file) + fnum = fnum + 1 + return files + + def isSubPathOf(self, first, second): + if not first.startswith(second): + return False + if first == second: + return True + return first[len(second)] == "/" + + def branchesForCommit(self, files): + branches = Set() + + for file in files: + relativePath = file["path"][len(self.globalPrefix):] + # strip off the filename + relativePath = relativePath[0:relativePath.rfind("/")] + + # if len(branches) == 0: + # branches.add(relativePath) + # knownBranches.add(relativePath) + # continue + + ###### this needs more testing :) + knownBranch = False + for branch in branches: + if relativePath == branch: + knownBranch = True + break + # if relativePath.startswith(branch): + if self.isSubPathOf(relativePath, branch): + knownBranch = True + break + # if branch.startswith(relativePath): + if self.isSubPathOf(branch, relativePath): + branches.remove(branch) + break + + if knownBranch: + continue + + for branch in knownBranches: + #if relativePath.startswith(branch): + if self.isSubPathOf(relativePath, branch): + if len(branches) == 0: + relativePath = branch + else: + knownBranch = True + break + + if knownBranch: + continue + + branches.add(relativePath) + self.knownBranches.add(relativePath) + + return branches + + def findBranchParent(self, branchPrefix, files): + for file in files: + path = file["path"] + if not path.startswith(branchPrefix): + continue + action = file["action"] + if action != "integrate" and action != "branch": + continue + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + branchAction = log["how0,0"] + # if branchAction == "branch into" or branchAction == "ignored": + # continue # ignore for branching + + if not branchAction.endswith(" from"): + continue # ignore for branching + # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) + # sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + lastSourceRev = log["erev0,0"] + + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + relPath = source[len(self.globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] + + for branch in self.knownBranches: + if self.isSubPathOf(relPath, branch): + # print "determined parent branch branch %s due to change in file %s" % (branch, source) + return branch + # else: + # print "%s is not a subpath of branch %s" % (relPath, branch) + + return "" + + def commit(self, details, files, branch, branchPrefix, parent = "", merged = ""): + epoch = details["time"] + author = details["user"] + + self.gitStream.write("commit %s\n" % branch) + # gitStream.write("mark :%s\n" % details["change"]) + self.committedChanges.add(int(details["change"])) + committer = "" + if author in self.users: + committer = "%s %s %s" % (self.users[author], epoch, self.tz) + else: + committer = "%s %s %s" % (author, epoch, self.tz) + + self.gitStream.write("committer %s\n" % committer) + + self.gitStream.write("data < 0: + self.gitStream.write("from %s\n" % parent) + + if len(merged) > 0: + self.gitStream.write("merge %s\n" % merged) + + for file in files: + path = file["path"] + if not path.startswith(branchPrefix): + # if not silent: + # print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) + continue + rev = file["rev"] + depotPath = path + "#" + rev + relPath = path[len(branchPrefix):] + action = file["action"] + + if file["type"] == "apple": + print "\nfile %s is a strange apple file that forks. Ignoring!" % path + continue + + if action == "delete": + self.gitStream.write("D %s\n" % relPath) + else: + mode = 644 + if file["type"].startswith("x"): + mode = 755 + + data = self.p4File(depotPath) + + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) + self.gitStream.write("data %s\n" % len(data)) + self.gitStream.write(data) + self.gitStream.write("\n") + + self.gitStream.write("\n") + + self.lastChange = int(details["change"]) + + def extractFilesInCommitToBranch(self, files, branchPrefix): + newFiles = [] + + for file in files: + path = file["path"] + if path.startswith(branchPrefix): + newFiles.append(file) + + return newFiles + + def findBranchSourceHeuristic(self, files, branch, branchPrefix): + for file in files: + action = file["action"] + if action != "integrate" and action != "branch": + continue + path = file["path"] + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + branchAction = log["how0,0"] + + if not branchAction.endswith(" from"): + continue # ignore for branching + # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) + # sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + lastSourceRev = log["erev0,0"] + + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + relPath = source[len(self.globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] + + for candidate in self.knownBranches: + if self.isSubPathOf(relPath, candidate) and candidate != branch: + return candidate + + return "" + + def changeIsBranchMerge(self, sourceBranch, destinationBranch, change): + sourceFiles = {} + for file in p4CmdList("files %s...@%s" % (self.globalPrefix + sourceBranch + "/", change)): + if file["action"] == "delete": + continue + sourceFiles[file["depotFile"]] = file + + destinationFiles = {} + for file in p4CmdList("files %s...@%s" % (self.globalPrefix + destinationBranch + "/", change)): + destinationFiles[file["depotFile"]] = file + + for fileName in sourceFiles.keys(): + integrations = [] + deleted = False + integrationCount = 0 + for integration in p4CmdList("integrated \"%s\"" % fileName): + toFile = integration["fromFile"] # yes, it's true, it's fromFile + if not toFile in destinationFiles: + continue + destFile = destinationFiles[toFile] + if destFile["action"] == "delete": + # print "file %s has been deleted in %s" % (fileName, toFile) + deleted = True + break + integrationCount += 1 + if integration["how"] == "branch from": + continue + + if int(integration["change"]) == change: + integrations.append(integration) + continue + if int(integration["change"]) > change: + continue + + destRev = int(destFile["rev"]) + + startRev = integration["startFromRev"][1:] + if startRev == "none": + startRev = 0 + else: + startRev = int(startRev) + + endRev = integration["endFromRev"][1:] + if endRev == "none": + endRev = 0 + else: + endRev = int(endRev) + + initialBranch = (destRev == 1 and integration["how"] != "branch into") + inRange = (destRev >= startRev and destRev <= endRev) + newer = (destRev > startRev and destRev > endRev) + + if initialBranch or inRange or newer: + integrations.append(integration) + + if deleted: + continue + + if len(integrations) == 0 and integrationCount > 1: + print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) + return False + + return True + + def getUserMap(self): + self.users = {} + + for output in p4CmdList("users"): + if not output.has_key("User"): + continue + self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" + + def run(self, args): + self.branch = "refs/heads/" + self.branch + self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() + if len(self.globalPrefix) != 0: + self.globalPrefix = self.globalPrefix[:-1] + + if len(args) == 0 and len(self.globalPrefix) != 0: + if not self.silent: + print "[using previously specified depot path %s]" % self.globalPrefix + elif len(args) != 1: + return False + else: + if len(self.globalPrefix) != 0 and self.globalPrefix != args[0]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.globalPrefix, args[0]) + sys.exit(1) + self.globalPrefix = args[0] + + self.changeRange = "" + self.revision = "" + self.users = {} + self.initialParent = "" + self.lastChange = 0 + self.initialTag = "" + + if self.globalPrefix.find("@") != -1: + atIdx = self.globalPrefix.index("@") + self.changeRange = self.globalPrefix[atIdx:] + if self.changeRange == "@all": + self.changeRange = "" + elif self.changeRange.find(",") == -1: + self.revision = self.changeRange + self.changeRange = "" + self.globalPrefix = self.globalPrefix[0:atIdx] + elif self.globalPrefix.find("#") != -1: + hashIdx = self.globalPrefix.index("#") + self.revision = self.globalPrefix[hashIdx:] + self.globalPrefix = self.globalPrefix[0:hashIdx] + elif len(self.previousDepotPath) == 0: + self.revision = "#head" + + if self.globalPrefix.endswith("..."): + self.globalPrefix = self.globalPrefix[:-3] + + if not self.globalPrefix.endswith("/"): + self.globalPrefix += "/" + + self.getUserMap() + + if len(self.changeRange) == 0: + try: + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % self.branch) + output = sout.read() + if output.endswith("\n"): + output = output[:-1] + tagIdx = output.index(" tags/p4/") + caretIdx = output.find("^") + endPos = len(output) + if caretIdx != -1: + endPos = caretIdx + self.rev = int(output[tagIdx + 9 : endPos]) + 1 + self.changeRange = "@%s,#head" % self.rev + self.initialParent = os.popen("git-rev-parse %s" % self.branch).read()[:-1] + self.initialTag = "p4/%s" % (int(self.rev) - 1) + except: + pass + + self.tz = - time.timezone / 36 + tzsign = ("%s" % self.tz)[0] + if tzsign != '+' and tzsign != '-': + self.tz = "+" + ("%s" % self.tz) + + self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git-fast-import") + + if len(self.revision) > 0: + print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) + + details = { "user" : "git perforce import user", "time" : int(time.time()) } + details["desc"] = "Initial import of %s from the state at revision %s" % (self.globalPrefix, self.revision) + details["change"] = self.revision + newestRevision = 0 + + fileCnt = 0 + for info in p4CmdList("files %s...%s" % (self.globalPrefix, self.revision)): + change = int(info["change"]) + if change > newestRevision: + newestRevision = change + + if info["action"] == "delete": + fileCnt = fileCnt + 1 + continue + + for prop in [ "depotFile", "rev", "action", "type" ]: + details["%s%s" % (prop, fileCnt)] = info[prop] + + fileCnt = fileCnt + 1 + + details["change"] = newestRevision + + try: + self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) + except IOError: + print self.gitError.read() + + else: + changes = [] + + if len(self.changesFile) > 0: + output = open(self.changesFile).readlines() + changeSet = Set() + for line in output: + changeSet.add(int(line)) + + for change in changeSet: + changes.append(change) + + changes.sort() + else: + output = os.popen("p4 changes %s...%s" % (self.globalPrefix, self.changeRange)).readlines() + + for line in output: + changeNum = line.split(" ")[1] + changes.append(changeNum) + + changes.reverse() + + if len(changes) == 0: + if not self.silent: + print "no changes to import!" + sys.exit(1) + + cnt = 1 + for change in changes: + description = p4Cmd("describe %s" % change) + + if not self.silent: + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() + cnt = cnt + 1 + + try: + files = self.extractFilesFromCommit(description) + if self.detectBranches: + for branch in self.branchesForCommit(files): + self.knownBranches.add(branch) + branchPrefix = self.globalPrefix + branch + "/" + + filesForCommit = self.extractFilesInCommitToBranch(files, branchPrefix) + + merged = "" + parent = "" + ########### remove cnt!!! + if branch not in self.createdBranches and cnt > 2: + self.createdBranches.add(branch) + parent = self.findBranchParent(branchPrefix, files) + if parent == branch: + parent = "" + # elif len(parent) > 0: + # print "%s branched off of %s" % (branch, parent) + + if len(parent) == 0: + merged = self.findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) + if len(merged) > 0: + print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) + if not self.changeIsBranchMerge(merged, branch, int(description["change"])): + merged = "" + + branch = "refs/heads/" + branch + if len(parent) > 0: + parent = "refs/heads/" + parent + if len(merged) > 0: + merged = "refs/heads/" + merged + self.commit(description, files, branch, branchPrefix, parent, merged) + else: + self.commit(description, files, self.branch, self.globalPrefix, self.initialParent) + self.initialParent = "" + except IOError: + print self.gitError.read() + sys.exit(1) + + if not self.silent: + print "" + + self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) + self.gitStream.write("from %s\n\n" % self.branch); + + + self.gitStream.close() + self.gitOutput.close() + self.gitError.close() + + os.popen("git-repo-config p4.depotpath %s" % self.globalPrefix).read() + if len(self.initialTag) > 0: + os.popen("git tag -d %s" % self.initialTag).read() + + return True + +class HelpFormatter(optparse.IndentedHelpFormatter): + def __init__(self): + optparse.IndentedHelpFormatter.__init__(self) + + def format_description(self, description): + if description: + return description + "\n" + else: + return "" + +def printUsage(commands): + print "usage: %s [options]" % sys.argv[0] + print "" + print "valid commands: %s" % ", ".join(commands) + print "" + print "Try %s --help for command specific help." % sys.argv[0] + print "" + +commands = { + "debug" : P4Debug(), + "clean-tags" : P4CleanTags(), + "submit" : P4Sync(), + "sync" : GitSync() +} + +if len(sys.argv[1:]) == 0: + printUsage(commands.keys()) + sys.exit(2) + +cmd = "" +cmdName = sys.argv[1] +try: + cmd = commands[cmdName] +except KeyError: + print "unknown command %s" % cmdName + print "" + printUsage(commands.keys()) + sys.exit(2) + +options = cmd.options +cmd.gitdir = gitdir +options.append(optparse.make_option("--git-dir", dest="gitdir")) + +parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), + options, + description = cmd.description, + formatter = HelpFormatter()) + +(cmd, args) = parser.parse_args(sys.argv[2:], cmd); + +gitdir = cmd.gitdir +if len(gitdir) == 0: + gitdir = ".git" + if not isValidGitDir(gitdir): + cdup = os.popen("git-rev-parse --show-cdup").read()[:-1] + if isValidGitDir(cdup + "/" + gitdir): + os.chdir(cdup) + +if not isValidGitDir(gitdir): + if isValidGitDir(gitdir + "/.git"): + gitdir += "/.git" + else: + die("fatal: cannot locate git repository at %s" % gitdir) + +os.environ["GIT_DIR"] = gitdir + +if not cmd.run(args): + parser.print_help() + diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py deleted file mode 100755 index 06858844e5..0000000000 --- a/contrib/fast-import/git-p4.py +++ /dev/null @@ -1,945 +0,0 @@ -#!/usr/bin/env python -# -# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git. -# -# Author: Simon Hausmann -# Copyright: 2007 Simon Hausmann -# 2007 Trolltech ASA -# License: MIT -# - -import optparse, sys, os, marshal, popen2, shelve -import tempfile, getopt, sha, os.path, time -from sets import Set; - -gitdir = os.environ.get("GIT_DIR", "") - -def p4CmdList(cmd): - cmd = "p4 -G %s" % cmd - pipe = os.popen(cmd, "rb") - - result = [] - try: - while True: - entry = marshal.load(pipe) - result.append(entry) - except EOFError: - pass - pipe.close() - - return result - -def p4Cmd(cmd): - list = p4CmdList(cmd) - result = {} - for entry in list: - result.update(entry) - return result; - -def die(msg): - sys.stderr.write(msg + "\n") - sys.exit(1) - -def currentGitBranch(): - return os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] - -def isValidGitDir(path): - if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): - return True; - return False - -def system(cmd): - if os.system(cmd) != 0: - die("command failed: %s" % cmd) - -class Command: - def __init__(self): - self.usage = "usage: %prog [options]" - -class P4Debug(Command): - def __init__(self): - self.options = [ - ] - self.description = "A tool to debug the output of p4 -G." - - def run(self, args): - for output in p4CmdList(" ".join(args)): - print output - return True - -class P4CleanTags(Command): - def __init__(self): - Command.__init__(self) - self.options = [ -# optparse.make_option("--branch", dest="branch", default="refs/heads/master") - ] - self.description = "A tool to remove stale unused tags from incremental perforce imports." - def run(self, args): - branch = currentGitBranch() - print "Cleaning out stale p4 import tags..." - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) - output = sout.read() - try: - tagIdx = output.index(" tags/p4/") - except: - print "Cannot find any p4/* tag. Nothing to do." - sys.exit(0) - - try: - caretIdx = output.index("^") - except: - caretIdx = len(output) - 1 - rev = int(output[tagIdx + 9 : caretIdx]) - - allTags = os.popen("git tag -l p4/").readlines() - for i in range(len(allTags)): - allTags[i] = int(allTags[i][3:-1]) - - allTags.sort() - - allTags.remove(rev) - - for rev in allTags: - print os.popen("git tag -d p4/%s" % rev).read() - - print "%s tags removed." % len(allTags) - return True - -class P4Sync(Command): - def __init__(self): - Command.__init__(self) - self.options = [ - optparse.make_option("--continue", action="store_false", dest="firstTime"), - optparse.make_option("--origin", dest="origin"), - optparse.make_option("--reset", action="store_true", dest="reset"), - optparse.make_option("--master", dest="master"), - optparse.make_option("--log-substitutions", dest="substFile"), - optparse.make_option("--noninteractive", action="store_false"), - optparse.make_option("--dry-run", action="store_true"), - optparse.make_option("--apply-as-patch", action="store_true", dest="applyAsPatch") - ] - self.description = "Submit changes from git to the perforce depot." - self.firstTime = True - self.reset = False - self.interactive = True - self.dryRun = False - self.substFile = "" - self.firstTime = True - self.origin = "origin" - self.master = "" - self.applyAsPatch = True - - self.logSubstitutions = {} - self.logSubstitutions[""] = "%log%" - self.logSubstitutions["\tDetails:"] = "\tDetails: %log%" - - def check(self): - if len(p4CmdList("opened ...")) > 0: - die("You have files opened with perforce! Close them before starting the sync.") - - def start(self): - if len(self.config) > 0 and not self.reset: - die("Cannot start sync. Previous sync config found at %s" % self.configFile) - - commits = [] - for line in os.popen("git-rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): - commits.append(line[:-1]) - commits.reverse() - - self.config["commits"] = commits - - if not self.applyAsPatch: - print "Creating temporary p4-sync branch from %s ..." % self.origin - system("git checkout -f -b p4-sync %s" % self.origin) - - def prepareLogMessage(self, template, message): - result = "" - - for line in template.split("\n"): - if line.startswith("#"): - result += line + "\n" - continue - - substituted = False - for key in self.logSubstitutions.keys(): - if line.find(key) != -1: - value = self.logSubstitutions[key] - value = value.replace("%log%", message) - if value != "@remove@": - result += line.replace(key, value) + "\n" - substituted = True - break - - if not substituted: - result += line + "\n" - - return result - - def apply(self, id): - print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) - diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() - filesToAdd = set() - filesToDelete = set() - for line in diff: - modifier = line[0] - path = line[1:].strip() - if modifier == "M": - system("p4 edit %s" % path) - elif modifier == "A": - filesToAdd.add(path) - if path in filesToDelete: - filesToDelete.remove(path) - elif modifier == "D": - filesToDelete.add(path) - if path in filesToAdd: - filesToAdd.remove(path) - else: - die("unknown modifier %s for %s" % (modifier, path)) - - if self.applyAsPatch: - system("git-diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) - else: - system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") - system("git cherry-pick --no-commit \"%s\"" % id) - - for f in filesToAdd: - system("p4 add %s" % f) - for f in filesToDelete: - system("p4 revert %s" % f) - system("p4 delete %s" % f) - - logMessage = "" - foundTitle = False - for log in os.popen("git-cat-file commit %s" % id).readlines(): - if not foundTitle: - if len(log) == 1: - foundTitle = 1 - continue - - if len(logMessage) > 0: - logMessage += "\t" - logMessage += log - - template = os.popen("p4 change -o").read() - - if self.interactive: - submitTemplate = self.prepareLogMessage(template, logMessage) - diff = os.popen("p4 diff -du ...").read() - - for newFile in filesToAdd: - diff += "==== new file ====\n" - diff += "--- /dev/null\n" - diff += "+++ %s\n" % newFile - f = open(newFile, "r") - for line in f.readlines(): - diff += "+" + line - f.close() - - separatorLine = "######## everything below this line is just the diff #######\n" - - response = "e" - firstIteration = True - while response == "e": - if not firstIteration: - response = raw_input("Do you want to submit this change (y/e/n)? ") - firstIteration = False - if response == "e": - [handle, fileName] = tempfile.mkstemp() - tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate + separatorLine + diff) - tmpFile.close() - editor = os.environ.get("EDITOR", "vi") - system(editor + " " + fileName) - tmpFile = open(fileName, "r") - message = tmpFile.read() - tmpFile.close() - os.remove(fileName) - submitTemplate = message[:message.index(separatorLine)] - - if response == "y" or response == "yes": - if self.dryRun: - print submitTemplate - raw_input("Press return to continue...") - else: - pipe = os.popen("p4 submit -i", "w") - pipe.write(submitTemplate) - pipe.close() - else: - print "Not submitting!" - self.interactive = False - else: - fileName = "submit.txt" - file = open(fileName, "w+") - file.write(self.prepareLogMessage(template, logMessage)) - file.close() - print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) - - def run(self, args): - if self.reset: - self.firstTime = True - - if len(self.substFile) > 0: - for line in open(self.substFile, "r").readlines(): - tokens = line[:-1].split("=") - self.logSubstitutions[tokens[0]] = tokens[1] - - if len(self.master) == 0: - self.master = currentGitBranch() - if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): - die("Detecting current git branch failed!") - - self.check() - self.configFile = gitdir + "/p4-git-sync.cfg" - self.config = shelve.open(self.configFile, writeback=True) - - if self.firstTime: - self.start() - - commits = self.config.get("commits", []) - - while len(commits) > 0: - self.firstTime = False - commit = commits[0] - commits = commits[1:] - self.config["commits"] = commits - self.apply(commit) - if not self.interactive: - break - - self.config.close() - - if len(commits) == 0: - if self.firstTime: - print "No changes found to apply between %s and current HEAD" % self.origin - else: - print "All changes applied!" - if not self.applyAsPatch: - print "Deleting temporary p4-sync branch and going back to %s" % self.master - system("git checkout %s" % self.master) - system("git branch -D p4-sync") - print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." - system("p4 edit ... >/dev/null") - system("p4 revert ... >/dev/null") - os.remove(self.configFile) - - return True - -class GitSync(Command): - def __init__(self): - Command.__init__(self) - self.options = [ - optparse.make_option("--branch", dest="branch"), - optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"), - optparse.make_option("--changesfile", dest="changesFile"), - optparse.make_option("--silent", dest="silent", action="store_true"), - optparse.make_option("--known-branches", dest="knownBranches"), - optparse.make_option("--cache", dest="doCache", action="store_true"), - optparse.make_option("--command-cache", dest="commandCache", action="store_true") - ] - self.description = """Imports from Perforce into a git repository.\n - example: - //depot/my/project/ -- to import the current head - //depot/my/project/@all -- to import everything - //depot/my/project/@1,6 -- to import only from revision 1 to 6 - - (a ... is not needed in the path p4 specification, it's added implicitly)""" - - self.usage += " //depot/path[@revRange]" - - self.dataCache = False - self.commandCache = False - self.silent = False - self.knownBranches = Set() - self.createdBranches = Set() - self.committedChanges = Set() - self.branch = "master" - self.detectBranches = False - self.changesFile = "" - - def p4File(self, depotPath): - return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() - - def extractFilesFromCommit(self, commit): - files = [] - fnum = 0 - while commit.has_key("depotFile%s" % fnum): - path = commit["depotFile%s" % fnum] - if not path.startswith(self.globalPrefix): - # if not self.silent: - # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.globalPrefix, change) - fnum = fnum + 1 - continue - - file = {} - file["path"] = path - file["rev"] = commit["rev%s" % fnum] - file["action"] = commit["action%s" % fnum] - file["type"] = commit["type%s" % fnum] - files.append(file) - fnum = fnum + 1 - return files - - def isSubPathOf(self, first, second): - if not first.startswith(second): - return False - if first == second: - return True - return first[len(second)] == "/" - - def branchesForCommit(self, files): - branches = Set() - - for file in files: - relativePath = file["path"][len(self.globalPrefix):] - # strip off the filename - relativePath = relativePath[0:relativePath.rfind("/")] - - # if len(branches) == 0: - # branches.add(relativePath) - # knownBranches.add(relativePath) - # continue - - ###### this needs more testing :) - knownBranch = False - for branch in branches: - if relativePath == branch: - knownBranch = True - break - # if relativePath.startswith(branch): - if self.isSubPathOf(relativePath, branch): - knownBranch = True - break - # if branch.startswith(relativePath): - if self.isSubPathOf(branch, relativePath): - branches.remove(branch) - break - - if knownBranch: - continue - - for branch in knownBranches: - #if relativePath.startswith(branch): - if self.isSubPathOf(relativePath, branch): - if len(branches) == 0: - relativePath = branch - else: - knownBranch = True - break - - if knownBranch: - continue - - branches.add(relativePath) - self.knownBranches.add(relativePath) - - return branches - - def findBranchParent(self, branchPrefix, files): - for file in files: - path = file["path"] - if not path.startswith(branchPrefix): - continue - action = file["action"] - if action != "integrate" and action != "branch": - continue - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] - # if branchAction == "branch into" or branchAction == "ignored": - # continue # ignore for branching - - if not branchAction.endswith(" from"): - continue # ignore for branching - # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) - # sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(self.globalPrefix):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for branch in self.knownBranches: - if self.isSubPathOf(relPath, branch): - # print "determined parent branch branch %s due to change in file %s" % (branch, source) - return branch - # else: - # print "%s is not a subpath of branch %s" % (relPath, branch) - - return "" - - def commit(self, details, files, branch, branchPrefix, parent = "", merged = ""): - epoch = details["time"] - author = details["user"] - - self.gitStream.write("commit %s\n" % branch) - # gitStream.write("mark :%s\n" % details["change"]) - self.committedChanges.add(int(details["change"])) - committer = "" - if author in self.users: - committer = "%s %s %s" % (self.users[author], epoch, self.tz) - else: - committer = "%s %s %s" % (author, epoch, self.tz) - - self.gitStream.write("committer %s\n" % committer) - - self.gitStream.write("data < 0: - self.gitStream.write("from %s\n" % parent) - - if len(merged) > 0: - self.gitStream.write("merge %s\n" % merged) - - for file in files: - path = file["path"] - if not path.startswith(branchPrefix): - # if not silent: - # print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) - continue - rev = file["rev"] - depotPath = path + "#" + rev - relPath = path[len(branchPrefix):] - action = file["action"] - - if file["type"] == "apple": - print "\nfile %s is a strange apple file that forks. Ignoring!" % path - continue - - if action == "delete": - self.gitStream.write("D %s\n" % relPath) - else: - mode = 644 - if file["type"].startswith("x"): - mode = 755 - - data = self.p4File(depotPath) - - self.gitStream.write("M %s inline %s\n" % (mode, relPath)) - self.gitStream.write("data %s\n" % len(data)) - self.gitStream.write(data) - self.gitStream.write("\n") - - self.gitStream.write("\n") - - self.lastChange = int(details["change"]) - - def extractFilesInCommitToBranch(self, files, branchPrefix): - newFiles = [] - - for file in files: - path = file["path"] - if path.startswith(branchPrefix): - newFiles.append(file) - - return newFiles - - def findBranchSourceHeuristic(self, files, branch, branchPrefix): - for file in files: - action = file["action"] - if action != "integrate" and action != "branch": - continue - path = file["path"] - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] - - if not branchAction.endswith(" from"): - continue # ignore for branching - # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) - # sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(self.globalPrefix):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for candidate in self.knownBranches: - if self.isSubPathOf(relPath, candidate) and candidate != branch: - return candidate - - return "" - - def changeIsBranchMerge(self, sourceBranch, destinationBranch, change): - sourceFiles = {} - for file in p4CmdList("files %s...@%s" % (self.globalPrefix + sourceBranch + "/", change)): - if file["action"] == "delete": - continue - sourceFiles[file["depotFile"]] = file - - destinationFiles = {} - for file in p4CmdList("files %s...@%s" % (self.globalPrefix + destinationBranch + "/", change)): - destinationFiles[file["depotFile"]] = file - - for fileName in sourceFiles.keys(): - integrations = [] - deleted = False - integrationCount = 0 - for integration in p4CmdList("integrated \"%s\"" % fileName): - toFile = integration["fromFile"] # yes, it's true, it's fromFile - if not toFile in destinationFiles: - continue - destFile = destinationFiles[toFile] - if destFile["action"] == "delete": - # print "file %s has been deleted in %s" % (fileName, toFile) - deleted = True - break - integrationCount += 1 - if integration["how"] == "branch from": - continue - - if int(integration["change"]) == change: - integrations.append(integration) - continue - if int(integration["change"]) > change: - continue - - destRev = int(destFile["rev"]) - - startRev = integration["startFromRev"][1:] - if startRev == "none": - startRev = 0 - else: - startRev = int(startRev) - - endRev = integration["endFromRev"][1:] - if endRev == "none": - endRev = 0 - else: - endRev = int(endRev) - - initialBranch = (destRev == 1 and integration["how"] != "branch into") - inRange = (destRev >= startRev and destRev <= endRev) - newer = (destRev > startRev and destRev > endRev) - - if initialBranch or inRange or newer: - integrations.append(integration) - - if deleted: - continue - - if len(integrations) == 0 and integrationCount > 1: - print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) - return False - - return True - - def getUserMap(self): - self.users = {} - - for output in p4CmdList("users"): - if not output.has_key("User"): - continue - self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" - - def run(self, args): - self.branch = "refs/heads/" + self.branch - self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() - if len(self.globalPrefix) != 0: - self.globalPrefix = self.globalPrefix[:-1] - - if len(args) == 0 and len(self.globalPrefix) != 0: - if not self.silent: - print "[using previously specified depot path %s]" % self.globalPrefix - elif len(args) != 1: - return False - else: - if len(self.globalPrefix) != 0 and self.globalPrefix != args[0]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.globalPrefix, args[0]) - sys.exit(1) - self.globalPrefix = args[0] - - self.changeRange = "" - self.revision = "" - self.users = {} - self.initialParent = "" - self.lastChange = 0 - self.initialTag = "" - - if self.globalPrefix.find("@") != -1: - atIdx = self.globalPrefix.index("@") - self.changeRange = self.globalPrefix[atIdx:] - if self.changeRange == "@all": - self.changeRange = "" - elif self.changeRange.find(",") == -1: - self.revision = self.changeRange - self.changeRange = "" - self.globalPrefix = self.globalPrefix[0:atIdx] - elif self.globalPrefix.find("#") != -1: - hashIdx = self.globalPrefix.index("#") - self.revision = self.globalPrefix[hashIdx:] - self.globalPrefix = self.globalPrefix[0:hashIdx] - elif len(self.previousDepotPath) == 0: - self.revision = "#head" - - if self.globalPrefix.endswith("..."): - self.globalPrefix = self.globalPrefix[:-3] - - if not self.globalPrefix.endswith("/"): - self.globalPrefix += "/" - - self.getUserMap() - - if len(self.changeRange) == 0: - try: - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % self.branch) - output = sout.read() - if output.endswith("\n"): - output = output[:-1] - tagIdx = output.index(" tags/p4/") - caretIdx = output.find("^") - endPos = len(output) - if caretIdx != -1: - endPos = caretIdx - self.rev = int(output[tagIdx + 9 : endPos]) + 1 - self.changeRange = "@%s,#head" % self.rev - self.initialParent = os.popen("git-rev-parse %s" % self.branch).read()[:-1] - self.initialTag = "p4/%s" % (int(self.rev) - 1) - except: - pass - - self.tz = - time.timezone / 36 - tzsign = ("%s" % self.tz)[0] - if tzsign != '+' and tzsign != '-': - self.tz = "+" + ("%s" % self.tz) - - self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git-fast-import") - - if len(self.revision) > 0: - print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) - - details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = "Initial import of %s from the state at revision %s" % (self.globalPrefix, self.revision) - details["change"] = self.revision - newestRevision = 0 - - fileCnt = 0 - for info in p4CmdList("files %s...%s" % (self.globalPrefix, self.revision)): - change = int(info["change"]) - if change > newestRevision: - newestRevision = change - - if info["action"] == "delete": - fileCnt = fileCnt + 1 - continue - - for prop in [ "depotFile", "rev", "action", "type" ]: - details["%s%s" % (prop, fileCnt)] = info[prop] - - fileCnt = fileCnt + 1 - - details["change"] = newestRevision - - try: - self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) - except IOError: - print self.gitError.read() - - else: - changes = [] - - if len(self.changesFile) > 0: - output = open(self.changesFile).readlines() - changeSet = Set() - for line in output: - changeSet.add(int(line)) - - for change in changeSet: - changes.append(change) - - changes.sort() - else: - output = os.popen("p4 changes %s...%s" % (self.globalPrefix, self.changeRange)).readlines() - - for line in output: - changeNum = line.split(" ")[1] - changes.append(changeNum) - - changes.reverse() - - if len(changes) == 0: - if not self.silent: - print "no changes to import!" - sys.exit(1) - - cnt = 1 - for change in changes: - description = p4Cmd("describe %s" % change) - - if not self.silent: - sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) - sys.stdout.flush() - cnt = cnt + 1 - - try: - files = self.extractFilesFromCommit(description) - if self.detectBranches: - for branch in self.branchesForCommit(files): - self.knownBranches.add(branch) - branchPrefix = self.globalPrefix + branch + "/" - - filesForCommit = self.extractFilesInCommitToBranch(files, branchPrefix) - - merged = "" - parent = "" - ########### remove cnt!!! - if branch not in self.createdBranches and cnt > 2: - self.createdBranches.add(branch) - parent = self.findBranchParent(branchPrefix, files) - if parent == branch: - parent = "" - # elif len(parent) > 0: - # print "%s branched off of %s" % (branch, parent) - - if len(parent) == 0: - merged = self.findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) - if len(merged) > 0: - print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) - if not self.changeIsBranchMerge(merged, branch, int(description["change"])): - merged = "" - - branch = "refs/heads/" + branch - if len(parent) > 0: - parent = "refs/heads/" + parent - if len(merged) > 0: - merged = "refs/heads/" + merged - self.commit(description, files, branch, branchPrefix, parent, merged) - else: - self.commit(description, files, self.branch, self.globalPrefix, self.initialParent) - self.initialParent = "" - except IOError: - print self.gitError.read() - sys.exit(1) - - if not self.silent: - print "" - - self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) - self.gitStream.write("from %s\n\n" % self.branch); - - - self.gitStream.close() - self.gitOutput.close() - self.gitError.close() - - os.popen("git-repo-config p4.depotpath %s" % self.globalPrefix).read() - if len(self.initialTag) > 0: - os.popen("git tag -d %s" % self.initialTag).read() - - return True - -class HelpFormatter(optparse.IndentedHelpFormatter): - def __init__(self): - optparse.IndentedHelpFormatter.__init__(self) - - def format_description(self, description): - if description: - return description + "\n" - else: - return "" - -def printUsage(commands): - print "usage: %s [options]" % sys.argv[0] - print "" - print "valid commands: %s" % ", ".join(commands) - print "" - print "Try %s --help for command specific help." % sys.argv[0] - print "" - -commands = { - "debug" : P4Debug(), - "clean-tags" : P4CleanTags(), - "submit" : P4Sync(), - "sync" : GitSync() -} - -if len(sys.argv[1:]) == 0: - printUsage(commands.keys()) - sys.exit(2) - -cmd = "" -cmdName = sys.argv[1] -try: - cmd = commands[cmdName] -except KeyError: - print "unknown command %s" % cmdName - print "" - printUsage(commands.keys()) - sys.exit(2) - -options = cmd.options -cmd.gitdir = gitdir -options.append(optparse.make_option("--git-dir", dest="gitdir")) - -parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), - options, - description = cmd.description, - formatter = HelpFormatter()) - -(cmd, args) = parser.parse_args(sys.argv[2:], cmd); - -gitdir = cmd.gitdir -if len(gitdir) == 0: - gitdir = ".git" - if not isValidGitDir(gitdir): - cdup = os.popen("git-rev-parse --show-cdup").read()[:-1] - if isValidGitDir(cdup + "/" + gitdir): - os.chdir(cdup) - -if not isValidGitDir(gitdir): - if isValidGitDir(gitdir + "/.git"): - gitdir += "/.git" - else: - die("fatal: cannot locate git repository at %s" % gitdir) - -os.environ["GIT_DIR"] = gitdir - -if not cmd.run(args): - parser.print_help() - diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 4b4fcde72b..5786bffad4 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -1,11 +1,11 @@ -git-p4.py - Perforce <-> Git converter using git-fast-import +git-p4 - Perforce <-> Git converter using git-fast-import Usage ===== git-p4 supports two main modes: Importing from Perforce to a Git repository is -done using "git-p4.py sync". Submitting changes from Git back to Perforce is -done using "git-p4.py submit". +done using "git-p4 sync". Submitting changes from Git back to Perforce is +done using "git-p4 submit". Importing ========= @@ -15,7 +15,7 @@ The procedure is simple: mkdir repo-git cd repo-git git init - git-p4.py sync //path/in/your/perforce/depot + git-p4 sync //path/in/your/perforce/depot This will import the current head revision of the specified depot path into the master branch of your git repository. You can use the --branch=mybranch option @@ -23,7 +23,7 @@ to let git-p4 import from Perforce into a git branch of your choice. If you want to import the entire history of a given depot path just use - git-p4.py sync //path/in/depot@all + git-p4 sync //path/in/depot@all To achieve optimal compression you may want to run 'git repack -a -d -f' after a big import. This may take a while. @@ -56,7 +56,7 @@ back to a Perforce depot. This requires a Perforce checkout separate to your git repository. This is the basic procedure: cd path/to/your/perforce/checkout - git-p4.py submit --git-dir=/path/to/your/git/repository + git-p4 submit --git-dir=/path/to/your/git/repository This will create a temporary git branch, use git-rev-list to find out which git commits are in your current branch but not in the "origin" branch. You can @@ -73,7 +73,7 @@ submit template using "e". If a submit fails you may have to "p4 resolve" and submit manually. You can continue importing the remaining changes with - git-p4.py submit --git-dir=/path/to/your/git/repository --continue + git-p4 submit --git-dir=/path/to/your/git/repository --continue After submitting you should sync your origin branch from Perforce using git-p4's sync command. -- cgit v1.2.3 From a559b289bd78d5a3fac0f908be7d5ff92ad09dcb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 20:27:41 +0100 Subject: Changed the format of the imported log message slightly, so that it's easier to parse again. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 06858844e5..a5b6d94d1a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -506,7 +506,7 @@ class GitSync(Command): self.gitStream.write("data < 0: -- cgit v1.2.3 From f5816a5522763f46e075cc618eab12110107a917 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 20:36:28 +0100 Subject: Changed the default branch for imports from "master" to "p4" Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- contrib/fast-import/git-p4.txt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a5b6d94d1a..669cf95d74 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -352,7 +352,7 @@ class GitSync(Command): self.knownBranches = Set() self.createdBranches = Set() self.committedChanges = Set() - self.branch = "master" + self.branch = "p4" self.detectBranches = False self.changesFile = "" diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 5786bffad4..0d30aff64c 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -17,9 +17,9 @@ The procedure is simple: git init git-p4 sync //path/in/your/perforce/depot -This will import the current head revision of the specified depot path into the -master branch of your git repository. You can use the --branch=mybranch option -to let git-p4 import from Perforce into a git branch of your choice. +This will import the current head revision of the specified depot path into a +"p4" branch of your git repository. You can use the --branch=mybranch option +to use a different branch. If you want to import the entire history of a given depot path just use -- cgit v1.2.3 From 6ae8de88f53f92dd593c5d03b67d0194e7d4eefe Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 21:10:25 +0100 Subject: Added some helper function(s) to parse the depot path and change number from the log message Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 48 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 669cf95d74..6ead1c4173 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -52,12 +52,44 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) +def extractLogMessageFromGitCommit(commit): + logMessage = "" + foundTitle = False + for log in os.popen("git-cat-file commit %s" % commit).readlines(): + if not foundTitle: + if len(log) == 1: + foundTitle = 1 + continue + + logMessage += log + return logMessage + +def extractDepotPathAndChangeFromGitLog(log): + values = {} + for line in log.split("\n"): + line = line.strip() + if line.startswith("[git-p4:") and line.endswith("]"): + line = line[8:-1].strip() + for assignment in line.split(":"): + variable = assignment.strip() + value = "" + equalPos = assignment.find("=") + if equalPos != -1: + variable = assignment[:equalPos].strip() + value = assignment[equalPos + 1:].strip() + if value.startswith("\"") and value.endswith("\""): + value = value[1:-1] + values[variable] = value + + return values.get("depot-path"), values.get("change") + class Command: def __init__(self): self.usage = "usage: %prog [options]" class P4Debug(Command): def __init__(self): + Command.__init__(self) self.options = [ ] self.description = "A tool to debug the output of p4 -G." @@ -208,17 +240,9 @@ class P4Sync(Command): system("p4 revert %s" % f) system("p4 delete %s" % f) - logMessage = "" - foundTitle = False - for log in os.popen("git-cat-file commit %s" % id).readlines(): - if not foundTitle: - if len(log) == 1: - foundTitle = 1 - continue - - if len(logMessage) > 0: - logMessage += "\t" - logMessage += log + logMessage = extractLogMessageFromGitCommit(id) + logMessage = logMessage.replace("\n", "\n\t") + logMessage = logMessage[:-1] template = os.popen("p4 change -o").read() @@ -506,7 +530,7 @@ class GitSync(Command): self.gitStream.write("data < 0: -- cgit v1.2.3 From 8136a6399c886dec8d1b806a7d8728324e906729 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 21:27:14 +0100 Subject: Helper function to check the existance of a revision Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6ead1c4173..b21dee3199 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -83,6 +83,9 @@ def extractDepotPathAndChangeFromGitLog(log): return values.get("depot-path"), values.get("change") +def gitBranchExists(branch): + return os.system("git-rev-parse %s 2>/dev/null >/dev/null") == 0 + class Command: def __init__(self): self.usage = "usage: %prog [options]" -- cgit v1.2.3 From 569d1bd4092aacd44a826852e087d0f0e9928ce8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 21:34:16 +0100 Subject: Set the default branch in run, not in the constructor Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b21dee3199..e6a34f4f47 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -379,7 +379,7 @@ class GitSync(Command): self.knownBranches = Set() self.createdBranches = Set() self.committedChanges = Set() - self.branch = "p4" + self.branch = "" self.detectBranches = False self.changesFile = "" @@ -706,6 +706,9 @@ class GitSync(Command): self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" def run(self, args): + if len(self.branch) == 0: + self.branch = "p4" + self.branch = "refs/heads/" + self.branch self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() if len(self.globalPrefix) != 0: -- cgit v1.2.3 From 179caebff4a917dc35c8166ec183bc5c76df53e1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 22:17:42 +0100 Subject: Brand new smart incremental import that doesn't need tags or git repo-config :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 38 +++++++++++++++++++++++++++++++------- contrib/fast-import/git-p4.txt | 6 ++---- 2 files changed, 33 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e6a34f4f47..8684e4b20f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -84,7 +84,9 @@ def extractDepotPathAndChangeFromGitLog(log): return values.get("depot-path"), values.get("change") def gitBranchExists(branch): - return os.system("git-rev-parse %s 2>/dev/null >/dev/null") == 0 + if os.system("git-rev-parse %s 2>/dev/null >/dev/null" % branch) == 0: + return True + return False class Command: def __init__(self): @@ -706,17 +708,40 @@ class GitSync(Command): self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" def run(self, args): + self.globalPrefix = "" + self.changeRange = "" + self.initialParent = "" + self.tagLastChange = True + if len(self.branch) == 0: self.branch = "p4" + if len(args) == 0: + if not gitBranchExists(self.branch) and gitBranchExists("origin"): + if not self.silent: + print "Creating %s branch in git repository based on origin" % self.branch + system("git branch %s origin" % self.branch) + + [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) + if len(self.previousDepotPath) > 0 and len(p4Change) > 0: + p4Change = int(p4Change) + 1 + self.globalPrefix = self.previousDepotPath + self.changeRange = "@%s,#head" % p4Change + self.initialParent = self.branch + self.tagLastChange = False + if not self.silent: + print "Performing incremental import into %s git branch" % self.branch self.branch = "refs/heads/" + self.branch - self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() + + if len(self.globalPrefix) == 0: + self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() + if len(self.globalPrefix) != 0: self.globalPrefix = self.globalPrefix[:-1] if len(args) == 0 and len(self.globalPrefix) != 0: if not self.silent: - print "[using previously specified depot path %s]" % self.globalPrefix + print "Depot path: %s" % self.globalPrefix elif len(args) != 1: return False else: @@ -725,10 +750,8 @@ class GitSync(Command): sys.exit(1) self.globalPrefix = args[0] - self.changeRange = "" self.revision = "" self.users = {} - self.initialParent = "" self.lastChange = 0 self.initialTag = "" @@ -890,8 +913,9 @@ class GitSync(Command): if not self.silent: print "" - self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) - self.gitStream.write("from %s\n\n" % self.branch); + if self.tagLastChange: + self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) + self.gitStream.write("from %s\n\n" % self.branch); self.gitStream.close() diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 0d30aff64c..4319c82dcd 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -38,11 +38,9 @@ Incremental Imports After an initial import you can easily synchronize your git repository with newer changes from the Perforce depot by just calling - git-p4.p4 sync + git-p4 sync -in your git repository. git-p4 stores the depot path of the original import in -the .git/config file and remembers the last imported p4 revision as a git tag -called p4/ . +in your git repository. It is recommended to run 'git repack -a -d -f' from time to time when using incremental imports to optimally combine the individual git packs that each -- cgit v1.2.3 From 9512497bcf574a2f70e43be0bcb58e35fb6aaba8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 23 Mar 2007 09:16:07 +0100 Subject: Make it possible to run git-p4 submit from within the git repository Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 38 +++++++++++++++++++++++++++++++++++++- contrib/fast-import/git-p4.txt | 27 +++++++++++++-------------- 2 files changed, 50 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 8684e4b20f..a8f7cce25d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -162,7 +162,7 @@ class P4Sync(Command): self.dryRun = False self.substFile = "" self.firstTime = True - self.origin = "origin" + self.origin = "" self.master = "" self.applyAsPatch = True @@ -304,6 +304,42 @@ class P4Sync(Command): print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) def run(self, args): + global gitdir + # make gitdir absolute so we can cd out into the perforce checkout + gitdir = os.path.abspath(gitdir) + os.environ["GIT_DIR"] = gitdir + depotPath = "" + if gitBranchExists("p4"): + [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) + if len(depotPath) == 0 and gitBranchExists("origin"): + [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) + + if len(depotPath) == 0: + print "Internal error: cannot locate perforce depot path from existing branches" + sys.exit(128) + + if not depotPath.endswith("/"): + depotPath += "/" + clientPath = p4Cmd("where %s..." % depotPath).get("path") + if clientPath.endswith("..."): + clientPath = clientPath[:-3] + + if len(clientPath) == 0: + print "Error: Cannot locate perforce checkout of %s in client view" % depotPath + sys.exit(128) + + print "Perforce checkout for depot path %s located at %s" % (depotPath, clientPath) + os.chdir(clientPath) + response = raw_input("Do you want to sync %s with p4 sync? (y/n)" % clientPath) + if response == "y" or response == "yes": + system("p4 sync ...") + + if len(self.origin) == 0: + if gitBranchExists("p4"): + self.origin = "p4" + else: + self.origin = "origin" + if self.reset: self.firstTime = True diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 4319c82dcd..8bf0805c74 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -51,28 +51,27 @@ Submitting git-p4 has EXPERIMENTAL support for submitting changes from a git repository back to a Perforce depot. This requires a Perforce checkout separate to your -git repository. This is the basic procedure: +git repository. All it should take is calling - cd path/to/your/perforce/checkout - git-p4 submit --git-dir=/path/to/your/git/repository + git-p4 submit -This will create a temporary git branch, use git-rev-list to find out which git -commits are in your current branch but not in the "origin" branch. You can -override the name of the "origin" branch by using the --origin=mybranch option. -The "origin" branch has to be the branch populated with git-p4's sync -operation. +in your git repository. This will attempt to locate the perforce checkout +corresponding to your imported depot path. By default the changes between your +current branch and the "p4" branch will be submitted. If there is no "p4" +branch the "origin" branch will be used as reference instead. You can override +this with the --origin=mysourcebranch option. The "origin" branch has to be the +branch populated with git-p4's sync operation. After some preparations (which might take a while) git-p4 enters a loop where it will first show a Perforce submit template and a diff of the change to -apply. After quitting the pager with 'q' git-p4 asks for confirmation for -issuing the "p4 submit" command and also gives you the option of editing the -submit template using "e". +apply in the editor. After saving and exiting the editor you will be asked whether +you really want to submit the change or not. If a submit fails you may have to "p4 resolve" and submit manually. You can continue importing the remaining changes with - git-p4 submit --git-dir=/path/to/your/git/repository --continue + git-p4 submit --continue -After submitting you should sync your origin branch from Perforce using -git-p4's sync command. +After submitting you should sync your perforce import branch ("p4" or "origin") +from Perforce using git-p4's sync command. -- cgit v1.2.3 From 967f72e21b9f38725d64a7520bca99bbef9e8ab3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 23 Mar 2007 09:30:41 +0100 Subject: Use the new incremental import style by default Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a8f7cce25d..61978c3da6 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -751,21 +751,22 @@ class GitSync(Command): if len(self.branch) == 0: self.branch = "p4" - if len(args) == 0: - if not gitBranchExists(self.branch) and gitBranchExists("origin"): - if not self.silent: - print "Creating %s branch in git repository based on origin" % self.branch - system("git branch %s origin" % self.branch) - - [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) - if len(self.previousDepotPath) > 0 and len(p4Change) > 0: - p4Change = int(p4Change) + 1 - self.globalPrefix = self.previousDepotPath - self.changeRange = "@%s,#head" % p4Change - self.initialParent = self.branch - self.tagLastChange = False - if not self.silent: - print "Performing incremental import into %s git branch" % self.branch + + if len(args) == 0: + if not gitBranchExists(self.branch) and gitBranchExists("origin"): + if not self.silent: + print "Creating %s branch in git repository based on origin" % self.branch + system("git branch %s origin" % self.branch) + + [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) + if len(self.previousDepotPath) > 0 and len(p4Change) > 0: + p4Change = int(p4Change) + 1 + self.globalPrefix = self.previousDepotPath + self.changeRange = "@%s,#head" % p4Change + self.initialParent = self.branch + self.tagLastChange = False + if not self.silent: + print "Performing incremental import into %s git branch" % self.branch self.branch = "refs/heads/" + self.branch -- cgit v1.2.3 From cb2c9db507cda6804f85ebbacb58a7458ab127a6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 24 Mar 2007 09:15:11 +0100 Subject: Different versions of p4 have different output for the where command ;( Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 61978c3da6..9503786207 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -36,6 +36,22 @@ def p4Cmd(cmd): result.update(entry) return result; +def p4Where(depotPath): + if not depotPath.endswith("/"): + depotPath += "/" + output = p4Cmd("where %s..." % depotPath) + clientPath = "" + if "path" in output: + clientPath = output.get("path") + elif "data" in output: + data = output.get("data") + lastSpace = data.rfind(" ") + clientPath = data[lastSpace + 1:] + + if clientPath.endswith("..."): + clientPath = clientPath[:-3] + return clientPath + def die(msg): sys.stderr.write(msg + "\n") sys.exit(1) @@ -318,11 +334,7 @@ class P4Sync(Command): print "Internal error: cannot locate perforce depot path from existing branches" sys.exit(128) - if not depotPath.endswith("/"): - depotPath += "/" - clientPath = p4Cmd("where %s..." % depotPath).get("path") - if clientPath.endswith("..."): - clientPath = clientPath[:-3] + clientPath = p4Where(depotPath) if len(clientPath) == 0: print "Error: Cannot locate perforce checkout of %s in client view" % depotPath -- cgit v1.2.3 From 274917a3d65109cfdf225615177fe27624d8461a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 24 Mar 2007 09:18:20 +0100 Subject: Minor cosmetic fixlet for the git-p4 submit sync question. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9503786207..aaa5d5ee5f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -342,7 +342,7 @@ class P4Sync(Command): print "Perforce checkout for depot path %s located at %s" % (depotPath, clientPath) os.chdir(clientPath) - response = raw_input("Do you want to sync %s with p4 sync? (y/n)" % clientPath) + response = raw_input("Do you want to sync %s with p4 sync? (y/n) " % clientPath) if response == "y" or response == "yes": system("p4 sync ...") -- cgit v1.2.3 From 9863f4055e1219904f6d26c2803a72fc6c8ce561 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 24 Mar 2007 16:35:05 +0100 Subject: Prefer git command over git-command. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index aaa5d5ee5f..09990be373 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -57,7 +57,7 @@ def die(msg): sys.exit(1) def currentGitBranch(): - return os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] + return os.popen("git name-rev HEAD").read().split(" ")[1][:-1] def isValidGitDir(path): if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): @@ -71,7 +71,7 @@ def system(cmd): def extractLogMessageFromGitCommit(commit): logMessage = "" foundTitle = False - for log in os.popen("git-cat-file commit %s" % commit).readlines(): + for log in os.popen("git cat-file commit %s" % commit).readlines(): if not foundTitle: if len(log) == 1: foundTitle = 1 @@ -100,7 +100,7 @@ def extractDepotPathAndChangeFromGitLog(log): return values.get("depot-path"), values.get("change") def gitBranchExists(branch): - if os.system("git-rev-parse %s 2>/dev/null >/dev/null" % branch) == 0: + if os.system("git rev-parse %s 2>/dev/null >/dev/null" % branch) == 0: return True return False @@ -130,7 +130,7 @@ class P4CleanTags(Command): def run(self, args): branch = currentGitBranch() print "Cleaning out stale p4 import tags..." - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) + sout, sin, serr = popen2.popen3("git name-rev --tags `git rev-parse %s`" % branch) output = sout.read() try: tagIdx = output.index(" tags/p4/") @@ -195,7 +195,7 @@ class P4Sync(Command): die("Cannot start sync. Previous sync config found at %s" % self.configFile) commits = [] - for line in os.popen("git-rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + for line in os.popen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): commits.append(line[:-1]) commits.reverse() @@ -229,7 +229,7 @@ class P4Sync(Command): return result def apply(self, id): - print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) + print "Applying %s" % (os.popen("git log --max-count=1 --pretty=oneline %s" % id).read()) diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() filesToAdd = set() filesToDelete = set() @@ -250,9 +250,9 @@ class P4Sync(Command): die("unknown modifier %s for %s" % (modifier, path)) if self.applyAsPatch: - system("git-diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) + system("git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) else: - system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") + system("git diff-files --name-only -z | git update-index --remove -z --stdin") system("git cherry-pick --no-commit \"%s\"" % id) for f in filesToAdd: @@ -783,7 +783,7 @@ class GitSync(Command): self.branch = "refs/heads/" + self.branch if len(self.globalPrefix) == 0: - self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() + self.globalPrefix = self.previousDepotPath = os.popen("git repo-config --get p4.depotpath").read() if len(self.globalPrefix) != 0: self.globalPrefix = self.globalPrefix[:-1] @@ -830,7 +830,7 @@ class GitSync(Command): if len(self.changeRange) == 0: try: - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % self.branch) + sout, sin, serr = popen2.popen3("git name-rev --tags `git rev-parse %s`" % self.branch) output = sout.read() if output.endswith("\n"): output = output[:-1] @@ -841,7 +841,7 @@ class GitSync(Command): endPos = caretIdx self.rev = int(output[tagIdx + 9 : endPos]) + 1 self.changeRange = "@%s,#head" % self.rev - self.initialParent = os.popen("git-rev-parse %s" % self.branch).read()[:-1] + self.initialParent = os.popen("git rev-parse %s" % self.branch).read()[:-1] self.initialTag = "p4/%s" % (int(self.rev) - 1) except: pass @@ -851,7 +851,7 @@ class GitSync(Command): if tzsign != '+' and tzsign != '-': self.tz = "+" + ("%s" % self.tz) - self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git-fast-import") + self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git fast-import") if len(self.revision) > 0: print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) @@ -971,7 +971,7 @@ class GitSync(Command): self.gitOutput.close() self.gitError.close() - os.popen("git-repo-config p4.depotpath %s" % self.globalPrefix).read() + os.popen("git repo-config p4.depotpath %s" % self.globalPrefix).read() if len(self.initialTag) > 0: os.popen("git tag -d %s" % self.initialTag).read() @@ -1031,7 +1031,7 @@ gitdir = cmd.gitdir if len(gitdir) == 0: gitdir = ".git" if not isValidGitDir(gitdir): - cdup = os.popen("git-rev-parse --show-cdup").read()[:-1] + cdup = os.popen("git rev-parse --show-cdup").read()[:-1] if isValidGitDir(cdup + "/" + gitdir): os.chdir(cdup) -- cgit v1.2.3 From e20a9e530a02fefaec31b484bd224784b8814554 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Mar 2007 00:13:51 +0200 Subject: Don't try to parse any options with git-p4 debug but pass it straight on to p4 Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 09990be373..5b023b1b7a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1018,14 +1018,18 @@ except KeyError: options = cmd.options cmd.gitdir = gitdir -options.append(optparse.make_option("--git-dir", dest="gitdir")) -parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), - options, - description = cmd.description, - formatter = HelpFormatter()) +args = sys.argv[2:] -(cmd, args) = parser.parse_args(sys.argv[2:], cmd); +if len(options) > 0: + options.append(optparse.make_option("--git-dir", dest="gitdir")) + + parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), + options, + description = cmd.description, + formatter = HelpFormatter()) + + (cmd, args) = parser.parse_args(sys.argv[2:], cmd); gitdir = cmd.gitdir if len(gitdir) == 0: -- cgit v1.2.3 From 8910ac0e888daeefdbe6f7391bece150b12b1ad0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Mar 2007 08:18:55 +0200 Subject: git-p4 debug doesn't need a git repository Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 5b023b1b7a..eb5b40aa98 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -107,6 +107,7 @@ def gitBranchExists(branch): class Command: def __init__(self): self.usage = "usage: %prog [options]" + self.needsGit = True class P4Debug(Command): def __init__(self): @@ -114,6 +115,7 @@ class P4Debug(Command): self.options = [ ] self.description = "A tool to debug the output of p4 -G." + self.needsGit = False def run(self, args): for output in p4CmdList(" ".join(args)): @@ -1031,21 +1033,22 @@ if len(options) > 0: (cmd, args) = parser.parse_args(sys.argv[2:], cmd); -gitdir = cmd.gitdir -if len(gitdir) == 0: - gitdir = ".git" - if not isValidGitDir(gitdir): - cdup = os.popen("git rev-parse --show-cdup").read()[:-1] - if isValidGitDir(cdup + "/" + gitdir): - os.chdir(cdup) +if cmd.needsGit: + gitdir = cmd.gitdir + if len(gitdir) == 0: + gitdir = ".git" + if not isValidGitDir(gitdir): + cdup = os.popen("git rev-parse --show-cdup").read()[:-1] + if isValidGitDir(cdup + "/" + gitdir): + os.chdir(cdup) -if not isValidGitDir(gitdir): - if isValidGitDir(gitdir + "/.git"): - gitdir += "/.git" - else: - die("fatal: cannot locate git repository at %s" % gitdir) + if not isValidGitDir(gitdir): + if isValidGitDir(gitdir + "/.git"): + gitdir += "/.git" + else: + die("fatal: cannot locate git repository at %s" % gitdir) -os.environ["GIT_DIR"] = gitdir + os.environ["GIT_DIR"] = gitdir if not cmd.run(args): parser.print_help() -- cgit v1.2.3 From 1f4ba1cbfc1d08a9c120012c9199caaec3dc5f58 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Mar 2007 22:34:34 +0200 Subject: Added support for mapping p4 labels to git tags Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 58 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index eb5b40aa98..eab5990548 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -625,7 +625,47 @@ class GitSync(Command): self.gitStream.write("\n") - self.lastChange = int(details["change"]) + change = int(details["change"]) + + self.lastChange = change + + if change in self.labels: + label = self.labels[change] + labelDetails = label[0] + labelRevisions = label[1] + + files = p4CmdList("files %s...@%s" % (branchPrefix, change)) + + if len(files) == len(labelRevisions): + + cleanedFiles = {} + for info in files: + if info["action"] == "delete": + continue + cleanedFiles[info["depotFile"]] = info["rev"] + + if cleanedFiles == labelRevisions: + self.gitStream.write("tag tag_%s\n" % labelDetails["label"]) + self.gitStream.write("from %s\n" % branch) + + owner = labelDetails["Owner"] + tagger = "" + if author in self.users: + tagger = "%s %s %s" % (self.users[owner], epoch, self.tz) + else: + tagger = "%s %s %s" % (owner, epoch, self.tz) + self.gitStream.write("tagger %s\n" % tagger) + self.gitStream.write("data <" + def getLabels(self): + self.labels = {} + + for output in p4CmdList("labels %s..." % self.globalPrefix): + label = output["label"] + revisions = {} + newestChange = 0 + for file in p4CmdList("files //...@%s" % label): + revisions[file["depotFile"]] = file["rev"] + change = int(file["change"]) + if change > newestChange: + newestChange = change + + self.labels[newestChange] = [output, revisions] + def run(self, args): self.globalPrefix = "" self.changeRange = "" @@ -829,6 +884,7 @@ class GitSync(Command): self.globalPrefix += "/" self.getUserMap() + self.getLabels(); if len(self.changeRange) == 0: try: -- cgit v1.2.3 From b704e589f411d51c7240d104d88422aa2d757822 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 26 Mar 2007 12:16:16 +0200 Subject: git.el: Display some information about the HEAD commit. Use git-log --pretty=oneline to print a short description of the current HEAD (and merge heads if any) in the buffer header. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 5f22dec5f7..2f9995ea39 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -263,6 +263,16 @@ and returns the process output as a string." (locale-charset-to-coding-system repo-config)) 'utf-8))) +(defun git-get-logoutput-coding-system () + "Return the coding system used for git-log output." + (let ((repo-config (or (git-config "i18n.logoutputencoding") + (git-config "i18n.commitencoding")))) + (or git-commits-coding-system + (and repo-config + (fboundp 'locale-charset-to-coding-system) + (locale-charset-to-coding-system repo-config)) + 'utf-8))) + (defun git-escape-file-name (name) "Escape a file name if necessary." (if (string-match "[\n\t\"\\]" name) @@ -406,6 +416,14 @@ and returns the process output as a string." (push (match-string 0) heads)))) (nreverse heads))) +(defun git-get-commit-description (commit) + "Get a one-line description of COMMIT." + (let ((coding-system-for-read (git-get-logoutput-coding-system))) + (let ((descr (git-call-process-env-string nil "log" "--max-count=1" "--pretty=oneline" commit))) + (if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr)) + (concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr)) + descr)))) + ;;;; File info structure ;;;; ------------------------------------------------------------ @@ -573,7 +591,7 @@ and returns the process output as a string." "Refresh the ewoc header and footer." (let ((branch (git-symbolic-ref "HEAD")) (head (if (git-empty-db-p) "Nothing committed yet" - (substring (git-rev-parse "HEAD") 0 10))) + (git-get-commit-description "HEAD"))) (merge-heads (git-get-merge-heads))) (ewoc-set-hf status (format "Directory: %s\nBranch: %s\nHead: %s%s\n" @@ -584,7 +602,7 @@ and returns the process output as a string." head (if merge-heads (concat "\nMerging: " - (mapconcat (lambda (str) (substring str 0 10)) merge-heads " ")) + (mapconcat (lambda (str) (git-get-commit-description str)) merge-heads "\n ")) "")) (if (ewoc-nth status 0) "" " No changes.")))) -- cgit v1.2.3 From a46668faf7f02c3fb55bf877e16c7ffdf8c9a129 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 28 Mar 2007 17:05:38 +0200 Subject: Fix variable usage in tag import Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index eab5990548..60c4b3dc6c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -660,11 +660,11 @@ class GitSync(Command): self.gitStream.write("EOT\n\n") else: - if not silent: + if not self.silent: print "Tag %s does not match with change %s: files do not match." % (labelDetails["label"], change) else: - if not silent: + if not self.silent: print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change) def extractFilesInCommitToBranch(self, files, branchPrefix): -- cgit v1.2.3 From c9b50e6307aa9931e18b6cd4d00de817b8e4a4c2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 29 Mar 2007 19:15:24 +0200 Subject: Fix the docs for git-p4 submit and turn git-p4 submit --master=foo into simply git-p4 submit mytopicbranch. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 18 +++++++++++------- contrib/fast-import/git-p4.txt | 23 +++++++++-------------- 2 files changed, 20 insertions(+), 21 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 60c4b3dc6c..59c3edae19 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -167,13 +167,13 @@ class P4Sync(Command): optparse.make_option("--continue", action="store_false", dest="firstTime"), optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), - optparse.make_option("--master", dest="master"), optparse.make_option("--log-substitutions", dest="substFile"), optparse.make_option("--noninteractive", action="store_false"), optparse.make_option("--dry-run", action="store_true"), optparse.make_option("--apply-as-patch", action="store_true", dest="applyAsPatch") ] self.description = "Submit changes from git to the perforce depot." + self.usage += " [name of git branch to submit into perforce depot]" self.firstTime = True self.reset = False self.interactive = True @@ -181,7 +181,6 @@ class P4Sync(Command): self.substFile = "" self.firstTime = True self.origin = "" - self.master = "" self.applyAsPatch = True self.logSubstitutions = {} @@ -326,6 +325,16 @@ class P4Sync(Command): # make gitdir absolute so we can cd out into the perforce checkout gitdir = os.path.abspath(gitdir) os.environ["GIT_DIR"] = gitdir + + if len(args) == 0: + self.master = currentGitBranch() + if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): + die("Detecting current git branch failed!") + elif len(args) == 1: + self.master = args[0] + else: + return False + depotPath = "" if gitBranchExists("p4"): [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) @@ -362,11 +371,6 @@ class P4Sync(Command): tokens = line[:-1].split("=") self.logSubstitutions[tokens[0]] = tokens[1] - if len(self.master) == 0: - self.master = currentGitBranch() - if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): - die("Detecting current git branch failed!") - self.check() self.configFile = gitdir + "/p4-git-sync.cfg" self.config = shelve.open(self.configFile, writeback=True) diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 8bf0805c74..30e2cb9a55 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -49,23 +49,19 @@ incremental import creates through the use of git-fast-import. Submitting ========== -git-p4 has EXPERIMENTAL support for submitting changes from a git repository -back to a Perforce depot. This requires a Perforce checkout separate to your -git repository. All it should take is calling +git-p4 has support for submitting changes from a git repository back to the +Perforce depot. This requires a Perforce checkout separate to your git +repository. To submit all changes that are in the current git branch but not in +the "p4" branch (or "origin" if "p4" doesn't exist) simply call git-p4 submit -in your git repository. This will attempt to locate the perforce checkout -corresponding to your imported depot path. By default the changes between your -current branch and the "p4" branch will be submitted. If there is no "p4" -branch the "origin" branch will be used as reference instead. You can override -this with the --origin=mysourcebranch option. The "origin" branch has to be the -branch populated with git-p4's sync operation. +in your git repository. If you want to submit changes in a specific branch that +is not your current git branch you can also pass that as an argument: -After some preparations (which might take a while) git-p4 enters a loop where -it will first show a Perforce submit template and a diff of the change to -apply in the editor. After saving and exiting the editor you will be asked whether -you really want to submit the change or not. + git-p4 submit mytopicbranch + +You can override the reference branch with the --origin=mysourcebranch option. If a submit fails you may have to "p4 resolve" and submit manually. You can continue importing the remaining changes with @@ -74,4 +70,3 @@ continue importing the remaining changes with After submitting you should sync your perforce import branch ("p4" or "origin") from Perforce using git-p4's sync command. - -- cgit v1.2.3 From 4557e0de5b7ef9d25cc7618ca37ae5f1acf82806 Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Fri, 30 Mar 2007 19:16:26 +0000 Subject: Reimplement emailing part of hooks--update in contrib/hooks/post-receive-email The update hook is no longer the correct place to generate emails; there is now the hooks/post-receive script which is run automatically after a ref has been updated. This patch is to make use of that new location, and to address some faults in the old update hook. The primary problem in the conversion was that in the update hook, the ref has not actually been changed, but is about to be. In the post-receive hook the ref has already been updated. That meant that where we previously had lines like: git rev-list --not --all would now give the wrong list because "--all" in the post-receive hook includes the ref that we are making the email for. This made it more difficult to show only the new revisions added by this update. The solution is not pretty; however it does work and doesn't need any changes to git-rev-list itself. It also fixes (more accurately: reduces the likelihood of) a nasty race when another update occurs while this script is running. The solution, in short, looks like this (see the source code for a longer explanation) git rev-parse --not --all | grep -v $(git rev-parse $refname) | git rev-list --pretty --stdin $oldrev..$newrev This uses git-rev-parse followed by grep to filter out the revision of the ref in question before it gets to rev-list and inhibits the output of itself. By using $(git rev-parse $revname) rather than $newrev as the filter, it also takes care of the situation where another update to the same ref has been made since $refname was $newrev. The second problem that is addressed is that of tags inhibiting the correct output of an update email. Consider this, with somebranch and sometag pointing at the same revision: git push origin somebranch git push origin sometag That would work fine; the push of the branch would generate an email containing all the new commits introduced by the update, then the push of the tag would generate the shortlog formatted tag email. Now consider: git push origin sometag git push origin somebranch When some branch comes to run its "--not --all" line, it will find sometag, and filter those commits from the email - leaving nothing. That meant that those commits would not show (in full) on any email. The fix is to not use "--all", and instead use "--branches" in the git-rev-parse command. Other changes * Lose the monstrous one-giant-script layout and put things in easy to digest functions. This makes it much easier to find the place you need to change if you wanted to customise the output. I've also tried to write more verbose comments for the same reason. The hook script is big, mainly because of all the different cases that it has to handle, so being easy to navigate is important. * All uses of "git-command" changed to "git command", to cope better if a user decided not to install all the hard links to git; * Cleaned up some of the English in the email * The fact that the receive hook makes the ref available also allows me to use Shawn Pearce's fantastic suggestion that an annotated tag can be parsed with git-for-each-ref. This removes the potentially non-portable use of "<<<" heredocs and the nasty messing around with "date" to convert numbers of seconds UTC to a real date * Deletions are now caught and notified (briefly) * To help with debugging, I've retained the command line mode from the update hook; but made it so that the output is not emailed, it's just printed to the screen. This could then be redirected if the user wanted * Removed the "Hello" from the beginning of the email - it's just noise, and no one seriously has their day made happier by "friendly" programs * The fact that it doesn't rely on repository state as an indicator any more means that it's far more stable in its output; hopefully the same arguments will always generate the same email - even if the repository changes in the future. This means you can easily recreate an email should you want to. * Included Jim Meyering's envelope sender option for the sendmail call * The hook is now so big that it was inappropriate to copy it to every repository by keeping it in the templates directory. Instead, I've put a comment saying to look in contrib/hooks, and given an example of calling the script from that template hook. The advantage of calling the script residing at some fixed location is that if a future package of git included a bug fixed version of the script, that would be picked up automatically, and the user would not have to notice and manually copy the new hook to every repository that uses it. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/hooks/post-receieve-email | 588 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 588 insertions(+) create mode 100644 contrib/hooks/post-receieve-email (limited to 'contrib') diff --git a/contrib/hooks/post-receieve-email b/contrib/hooks/post-receieve-email new file mode 100644 index 0000000000..65160153ee --- /dev/null +++ b/contrib/hooks/post-receieve-email @@ -0,0 +1,588 @@ +#!/bin/sh +# +# Copyright (c) 2007 Andy Parkins +# +# An example hook script to mail out commit update information. This hook sends emails +# listing new revisions to the repository introduced by the change being reported. The +# rule is that (for branch updates) each commit will appear on one email and one email +# only. +# +# This hook is stored in the contrib/hooks directory. Your distribution will have put +# this somewhere standard. You should make this script executable then link to it in +# the repository you would like to use it in. For example, on debian the hook is stored +# in /usr/share/doc/git-core/contrib/hooks/post-receive-email: +# +# chmod a+x post-receive-email +# cd /path/to/your/repository.git +# ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive +# +# This hook script assumes it is enabled on the central repository of a project, with +# all users pushing only to it and not between each other. It will still work if you +# don't operate in that style, but it would become possible for the email to be from +# someone other than the person doing the push. +# +# Config +# ------ +# hooks.mailinglist +# This is the list that all pushes will go to; leave it blank to not send +# emails for every ref update. +# hooks.announcelist +# This is the list that all pushes of annotated tags will go to. Leave it +# blank to default to the mailinglist field. The announce emails lists the +# short log summary of the changes since the last annotated tag. +# hook.envelopesender +# If set then the -f option is passed to sendmail to allow the envelope sender +# address to be set +# +# Notes +# ----- +# All emails have their subjects prefixed with "[SCM]" to aid filtering. +# All emails include the headers "X-Git-Refname", "X-Git-Oldrev", +# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and +# give information for debugging. +# + +# ---------------------------- Functions + +# +# Top level email generation function. This decides what type of update +# this is and calls the appropriate body-generation routine after outputting +# the common header +# +# Note this function doesn't actually generate any email output, that is taken +# care of by the functions it calls: +# - generate_email_header +# - generate_create_XXXX_email +# - generate_update_XXXX_email +# - generate_delete_XXXX_email +# - generate_email_footer +# +generate_email() +{ + # --- Arguments + oldrev=$(git rev-parse $1) + newrev=$(git rev-parse $2) + refname="$3" + + # --- Interpret + # 0000->1234 (create) + # 1234->2345 (update) + # 2345->0000 (delete) + if expr "$oldrev" : '0*$' >/dev/null + then + change_type="create" + else + if expr "$newrev" : '0*$' >/dev/null + then + change_type="delete" + else + change_type="update" + fi + fi + + # --- Get the revision types + newrev_type=$(git cat-file -t $newrev 2> /dev/null) + oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null) + case "$change_type" in + create|update) + rev="$newrev" + rev_type="$newrev_type" + ;; + delete) + rev="$oldrev" + rev_type="$oldrev_type" + ;; + esac + + # The revision type tells us what type the commit is, combined with + # the location of the ref we can decide between + # - working branch + # - tracking branch + # - unannoted tag + # - annotated tag + case "$refname","$rev_type" in + refs/tags/*,commit) + # un-annotated tag + refname_type="tag" + short_refname=${refname##refs/tags/} + ;; + refs/tags/*,tag) + # annotated tag + refname_type="annotated tag" + short_refname=${refname##refs/tags/} + # change recipients + if [ -n "$announcerecipients" ]; then + recipients="$announcerecipients" + fi + ;; + refs/heads/*,commit) + # branch + refname_type="branch" + short_refname=${refname##refs/heads/} + ;; + refs/remotes/*,commit) + # tracking branch + refname_type="tracking branch" + short_refname=${refname##refs/remotes/} + echo >&2 "*** Push-update of tracking branch, $refname" + echo >&2 "*** - no email generated." + exit 0 + ;; + *) + # Anything else (is there anything else?) + echo >&2 "*** Unknown type of update to $refname ($rev_type)" + echo >&2 "*** - no email generated" + exit 1 + ;; + esac + + # Check if we've got anyone to send to + if [ -z "$recipients" ]; then + echo >&2 "*** hooks.recipients is not set so no email will be sent" + echo >&2 "*** for $refname update $oldrev->$newrev" + exit 0 + fi + + # Email parameters + # The committer will be obtained from the latest existing rev; so + # for a deletion it will be the oldrev, for the others, then newrev + committer=$(git show --pretty=full -s $rev | sed -ne "s/^Commit: //p" | + sed -ne 's/\(.*\) /dev/null) + if [ -z "$describe" ]; then + describe=$rev + fi + + generate_email_header + + # Call the correct body generation function + fn_name=general + case "$refname_type" in + "tracking branch"|branch) + fn_name=branch + ;; + "annotated tag") + fn_name=atag + ;; + esac + generate_${change_type}_${fn_name}_email + + generate_email_footer +} + +generate_email_header() +{ + # --- Email (all stdout will be the email) + # Generate header + cat <<-EOF + From: $committer + To: $recipients + Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe + X-Git-Refname: $refname + X-Git-Reftype: $refname_type + X-Git-Oldrev: $oldrev + X-Git-Newrev: $newrev + + This is an automated email from the git hooks/post-receive script. It was + generated because a ref change was pushed to the repository containing + the project "$projectdesc". + + The $refname_type, $short_refname has been ${change_type}d + EOF +} + +generate_email_footer() +{ + cat <<-EOF + + + hooks/post-receive + -- + $projectdesc + EOF +} + +# --------------- Branches + +# +# Called for the creation of a branch +# +generate_create_branch_email() +{ + # This is a new branch and so oldrev is not valid + echo " at $newrev ($newrev_type)" + echo "" + + echo $LOGBEGIN + # This shows all log entries that are not already covered by + # another ref - i.e. commits that are now accessible from this + # ref that were previously not accessible (see generate_update_branch_email + # for the explanation of this command) + git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + git rev-list --pretty --stdin $newrev + echo $LOGEND +} + +# +# Called for the change of a pre-existing branch +# +generate_update_branch_email() +{ + # Consider this: + # 1 --- 2 --- O --- X --- 3 --- 4 --- N + # + # O is $oldrev for $refname + # N is $newrev for $refname + # X is a revision pointed to by some other ref, for which we may + # assume that an email has already been generated. + # In this case we want to issue an email containing only revisions + # 3, 4, and N. Given (almost) by + # + # git-rev-list N ^O --not --all + # + # The reason for the "almost", is that the "--not --all" will take + # precedence over the "N", and effectively will translate to + # + # git-rev-list N ^O ^X ^N + # + # So, we need to build up the list more carefully. git-rev-parse will + # generate a list of revs that may be fed into git-rev-list. We can get + # it to make the "--not --all" part and then filter out the "^N" with: + # + # git-rev-parse --not --all | grep -v N + # + # Then, using the --stdin switch to git-rev-list we have effectively + # manufactured + # + # git-rev-list N ^O ^X + # + # This leaves a problem when someone else updates the repository + # while this script is running. Their new value of the ref we're working + # on would be included in the "--not --all" output; and as our $newrev + # would be an ancestor of that commit, it would exclude all of our + # commits. What we really want is to exclude the current value of + # $refname from the --not list, rather than N itself. So: + # + # git-rev-parse --not --all | grep -v $(git-rev-parse $refname) + # + # Get's us to something pretty safe (apart from the small time between + # refname being read, and git-rev-parse running - for that, I give up) + # + # + # Next problem, consider this: + # * --- B --- * --- O ($oldrev) + # \ + # * --- X --- * --- N ($newrev) + # + # That is to say, there is no guarantee that oldrev is a strict subset of + # newrev (it would have required a --force, but that's allowed). So, we + # can't simply say rev-list $oldrev..$newrev. Instead we find the common + # base of the two revs and list from there. + # + # As above, we need to take into account the presence of X; if another + # branch is already in the repository and points at some of the revisions + # that we are about to output - we don't want them. The solution is as + # before: git-rev-parse output filtered. + # + # Finally, tags: + # 1 --- 2 --- O --- T --- 3 --- 4 --- N + # + # Tags pushed into the repository generate nice shortlog emails that + # summarise the commits between them and the previous tag. However, + # those emails don't include the full commit messages that we output + # for a branch update. Therefore we still want to output revisions + # that have been output on a tag email. + # + # Luckily, git-rev-parse includes just the tool. Instead of using "--all" + # we use "--branches"; this has the added benefit that "remotes/" will + # be ignored as well. + + # List all of the revisions that were removed by this update, in a fast forward + # update, this list will be empty, because rev-list O ^N is empty. For a non + # fast forward, O ^N is the list of removed revisions + fastforward="" + rev="" + for rev in $(git rev-list $newrev..$oldrev) + do + revtype=$(git cat-file -t "$rev") + echo " discards $rev ($revtype)" + done + if [ -z "$rev" ]; then + fast_forward=1 + fi + + # List all the revisions from baserev to newrev in a kind of + # "table-of-contents"; note this list can include revisions that have + # already had notification emails and is present to show the full detail + # of the change from rolling back the old revision to the base revision and + # then forward to the new revision + for rev in $(git rev-list $oldrev..$newrev) + do + revtype=$(git cat-file -t "$rev") + echo " via $rev ($revtype)" + done + + if [ -z "$fastforward" ]; then + echo " from $oldrev ($oldrev_type)" + else + echo "" + echo "This update added new revisions after undoing old revisions. That is to" + echo "say, the old revision is not a strict subset of the new revision. This" + echo "situation occurs when you --force push a change and generate a" + echo "repository containing something like this:" + echo "" + echo " * -- * -- B -- O -- O -- O ($oldrev)" + echo " \\" + echo " N -- N -- N ($newrev)" + echo "" + echo "When this happens we assume that you've already had alert emails for all" + echo "of the O revisions, and so we here report only the revisions in the N" + echo "branch from the common base, B." + fi + + echo "" + echo "Those revisions listed above that are new to this repository have" + echo "not appeared on any other notification email; so we list those" + echo "revisions in full, below." + + echo "" + echo $LOGBEGIN + git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + git rev-list --pretty --stdin $oldrev..$newrev + + # XXX: Need a way of detecting whether git rev-list actually outputted + # anything, so that we can issue a "no new revisions added by this + # update" message + + echo $LOGEND + + # The diffstat is shown from the old revision to the new revision. This + # is to show the truth of what happened in this change. There's no point + # showing the stat from the base to the new revision because the base + # is effectively a random revision at this point - the user will be + # interested in what this revision changed - including the undoing of + # previous revisions in the case of non-fast forward updates. + echo "" + echo "Summary of changes:" + git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev +} + +# +# Called for the deletion of a branch +# +generate_delete_branch_email() +{ + echo " was $oldrev" + echo "" + echo $LOGEND + git show -s --pretty=oneline $oldrev + echo $LOGEND +} + +# --------------- Annotated tags + +# +# Called for the creation of an annotated tag +# +generate_create_atag_email() +{ + echo " at $newrev ($newrev_type)" + + generate_atag_email +} + +# +# Called for the update of an annotated tag (this is probably a rare event +# and may not even be allowed) +# +generate_update_atag_email() +{ + echo " to $newrev ($newrev_type)" + echo " from $oldrev (which is now obsolete)" + + generate_atag_email +} + +# +# Called when an annotated tag is created or changed +# +generate_atag_email() +{ + # Use git-for-each-ref to pull out the individual fields from the tag + eval $(git for-each-ref --shell --format=' + tagobject=%(*objectname) + tagtype=%(*objecttype) + tagger=%(taggername) + tagged=%(taggerdate)' $refname + ) + + echo " tagging $tagobject ($tagtype)" + case "$tagtype" in + commit) + # If the tagged object is a commit, then we assume this is a + # release, and so we calculate which tag this tag is replacing + prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) + + if [ -n "$prevtag" ]; then + echo " replaces $prevtag" + fi + ;; + *) + echo " length $(git cat-file -s $tagobject) bytes" + ;; + esac + echo " tagged by $tagger" + echo " on $tagged" + + echo "" + echo $LOGBEGIN + + # Show the content of the tag message; this might contain a change log + # or release notes so is worth displaying. + git cat-file tag $newrev | sed -e '1,/^$/d' + + echo "" + case "$tagtype" in + commit) + # Only commit tags make sense to have rev-list operations performed + # on them + if [ -n "$prevtag" ]; then + # Show changes since the previous release + git rev-list --pretty=short "$prevtag..$newrev" | git shortlog + else + # No previous tag, show all the changes since time began + git rev-list --pretty=short $newrev | git shortlog + fi + ;; + *) + # XXX: Is there anything useful we can do for non-commit objects? + ;; + esac + + echo $LOGEND +} + +# +# Called for the deletion of an annotated tag +# +generate_delete_atag_email() +{ + echo " was $oldrev" + echo "" + echo $LOGEND + git show -s --pretty=oneline $oldrev + echo $LOGEND +} + +# --------------- General references + +# +# Called when any other type of reference is created (most likely a +# non-annotated tag) +# +generate_create_general_email() +{ + echo " at $newrev ($newrev_type)" + + generate_general_email +} + +# +# Called when any other type of reference is updated (most likely a +# non-annotated tag) +# +generate_update_general_email() +{ + echo " to $newrev ($newrev_type)" + echo " from $oldrev" + + generate_general_email +} + +# +# Called for creation or update of any other type of reference +# +generate_general_email() +{ + # Unannotated tags are more about marking a point than releasing a version; + # therefore we don't do the shortlog summary that we do for annotated tags + # above - we simply show that the point has been marked, and print the log + # message for the marked point for reference purposes + # + # Note this section also catches any other reference type (although there + # aren't any) and deals with them in the same way. + + echo "" + if [ "$newrev_type" = "commit" ]; then + echo $LOGBEGIN + git show --no-color --root -s $newrev + echo $LOGEND + else + # What can we do here? The tag marks an object that is not a commit, + # so there is no log for us to display. It's probably not wise to + # output git-cat-file as it could be a binary blob. We'll just say how + # big it is + echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." + fi +} + +# +# Called for the deletion of any other type of reference +# +generate_delete_general_email() +{ + echo " was $oldrev" + echo "" + echo $LOGEND + git show -s --pretty=oneline $oldrev + echo $LOGEND +} + +# ---------------------------- main() + +# --- Constants +EMAILPREFIX="[SCM] " +LOGBEGIN="- Log -----------------------------------------------------------------" +LOGEND="-----------------------------------------------------------------------" + +# --- Config +# Set GIT_DIR either from the working directory, or from the environment +# variable. +GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) +if [ -z "$GIT_DIR" ]; then + echo >&2 "fatal: post-receive: GIT_DIR not set" + exit 1 +fi + +projectdesc=$(sed -e '1p' "$GIT_DIR/description") +# Check if the description is unchanged from it's default, and shorten it to a +# more manageable length if it is +if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null +then + projectdesc="UNNAMED PROJECT" +fi + +recipients=$(git repo-config hooks.mailinglist) +announcerecipients=$(git repo-config hooks.announcelist) +envelopesender=$(git-repo-config hooks.envelopesender) + +# --- Main loop +# Allow dual mode: run from the command line just like the update hook, or if +# no arguments are given then run as a hook script +if [ -n "$1" -a -n "$2" -a -n "$3" ]; then + # Output to the terminal in command line mode - if someone wanted to + # resend an email; they could redirect the output to sendmail themselves + PAGER= generate_email $2 $3 $1 +else + if [ -n "$envelopesender" ]; then + envelopesender="-f '$envelopesender'" + fi + + while read oldrev newrev refname + do + generate_email $oldrev $newrev $refname | + /usr/sbin/sendmail -t $envelopesender + done +fi -- cgit v1.2.3 From 4f01748d51b530c297eeb5a0ece9af923d5db937 Mon Sep 17 00:00:00 2001 From: Julian Phillips Date: Tue, 27 Mar 2007 00:15:32 +0100 Subject: contrib/workdir: add a simple script to create a working directory Add a simple script to create a working directory that uses symlinks to point at an exisiting repository. This allows having different branches in different working directories but all from the same repository. Based on a description from Junio of how he creates multiple working directories[1]. With the following caveat: "This risks confusion for an uninitiated if you update a ref that is checked out in another working tree, but modulo that caveat it works reasonably well." [1] http://article.gmane.org/gmane.comp.version-control.git/41513/ Signed-off-by: Julian Phillips Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 57 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100755 contrib/workdir/git-new-workdir (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir new file mode 100755 index 0000000000..9877b98508 --- /dev/null +++ b/contrib/workdir/git-new-workdir @@ -0,0 +1,57 @@ +#!/bin/sh + +usage () { + echo "usage:" $@ + exit 127 +} + +die () { + echo $@ + exit 128 +} + +if test $# -lt 2 || test $# -gt 3 +then + usage "$0 []" +fi + +orig_git=$1 +new_workdir=$2 +branch=$3 + +# want to make sure that what is pointed to has a .git directory ... +test -d "$orig_git/.git" || die "\"$orig_git\" is not a git repository!" + +# don't link to a workdir +if test -L "$orig_git/.git/config" +then + die "\"$orig_git\" is a working directory only, please specify" \ + "a complete repository." +fi + +# make sure the the links use full paths +orig_git=$(cd "$orig_git"; pwd) + +# create the workdir +mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!" + +# create the links to the original repo. explictly exclude index, HEAD and +# logs/HEAD from the list since they are purely related to the current working +# directory, and should not be shared. +for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache +do + case $x in + */*) + mkdir -p "$(dirname "$new_workdir/.git/$x")" + ;; + esac + ln -s "$orig_git/.git/$x" "$new_workdir/.git/$x" +done + +# now setup the workdir +cd "$new_workdir" +# copy the HEAD from the original repository as a default branch +cp "$orig_git/.git/HEAD" .git/HEAD +# checkout the branch (either the same as HEAD from the original repository, or +# the one that was asked for) +git checkout -f $branch -- cgit v1.2.3 From 02f0559ebafffac3af362ea1b1b0ee009090efa1 Mon Sep 17 00:00:00 2001 From: Xavier Maillard Date: Mon, 26 Mar 2007 23:00:54 +0200 Subject: git-blame.el: separate git-blame-mode to ease maintenance git-blame-mode has been splitted into git-blame-mode-on and git-blame-mode-off; it now conditionnaly calls one of them depending of how we call it. Code is now easier to maintain and to understand. Fixed `git-reblame' function: interactive form was at the wrong place. String displayed on the mode line is now configurable through `git-blame-mode-line-string` (default to " blame"). Signed-off-by: Xavier Maillard Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 55 +++++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index 64ad50b327..c03ea3e4a7 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -127,39 +127,58 @@ (defvar git-blame-mode nil) (make-variable-buffer-local 'git-blame-mode) -(unless (assq 'git-blame-mode minor-mode-alist) - (setq minor-mode-alist - (cons (list 'git-blame-mode " blame") - minor-mode-alist))) + +(defvar git-blame-mode-line-string " blame" + "String to display on the mode line when git-blame is active.") + +(or (assq 'git-blame-mode minor-mode-alist) + (setq minor-mode-alist + (cons '(git-blame-mode git-blame-mode-line-string) minor-mode-alist))) ;;;###autoload (defun git-blame-mode (&optional arg) - "Minor mode for displaying Git blame" + "Toggle minor mode for displaying Git blame + +With prefix ARG, turn the mode on if ARG is positive." (interactive "P") - (if arg - (setq git-blame-mode (eq arg 1)) - (setq git-blame-mode (not git-blame-mode))) + (cond + ((null arg) + (if git-blame-mode (git-blame-mode-off) (git-blame-mode-on))) + ((> (prefix-numeric-value arg) 0) (git-blame-mode-on)) + (t (git-blame-mode-off)))) + +(defun git-blame-mode-on () + "Turn on git-blame mode. + +See also function `git-blame-mode'." (make-local-variable 'git-blame-colors) (if git-blame-autoupdate (add-hook 'after-change-functions 'git-blame-after-change nil t) (remove-hook 'after-change-functions 'git-blame-after-change t)) (git-blame-cleanup) - (if git-blame-mode - (progn - (let ((bgmode (cdr (assoc 'background-mode (frame-parameters))))) - (if (eq bgmode 'dark) - (setq git-blame-colors git-blame-dark-colors) - (setq git-blame-colors git-blame-light-colors))) - (setq git-blame-cache (make-hash-table :test 'equal)) - (git-blame-run)) - (cancel-timer git-blame-idle-timer))) + (let ((bgmode (cdr (assoc 'background-mode (frame-parameters))))) + (if (eq bgmode 'dark) + (setq git-blame-colors git-blame-dark-colors) + (setq git-blame-colors git-blame-light-colors))) + (setq git-blame-cache (make-hash-table :test 'equal)) + (setq git-blame-mode t) + (git-blame-run)) + +(defun git-blame-mode-off () + "Turn off git-blame mode. + +See also function `git-blame-mode'." + (git-blame-cleanup) + (if git-blame-idle-timer (cancel-timer git-blame-idle-timer)) + (setq git-blame-mode nil)) ;;;###autoload (defun git-reblame () "Recalculate all blame information in the current buffer" + (interactive) (unless git-blame-mode (error "git-blame is not active")) - (interactive) + (git-blame-cleanup) (git-blame-run)) -- cgit v1.2.3 From 3cc5ca3923c4db78d638fb94a3dfb70f103267bf Mon Sep 17 00:00:00 2001 From: Xavier Maillard Date: Wed, 28 Mar 2007 18:44:34 +0200 Subject: git-blame.el: pick a set of random colors for each git-blame turn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I thought it would be cool to have different set of colors for each git-blame-mode. Function `git-blame-new-commit' does this for us picking when possible, a random colors based on the set we build on startup. When it fails, `git-blame-ancient-color' will be used. We also take care not to use the same color more than once (thank you David Kågedal, really). * Prevent (future possible) namespace clash by renaming `color-scale' into `git-blame-color-scale'. Definition has been changed to be more in the "lisp" way (thanks for help to #emacs). Also added a small description of what it does. * Added docstrings at some point and instructed defvar when a variable was candidate to customisation by users. * Added missing defvar to silent byte-compilers (git-blame-file, git-blame-current) * Do not require 'cl at startup * Added more informations on compatibility Signed-off-by: Xavier Maillard Acked-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 83 ++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 28 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index c03ea3e4a7..bb671d561e 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -8,8 +8,8 @@ ;; License: GPL ;; Keywords: git, version control, release management ;; -;; Compatibility: Emacs21 - +;; Compatibility: Emacs21, Emacs22 and EmacsCVS +;; Git 1.5 and up ;; This file is *NOT* part of GNU Emacs. ;; This file is distributed under the same terms as GNU Emacs. @@ -61,8 +61,9 @@ ;;; Compatibility: ;; -;; It requires GNU Emacs 21. If you'are using Emacs 20, try -;; changing this: +;; It requires GNU Emacs 21 or later and Git 1.5.0 and up +;; +;; If you'are using Emacs 20, try changing this: ;; ;; (overlay-put ovl 'face (list :background ;; (cdr (assq 'color (cddddr info))))) @@ -77,30 +78,51 @@ ;; ;;; Code: -(require 'cl) ; to use `push', `pop' - -(defun color-scale (l) - (let* ((colors ()) - r g b) - (setq r l) - (while r - (setq g l) - (while g - (setq b l) - (while b - (push (concat "#" (car r) (car g) (car b)) colors) - (pop b)) - (pop g)) - (pop r)) - colors)) +(eval-when-compile (require 'cl)) ; to use `push', `pop' + + +(defun git-blame-color-scale (&rest elements) + "Given a list, returns a list of triples formed with each +elements of the list. + +a b => bbb bba bab baa abb aba aaa aab" + (let (result) + (dolist (a elements) + (dolist (b elements) + (dolist (c elements) + (setq result (cons (format "#%s%s%s" a b c) result))))) + result)) + +;; (git-blame-color-scale "0c" "04" "24" "1c" "2c" "34" "14" "3c") => +;; ("#3c3c3c" "#3c3c14" "#3c3c34" "#3c3c2c" "#3c3c1c" "#3c3c24" +;; "#3c3c04" "#3c3c0c" "#3c143c" "#3c1414" "#3c1434" "#3c142c" ...) + +(defmacro git-blame-random-pop (l) + "Select a random element from L and returns it. Also remove +selected element from l." + ;; only works on lists with unique elements + `(let ((e (elt ,l (random (length ,l))))) + (setq ,l (remove e ,l)) + e)) (defvar git-blame-dark-colors - (color-scale '("0c" "04" "24" "1c" "2c" "34" "14" "3c"))) + (git-blame-color-scale "0c" "04" "24" "1c" "2c" "34" "14" "3c") + "*List of colors (format #RGB) to use in a dark environment. + +To check out the list, evaluate (list-colors-display git-blame-dark-colors).") (defvar git-blame-light-colors - (color-scale '("c4" "d4" "cc" "dc" "f4" "e4" "fc" "ec"))) + (git-blame-color-scale "c4" "d4" "cc" "dc" "f4" "e4" "fc" "ec") + "*List of colors (format #RGB) to use in a light environment. + +To check out the list, evaluate (list-colors-display git-blame-light-colors).") -(defvar git-blame-ancient-color "dark green") +(defvar git-blame-colors '() + "Colors used by git-blame. The list is built once when activating git-blame +minor mode.") + +(defvar git-blame-ancient-color "dark green" + "*Color to be used for ancient commit.") (defvar git-blame-autoupdate t "*Automatically update the blame display while editing") @@ -125,6 +147,10 @@ "A queue of update requests") (make-variable-buffer-local 'git-blame-update-queue) +;; FIXME: docstrings +(defvar git-blame-file nil) +(defvar git-blame-current nil) + (defvar git-blame-mode nil) (make-variable-buffer-local 'git-blame-mode) @@ -177,7 +203,7 @@ See also function `git-blame-mode'." "Recalculate all blame information in the current buffer" (interactive) (unless git-blame-mode - (error "git-blame is not active")) + (error "Git-blame is not active")) (git-blame-cleanup) (git-blame-run)) @@ -294,7 +320,6 @@ See also function `git-blame-mode'." (t nil))) - (defun git-blame-new-commit (hash src-line res-line num-lines) (save-excursion (set-buffer git-blame-file) @@ -302,9 +327,11 @@ See also function `git-blame-mode'." (inhibit-point-motion-hooks t) (inhibit-modification-hooks t)) (when (not info) - (let ((color (pop git-blame-colors))) - (unless color - (setq color git-blame-ancient-color)) + ;; Assign a random color to each new commit info + ;; Take care not to select the same color multiple times + (let ((color (if git-blame-colors + (git-blame-random-pop git-blame-colors) + git-blame-ancient-color))) (setq info (list hash src-line res-line num-lines (git-describe-commit hash) (cons 'color color)))) -- cgit v1.2.3 From 2a9489c0249c8aef6c6ead501bae25771e710e12 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 1 Apr 2007 13:39:39 +0200 Subject: Fix "compilation" :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 59c3edae19..0443337359 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -416,7 +416,7 @@ class GitSync(Command): optparse.make_option("--changesfile", dest="changesFile"), optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--known-branches", dest="knownBranches"), - optparse.make_option("--cache", dest="doCache", action="store_true"), + optparse.make_option("--data-cache", dest="dataCache", action="store_true"), optparse.make_option("--command-cache", dest="commandCache", action="store_true") ] self.description = """Imports from Perforce into a git repository.\n @@ -500,7 +500,7 @@ class GitSync(Command): if knownBranch: continue - for branch in knownBranches: + for branch in self.knownBranches: #if relativePath.startswith(branch): if self.isSubPathOf(relativePath, branch): if len(branches) == 0: -- cgit v1.2.3 From 711544b00c22b1c2559333e8925e449812f9e5cf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 1 Apr 2007 15:40:46 +0200 Subject: Clean up python class names. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0443337359..24c8e66e87 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -160,7 +160,7 @@ class P4CleanTags(Command): print "%s tags removed." % len(allTags) return True -class P4Sync(Command): +class P4Submit(Command): def __init__(self): Command.__init__(self) self.options = [ @@ -407,7 +407,7 @@ class P4Sync(Command): return True -class GitSync(Command): +class P4Sync(Command): def __init__(self): Command.__init__(self) self.options = [ @@ -1060,8 +1060,8 @@ def printUsage(commands): commands = { "debug" : P4Debug(), "clean-tags" : P4CleanTags(), - "submit" : P4Sync(), - "sync" : GitSync() + "submit" : P4Submit(), + "sync" : P4Sync() } if len(sys.argv[1:]) == 0: -- cgit v1.2.3 From 5850cb645d3ca44c3bc014f92672dae6394c0315 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Wed, 4 Apr 2007 11:52:12 +0000 Subject: rename contrib/hooks/post-receieve-email to contrib/hooks/post-receive-email. $ git grep post-receieve-email $ git grep post-receive-email templates/hooks--post-receive:#. /usr/share/doc/git-core/contrib/hooks/post-receive-email $ Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receieve-email | 588 -------------------------------------- contrib/hooks/post-receive-email | 588 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 588 insertions(+), 588 deletions(-) delete mode 100644 contrib/hooks/post-receieve-email create mode 100644 contrib/hooks/post-receive-email (limited to 'contrib') diff --git a/contrib/hooks/post-receieve-email b/contrib/hooks/post-receieve-email deleted file mode 100644 index 65160153ee..0000000000 --- a/contrib/hooks/post-receieve-email +++ /dev/null @@ -1,588 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2007 Andy Parkins -# -# An example hook script to mail out commit update information. This hook sends emails -# listing new revisions to the repository introduced by the change being reported. The -# rule is that (for branch updates) each commit will appear on one email and one email -# only. -# -# This hook is stored in the contrib/hooks directory. Your distribution will have put -# this somewhere standard. You should make this script executable then link to it in -# the repository you would like to use it in. For example, on debian the hook is stored -# in /usr/share/doc/git-core/contrib/hooks/post-receive-email: -# -# chmod a+x post-receive-email -# cd /path/to/your/repository.git -# ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive -# -# This hook script assumes it is enabled on the central repository of a project, with -# all users pushing only to it and not between each other. It will still work if you -# don't operate in that style, but it would become possible for the email to be from -# someone other than the person doing the push. -# -# Config -# ------ -# hooks.mailinglist -# This is the list that all pushes will go to; leave it blank to not send -# emails for every ref update. -# hooks.announcelist -# This is the list that all pushes of annotated tags will go to. Leave it -# blank to default to the mailinglist field. The announce emails lists the -# short log summary of the changes since the last annotated tag. -# hook.envelopesender -# If set then the -f option is passed to sendmail to allow the envelope sender -# address to be set -# -# Notes -# ----- -# All emails have their subjects prefixed with "[SCM]" to aid filtering. -# All emails include the headers "X-Git-Refname", "X-Git-Oldrev", -# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and -# give information for debugging. -# - -# ---------------------------- Functions - -# -# Top level email generation function. This decides what type of update -# this is and calls the appropriate body-generation routine after outputting -# the common header -# -# Note this function doesn't actually generate any email output, that is taken -# care of by the functions it calls: -# - generate_email_header -# - generate_create_XXXX_email -# - generate_update_XXXX_email -# - generate_delete_XXXX_email -# - generate_email_footer -# -generate_email() -{ - # --- Arguments - oldrev=$(git rev-parse $1) - newrev=$(git rev-parse $2) - refname="$3" - - # --- Interpret - # 0000->1234 (create) - # 1234->2345 (update) - # 2345->0000 (delete) - if expr "$oldrev" : '0*$' >/dev/null - then - change_type="create" - else - if expr "$newrev" : '0*$' >/dev/null - then - change_type="delete" - else - change_type="update" - fi - fi - - # --- Get the revision types - newrev_type=$(git cat-file -t $newrev 2> /dev/null) - oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null) - case "$change_type" in - create|update) - rev="$newrev" - rev_type="$newrev_type" - ;; - delete) - rev="$oldrev" - rev_type="$oldrev_type" - ;; - esac - - # The revision type tells us what type the commit is, combined with - # the location of the ref we can decide between - # - working branch - # - tracking branch - # - unannoted tag - # - annotated tag - case "$refname","$rev_type" in - refs/tags/*,commit) - # un-annotated tag - refname_type="tag" - short_refname=${refname##refs/tags/} - ;; - refs/tags/*,tag) - # annotated tag - refname_type="annotated tag" - short_refname=${refname##refs/tags/} - # change recipients - if [ -n "$announcerecipients" ]; then - recipients="$announcerecipients" - fi - ;; - refs/heads/*,commit) - # branch - refname_type="branch" - short_refname=${refname##refs/heads/} - ;; - refs/remotes/*,commit) - # tracking branch - refname_type="tracking branch" - short_refname=${refname##refs/remotes/} - echo >&2 "*** Push-update of tracking branch, $refname" - echo >&2 "*** - no email generated." - exit 0 - ;; - *) - # Anything else (is there anything else?) - echo >&2 "*** Unknown type of update to $refname ($rev_type)" - echo >&2 "*** - no email generated" - exit 1 - ;; - esac - - # Check if we've got anyone to send to - if [ -z "$recipients" ]; then - echo >&2 "*** hooks.recipients is not set so no email will be sent" - echo >&2 "*** for $refname update $oldrev->$newrev" - exit 0 - fi - - # Email parameters - # The committer will be obtained from the latest existing rev; so - # for a deletion it will be the oldrev, for the others, then newrev - committer=$(git show --pretty=full -s $rev | sed -ne "s/^Commit: //p" | - sed -ne 's/\(.*\) /dev/null) - if [ -z "$describe" ]; then - describe=$rev - fi - - generate_email_header - - # Call the correct body generation function - fn_name=general - case "$refname_type" in - "tracking branch"|branch) - fn_name=branch - ;; - "annotated tag") - fn_name=atag - ;; - esac - generate_${change_type}_${fn_name}_email - - generate_email_footer -} - -generate_email_header() -{ - # --- Email (all stdout will be the email) - # Generate header - cat <<-EOF - From: $committer - To: $recipients - Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe - X-Git-Refname: $refname - X-Git-Reftype: $refname_type - X-Git-Oldrev: $oldrev - X-Git-Newrev: $newrev - - This is an automated email from the git hooks/post-receive script. It was - generated because a ref change was pushed to the repository containing - the project "$projectdesc". - - The $refname_type, $short_refname has been ${change_type}d - EOF -} - -generate_email_footer() -{ - cat <<-EOF - - - hooks/post-receive - -- - $projectdesc - EOF -} - -# --------------- Branches - -# -# Called for the creation of a branch -# -generate_create_branch_email() -{ - # This is a new branch and so oldrev is not valid - echo " at $newrev ($newrev_type)" - echo "" - - echo $LOGBEGIN - # This shows all log entries that are not already covered by - # another ref - i.e. commits that are now accessible from this - # ref that were previously not accessible (see generate_update_branch_email - # for the explanation of this command) - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $newrev - echo $LOGEND -} - -# -# Called for the change of a pre-existing branch -# -generate_update_branch_email() -{ - # Consider this: - # 1 --- 2 --- O --- X --- 3 --- 4 --- N - # - # O is $oldrev for $refname - # N is $newrev for $refname - # X is a revision pointed to by some other ref, for which we may - # assume that an email has already been generated. - # In this case we want to issue an email containing only revisions - # 3, 4, and N. Given (almost) by - # - # git-rev-list N ^O --not --all - # - # The reason for the "almost", is that the "--not --all" will take - # precedence over the "N", and effectively will translate to - # - # git-rev-list N ^O ^X ^N - # - # So, we need to build up the list more carefully. git-rev-parse will - # generate a list of revs that may be fed into git-rev-list. We can get - # it to make the "--not --all" part and then filter out the "^N" with: - # - # git-rev-parse --not --all | grep -v N - # - # Then, using the --stdin switch to git-rev-list we have effectively - # manufactured - # - # git-rev-list N ^O ^X - # - # This leaves a problem when someone else updates the repository - # while this script is running. Their new value of the ref we're working - # on would be included in the "--not --all" output; and as our $newrev - # would be an ancestor of that commit, it would exclude all of our - # commits. What we really want is to exclude the current value of - # $refname from the --not list, rather than N itself. So: - # - # git-rev-parse --not --all | grep -v $(git-rev-parse $refname) - # - # Get's us to something pretty safe (apart from the small time between - # refname being read, and git-rev-parse running - for that, I give up) - # - # - # Next problem, consider this: - # * --- B --- * --- O ($oldrev) - # \ - # * --- X --- * --- N ($newrev) - # - # That is to say, there is no guarantee that oldrev is a strict subset of - # newrev (it would have required a --force, but that's allowed). So, we - # can't simply say rev-list $oldrev..$newrev. Instead we find the common - # base of the two revs and list from there. - # - # As above, we need to take into account the presence of X; if another - # branch is already in the repository and points at some of the revisions - # that we are about to output - we don't want them. The solution is as - # before: git-rev-parse output filtered. - # - # Finally, tags: - # 1 --- 2 --- O --- T --- 3 --- 4 --- N - # - # Tags pushed into the repository generate nice shortlog emails that - # summarise the commits between them and the previous tag. However, - # those emails don't include the full commit messages that we output - # for a branch update. Therefore we still want to output revisions - # that have been output on a tag email. - # - # Luckily, git-rev-parse includes just the tool. Instead of using "--all" - # we use "--branches"; this has the added benefit that "remotes/" will - # be ignored as well. - - # List all of the revisions that were removed by this update, in a fast forward - # update, this list will be empty, because rev-list O ^N is empty. For a non - # fast forward, O ^N is the list of removed revisions - fastforward="" - rev="" - for rev in $(git rev-list $newrev..$oldrev) - do - revtype=$(git cat-file -t "$rev") - echo " discards $rev ($revtype)" - done - if [ -z "$rev" ]; then - fast_forward=1 - fi - - # List all the revisions from baserev to newrev in a kind of - # "table-of-contents"; note this list can include revisions that have - # already had notification emails and is present to show the full detail - # of the change from rolling back the old revision to the base revision and - # then forward to the new revision - for rev in $(git rev-list $oldrev..$newrev) - do - revtype=$(git cat-file -t "$rev") - echo " via $rev ($revtype)" - done - - if [ -z "$fastforward" ]; then - echo " from $oldrev ($oldrev_type)" - else - echo "" - echo "This update added new revisions after undoing old revisions. That is to" - echo "say, the old revision is not a strict subset of the new revision. This" - echo "situation occurs when you --force push a change and generate a" - echo "repository containing something like this:" - echo "" - echo " * -- * -- B -- O -- O -- O ($oldrev)" - echo " \\" - echo " N -- N -- N ($newrev)" - echo "" - echo "When this happens we assume that you've already had alert emails for all" - echo "of the O revisions, and so we here report only the revisions in the N" - echo "branch from the common base, B." - fi - - echo "" - echo "Those revisions listed above that are new to this repository have" - echo "not appeared on any other notification email; so we list those" - echo "revisions in full, below." - - echo "" - echo $LOGBEGIN - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $oldrev..$newrev - - # XXX: Need a way of detecting whether git rev-list actually outputted - # anything, so that we can issue a "no new revisions added by this - # update" message - - echo $LOGEND - - # The diffstat is shown from the old revision to the new revision. This - # is to show the truth of what happened in this change. There's no point - # showing the stat from the base to the new revision because the base - # is effectively a random revision at this point - the user will be - # interested in what this revision changed - including the undoing of - # previous revisions in the case of non-fast forward updates. - echo "" - echo "Summary of changes:" - git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev -} - -# -# Called for the deletion of a branch -# -generate_delete_branch_email() -{ - echo " was $oldrev" - echo "" - echo $LOGEND - git show -s --pretty=oneline $oldrev - echo $LOGEND -} - -# --------------- Annotated tags - -# -# Called for the creation of an annotated tag -# -generate_create_atag_email() -{ - echo " at $newrev ($newrev_type)" - - generate_atag_email -} - -# -# Called for the update of an annotated tag (this is probably a rare event -# and may not even be allowed) -# -generate_update_atag_email() -{ - echo " to $newrev ($newrev_type)" - echo " from $oldrev (which is now obsolete)" - - generate_atag_email -} - -# -# Called when an annotated tag is created or changed -# -generate_atag_email() -{ - # Use git-for-each-ref to pull out the individual fields from the tag - eval $(git for-each-ref --shell --format=' - tagobject=%(*objectname) - tagtype=%(*objecttype) - tagger=%(taggername) - tagged=%(taggerdate)' $refname - ) - - echo " tagging $tagobject ($tagtype)" - case "$tagtype" in - commit) - # If the tagged object is a commit, then we assume this is a - # release, and so we calculate which tag this tag is replacing - prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) - - if [ -n "$prevtag" ]; then - echo " replaces $prevtag" - fi - ;; - *) - echo " length $(git cat-file -s $tagobject) bytes" - ;; - esac - echo " tagged by $tagger" - echo " on $tagged" - - echo "" - echo $LOGBEGIN - - # Show the content of the tag message; this might contain a change log - # or release notes so is worth displaying. - git cat-file tag $newrev | sed -e '1,/^$/d' - - echo "" - case "$tagtype" in - commit) - # Only commit tags make sense to have rev-list operations performed - # on them - if [ -n "$prevtag" ]; then - # Show changes since the previous release - git rev-list --pretty=short "$prevtag..$newrev" | git shortlog - else - # No previous tag, show all the changes since time began - git rev-list --pretty=short $newrev | git shortlog - fi - ;; - *) - # XXX: Is there anything useful we can do for non-commit objects? - ;; - esac - - echo $LOGEND -} - -# -# Called for the deletion of an annotated tag -# -generate_delete_atag_email() -{ - echo " was $oldrev" - echo "" - echo $LOGEND - git show -s --pretty=oneline $oldrev - echo $LOGEND -} - -# --------------- General references - -# -# Called when any other type of reference is created (most likely a -# non-annotated tag) -# -generate_create_general_email() -{ - echo " at $newrev ($newrev_type)" - - generate_general_email -} - -# -# Called when any other type of reference is updated (most likely a -# non-annotated tag) -# -generate_update_general_email() -{ - echo " to $newrev ($newrev_type)" - echo " from $oldrev" - - generate_general_email -} - -# -# Called for creation or update of any other type of reference -# -generate_general_email() -{ - # Unannotated tags are more about marking a point than releasing a version; - # therefore we don't do the shortlog summary that we do for annotated tags - # above - we simply show that the point has been marked, and print the log - # message for the marked point for reference purposes - # - # Note this section also catches any other reference type (although there - # aren't any) and deals with them in the same way. - - echo "" - if [ "$newrev_type" = "commit" ]; then - echo $LOGBEGIN - git show --no-color --root -s $newrev - echo $LOGEND - else - # What can we do here? The tag marks an object that is not a commit, - # so there is no log for us to display. It's probably not wise to - # output git-cat-file as it could be a binary blob. We'll just say how - # big it is - echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." - fi -} - -# -# Called for the deletion of any other type of reference -# -generate_delete_general_email() -{ - echo " was $oldrev" - echo "" - echo $LOGEND - git show -s --pretty=oneline $oldrev - echo $LOGEND -} - -# ---------------------------- main() - -# --- Constants -EMAILPREFIX="[SCM] " -LOGBEGIN="- Log -----------------------------------------------------------------" -LOGEND="-----------------------------------------------------------------------" - -# --- Config -# Set GIT_DIR either from the working directory, or from the environment -# variable. -GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) -if [ -z "$GIT_DIR" ]; then - echo >&2 "fatal: post-receive: GIT_DIR not set" - exit 1 -fi - -projectdesc=$(sed -e '1p' "$GIT_DIR/description") -# Check if the description is unchanged from it's default, and shorten it to a -# more manageable length if it is -if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null -then - projectdesc="UNNAMED PROJECT" -fi - -recipients=$(git repo-config hooks.mailinglist) -announcerecipients=$(git repo-config hooks.announcelist) -envelopesender=$(git-repo-config hooks.envelopesender) - -# --- Main loop -# Allow dual mode: run from the command line just like the update hook, or if -# no arguments are given then run as a hook script -if [ -n "$1" -a -n "$2" -a -n "$3" ]; then - # Output to the terminal in command line mode - if someone wanted to - # resend an email; they could redirect the output to sendmail themselves - PAGER= generate_email $2 $3 $1 -else - if [ -n "$envelopesender" ]; then - envelopesender="-f '$envelopesender'" - fi - - while read oldrev newrev refname - do - generate_email $oldrev $newrev $refname | - /usr/sbin/sendmail -t $envelopesender - done -fi diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email new file mode 100644 index 0000000000..65160153ee --- /dev/null +++ b/contrib/hooks/post-receive-email @@ -0,0 +1,588 @@ +#!/bin/sh +# +# Copyright (c) 2007 Andy Parkins +# +# An example hook script to mail out commit update information. This hook sends emails +# listing new revisions to the repository introduced by the change being reported. The +# rule is that (for branch updates) each commit will appear on one email and one email +# only. +# +# This hook is stored in the contrib/hooks directory. Your distribution will have put +# this somewhere standard. You should make this script executable then link to it in +# the repository you would like to use it in. For example, on debian the hook is stored +# in /usr/share/doc/git-core/contrib/hooks/post-receive-email: +# +# chmod a+x post-receive-email +# cd /path/to/your/repository.git +# ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive +# +# This hook script assumes it is enabled on the central repository of a project, with +# all users pushing only to it and not between each other. It will still work if you +# don't operate in that style, but it would become possible for the email to be from +# someone other than the person doing the push. +# +# Config +# ------ +# hooks.mailinglist +# This is the list that all pushes will go to; leave it blank to not send +# emails for every ref update. +# hooks.announcelist +# This is the list that all pushes of annotated tags will go to. Leave it +# blank to default to the mailinglist field. The announce emails lists the +# short log summary of the changes since the last annotated tag. +# hook.envelopesender +# If set then the -f option is passed to sendmail to allow the envelope sender +# address to be set +# +# Notes +# ----- +# All emails have their subjects prefixed with "[SCM]" to aid filtering. +# All emails include the headers "X-Git-Refname", "X-Git-Oldrev", +# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and +# give information for debugging. +# + +# ---------------------------- Functions + +# +# Top level email generation function. This decides what type of update +# this is and calls the appropriate body-generation routine after outputting +# the common header +# +# Note this function doesn't actually generate any email output, that is taken +# care of by the functions it calls: +# - generate_email_header +# - generate_create_XXXX_email +# - generate_update_XXXX_email +# - generate_delete_XXXX_email +# - generate_email_footer +# +generate_email() +{ + # --- Arguments + oldrev=$(git rev-parse $1) + newrev=$(git rev-parse $2) + refname="$3" + + # --- Interpret + # 0000->1234 (create) + # 1234->2345 (update) + # 2345->0000 (delete) + if expr "$oldrev" : '0*$' >/dev/null + then + change_type="create" + else + if expr "$newrev" : '0*$' >/dev/null + then + change_type="delete" + else + change_type="update" + fi + fi + + # --- Get the revision types + newrev_type=$(git cat-file -t $newrev 2> /dev/null) + oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null) + case "$change_type" in + create|update) + rev="$newrev" + rev_type="$newrev_type" + ;; + delete) + rev="$oldrev" + rev_type="$oldrev_type" + ;; + esac + + # The revision type tells us what type the commit is, combined with + # the location of the ref we can decide between + # - working branch + # - tracking branch + # - unannoted tag + # - annotated tag + case "$refname","$rev_type" in + refs/tags/*,commit) + # un-annotated tag + refname_type="tag" + short_refname=${refname##refs/tags/} + ;; + refs/tags/*,tag) + # annotated tag + refname_type="annotated tag" + short_refname=${refname##refs/tags/} + # change recipients + if [ -n "$announcerecipients" ]; then + recipients="$announcerecipients" + fi + ;; + refs/heads/*,commit) + # branch + refname_type="branch" + short_refname=${refname##refs/heads/} + ;; + refs/remotes/*,commit) + # tracking branch + refname_type="tracking branch" + short_refname=${refname##refs/remotes/} + echo >&2 "*** Push-update of tracking branch, $refname" + echo >&2 "*** - no email generated." + exit 0 + ;; + *) + # Anything else (is there anything else?) + echo >&2 "*** Unknown type of update to $refname ($rev_type)" + echo >&2 "*** - no email generated" + exit 1 + ;; + esac + + # Check if we've got anyone to send to + if [ -z "$recipients" ]; then + echo >&2 "*** hooks.recipients is not set so no email will be sent" + echo >&2 "*** for $refname update $oldrev->$newrev" + exit 0 + fi + + # Email parameters + # The committer will be obtained from the latest existing rev; so + # for a deletion it will be the oldrev, for the others, then newrev + committer=$(git show --pretty=full -s $rev | sed -ne "s/^Commit: //p" | + sed -ne 's/\(.*\) /dev/null) + if [ -z "$describe" ]; then + describe=$rev + fi + + generate_email_header + + # Call the correct body generation function + fn_name=general + case "$refname_type" in + "tracking branch"|branch) + fn_name=branch + ;; + "annotated tag") + fn_name=atag + ;; + esac + generate_${change_type}_${fn_name}_email + + generate_email_footer +} + +generate_email_header() +{ + # --- Email (all stdout will be the email) + # Generate header + cat <<-EOF + From: $committer + To: $recipients + Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe + X-Git-Refname: $refname + X-Git-Reftype: $refname_type + X-Git-Oldrev: $oldrev + X-Git-Newrev: $newrev + + This is an automated email from the git hooks/post-receive script. It was + generated because a ref change was pushed to the repository containing + the project "$projectdesc". + + The $refname_type, $short_refname has been ${change_type}d + EOF +} + +generate_email_footer() +{ + cat <<-EOF + + + hooks/post-receive + -- + $projectdesc + EOF +} + +# --------------- Branches + +# +# Called for the creation of a branch +# +generate_create_branch_email() +{ + # This is a new branch and so oldrev is not valid + echo " at $newrev ($newrev_type)" + echo "" + + echo $LOGBEGIN + # This shows all log entries that are not already covered by + # another ref - i.e. commits that are now accessible from this + # ref that were previously not accessible (see generate_update_branch_email + # for the explanation of this command) + git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + git rev-list --pretty --stdin $newrev + echo $LOGEND +} + +# +# Called for the change of a pre-existing branch +# +generate_update_branch_email() +{ + # Consider this: + # 1 --- 2 --- O --- X --- 3 --- 4 --- N + # + # O is $oldrev for $refname + # N is $newrev for $refname + # X is a revision pointed to by some other ref, for which we may + # assume that an email has already been generated. + # In this case we want to issue an email containing only revisions + # 3, 4, and N. Given (almost) by + # + # git-rev-list N ^O --not --all + # + # The reason for the "almost", is that the "--not --all" will take + # precedence over the "N", and effectively will translate to + # + # git-rev-list N ^O ^X ^N + # + # So, we need to build up the list more carefully. git-rev-parse will + # generate a list of revs that may be fed into git-rev-list. We can get + # it to make the "--not --all" part and then filter out the "^N" with: + # + # git-rev-parse --not --all | grep -v N + # + # Then, using the --stdin switch to git-rev-list we have effectively + # manufactured + # + # git-rev-list N ^O ^X + # + # This leaves a problem when someone else updates the repository + # while this script is running. Their new value of the ref we're working + # on would be included in the "--not --all" output; and as our $newrev + # would be an ancestor of that commit, it would exclude all of our + # commits. What we really want is to exclude the current value of + # $refname from the --not list, rather than N itself. So: + # + # git-rev-parse --not --all | grep -v $(git-rev-parse $refname) + # + # Get's us to something pretty safe (apart from the small time between + # refname being read, and git-rev-parse running - for that, I give up) + # + # + # Next problem, consider this: + # * --- B --- * --- O ($oldrev) + # \ + # * --- X --- * --- N ($newrev) + # + # That is to say, there is no guarantee that oldrev is a strict subset of + # newrev (it would have required a --force, but that's allowed). So, we + # can't simply say rev-list $oldrev..$newrev. Instead we find the common + # base of the two revs and list from there. + # + # As above, we need to take into account the presence of X; if another + # branch is already in the repository and points at some of the revisions + # that we are about to output - we don't want them. The solution is as + # before: git-rev-parse output filtered. + # + # Finally, tags: + # 1 --- 2 --- O --- T --- 3 --- 4 --- N + # + # Tags pushed into the repository generate nice shortlog emails that + # summarise the commits between them and the previous tag. However, + # those emails don't include the full commit messages that we output + # for a branch update. Therefore we still want to output revisions + # that have been output on a tag email. + # + # Luckily, git-rev-parse includes just the tool. Instead of using "--all" + # we use "--branches"; this has the added benefit that "remotes/" will + # be ignored as well. + + # List all of the revisions that were removed by this update, in a fast forward + # update, this list will be empty, because rev-list O ^N is empty. For a non + # fast forward, O ^N is the list of removed revisions + fastforward="" + rev="" + for rev in $(git rev-list $newrev..$oldrev) + do + revtype=$(git cat-file -t "$rev") + echo " discards $rev ($revtype)" + done + if [ -z "$rev" ]; then + fast_forward=1 + fi + + # List all the revisions from baserev to newrev in a kind of + # "table-of-contents"; note this list can include revisions that have + # already had notification emails and is present to show the full detail + # of the change from rolling back the old revision to the base revision and + # then forward to the new revision + for rev in $(git rev-list $oldrev..$newrev) + do + revtype=$(git cat-file -t "$rev") + echo " via $rev ($revtype)" + done + + if [ -z "$fastforward" ]; then + echo " from $oldrev ($oldrev_type)" + else + echo "" + echo "This update added new revisions after undoing old revisions. That is to" + echo "say, the old revision is not a strict subset of the new revision. This" + echo "situation occurs when you --force push a change and generate a" + echo "repository containing something like this:" + echo "" + echo " * -- * -- B -- O -- O -- O ($oldrev)" + echo " \\" + echo " N -- N -- N ($newrev)" + echo "" + echo "When this happens we assume that you've already had alert emails for all" + echo "of the O revisions, and so we here report only the revisions in the N" + echo "branch from the common base, B." + fi + + echo "" + echo "Those revisions listed above that are new to this repository have" + echo "not appeared on any other notification email; so we list those" + echo "revisions in full, below." + + echo "" + echo $LOGBEGIN + git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + git rev-list --pretty --stdin $oldrev..$newrev + + # XXX: Need a way of detecting whether git rev-list actually outputted + # anything, so that we can issue a "no new revisions added by this + # update" message + + echo $LOGEND + + # The diffstat is shown from the old revision to the new revision. This + # is to show the truth of what happened in this change. There's no point + # showing the stat from the base to the new revision because the base + # is effectively a random revision at this point - the user will be + # interested in what this revision changed - including the undoing of + # previous revisions in the case of non-fast forward updates. + echo "" + echo "Summary of changes:" + git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev +} + +# +# Called for the deletion of a branch +# +generate_delete_branch_email() +{ + echo " was $oldrev" + echo "" + echo $LOGEND + git show -s --pretty=oneline $oldrev + echo $LOGEND +} + +# --------------- Annotated tags + +# +# Called for the creation of an annotated tag +# +generate_create_atag_email() +{ + echo " at $newrev ($newrev_type)" + + generate_atag_email +} + +# +# Called for the update of an annotated tag (this is probably a rare event +# and may not even be allowed) +# +generate_update_atag_email() +{ + echo " to $newrev ($newrev_type)" + echo " from $oldrev (which is now obsolete)" + + generate_atag_email +} + +# +# Called when an annotated tag is created or changed +# +generate_atag_email() +{ + # Use git-for-each-ref to pull out the individual fields from the tag + eval $(git for-each-ref --shell --format=' + tagobject=%(*objectname) + tagtype=%(*objecttype) + tagger=%(taggername) + tagged=%(taggerdate)' $refname + ) + + echo " tagging $tagobject ($tagtype)" + case "$tagtype" in + commit) + # If the tagged object is a commit, then we assume this is a + # release, and so we calculate which tag this tag is replacing + prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) + + if [ -n "$prevtag" ]; then + echo " replaces $prevtag" + fi + ;; + *) + echo " length $(git cat-file -s $tagobject) bytes" + ;; + esac + echo " tagged by $tagger" + echo " on $tagged" + + echo "" + echo $LOGBEGIN + + # Show the content of the tag message; this might contain a change log + # or release notes so is worth displaying. + git cat-file tag $newrev | sed -e '1,/^$/d' + + echo "" + case "$tagtype" in + commit) + # Only commit tags make sense to have rev-list operations performed + # on them + if [ -n "$prevtag" ]; then + # Show changes since the previous release + git rev-list --pretty=short "$prevtag..$newrev" | git shortlog + else + # No previous tag, show all the changes since time began + git rev-list --pretty=short $newrev | git shortlog + fi + ;; + *) + # XXX: Is there anything useful we can do for non-commit objects? + ;; + esac + + echo $LOGEND +} + +# +# Called for the deletion of an annotated tag +# +generate_delete_atag_email() +{ + echo " was $oldrev" + echo "" + echo $LOGEND + git show -s --pretty=oneline $oldrev + echo $LOGEND +} + +# --------------- General references + +# +# Called when any other type of reference is created (most likely a +# non-annotated tag) +# +generate_create_general_email() +{ + echo " at $newrev ($newrev_type)" + + generate_general_email +} + +# +# Called when any other type of reference is updated (most likely a +# non-annotated tag) +# +generate_update_general_email() +{ + echo " to $newrev ($newrev_type)" + echo " from $oldrev" + + generate_general_email +} + +# +# Called for creation or update of any other type of reference +# +generate_general_email() +{ + # Unannotated tags are more about marking a point than releasing a version; + # therefore we don't do the shortlog summary that we do for annotated tags + # above - we simply show that the point has been marked, and print the log + # message for the marked point for reference purposes + # + # Note this section also catches any other reference type (although there + # aren't any) and deals with them in the same way. + + echo "" + if [ "$newrev_type" = "commit" ]; then + echo $LOGBEGIN + git show --no-color --root -s $newrev + echo $LOGEND + else + # What can we do here? The tag marks an object that is not a commit, + # so there is no log for us to display. It's probably not wise to + # output git-cat-file as it could be a binary blob. We'll just say how + # big it is + echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." + fi +} + +# +# Called for the deletion of any other type of reference +# +generate_delete_general_email() +{ + echo " was $oldrev" + echo "" + echo $LOGEND + git show -s --pretty=oneline $oldrev + echo $LOGEND +} + +# ---------------------------- main() + +# --- Constants +EMAILPREFIX="[SCM] " +LOGBEGIN="- Log -----------------------------------------------------------------" +LOGEND="-----------------------------------------------------------------------" + +# --- Config +# Set GIT_DIR either from the working directory, or from the environment +# variable. +GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) +if [ -z "$GIT_DIR" ]; then + echo >&2 "fatal: post-receive: GIT_DIR not set" + exit 1 +fi + +projectdesc=$(sed -e '1p' "$GIT_DIR/description") +# Check if the description is unchanged from it's default, and shorten it to a +# more manageable length if it is +if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null +then + projectdesc="UNNAMED PROJECT" +fi + +recipients=$(git repo-config hooks.mailinglist) +announcerecipients=$(git repo-config hooks.announcelist) +envelopesender=$(git-repo-config hooks.envelopesender) + +# --- Main loop +# Allow dual mode: run from the command line just like the update hook, or if +# no arguments are given then run as a hook script +if [ -n "$1" -a -n "$2" -a -n "$3" ]; then + # Output to the terminal in command line mode - if someone wanted to + # resend an email; they could redirect the output to sendmail themselves + PAGER= generate_email $2 $3 $1 +else + if [ -n "$envelopesender" ]; then + envelopesender="-f '$envelopesender'" + fi + + while read oldrev newrev refname + do + generate_email $oldrev $newrev $refname | + /usr/sbin/sendmail -t $envelopesender + done +fi -- cgit v1.2.3 From 1e31fbe24fac983b0057af39824f73d65dba6502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Thu, 5 Apr 2007 21:09:31 +0300 Subject: DESTDIR support for git/contrib/emacs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit make install DESTDIR=... support for git/contrib/emacs Signed-off-by: Ville Skyttä Signed-off-by: Junio C Hamano --- contrib/emacs/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile index 8554e3967c..98aa0aae9b 100644 --- a/contrib/emacs/Makefile +++ b/contrib/emacs/Makefile @@ -11,8 +11,8 @@ emacsdir = $(prefix)/share/emacs/site-lisp all: $(ELC) install: all - $(INSTALL) -d $(emacsdir) - $(INSTALL_ELC) $(ELC) $(emacsdir) + $(INSTALL) -d $(DESTDIR)$(emacsdir) + $(INSTALL_ELC) $(ELC) $(DESTDIR)$(emacsdir) %.elc: %.el $(EMACS) -batch -f batch-byte-compile $< -- cgit v1.2.3 From 01ce1fe9676e3f714f3a2d068bf8cfe021f4073c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 7 Apr 2007 23:46:50 +0200 Subject: Added git-p4 rebase convenience Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 22 ++++++++++++++++++++-- contrib/fast-import/git-p4.txt | 14 +++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 24c8e66e87..aa85800d69 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -804,7 +804,11 @@ class P4Sync(Command): def getLabels(self): self.labels = {} - for output in p4CmdList("labels %s..." % self.globalPrefix): + l = p4CmdList("labels %s..." % self.globalPrefix) + if len(l) > 0: + print "Finding files belonging to labels in %s" % self.globalPrefix + + for output in l: label = output["label"] revisions = {} newestChange = 0 @@ -1039,6 +1043,19 @@ class P4Sync(Command): return True +class P4Rebase(Command): + def __init__(self): + Command.__init__(self) + self.options = [ ] + self.description = "Fetches the latest revision from perforce and rebases the current work (branch) against it" + + def run(self, args): + sync = P4Sync() + sync.run([]) + print "Rebasing the current branch" + system("git rebase p4") + return True + class HelpFormatter(optparse.IndentedHelpFormatter): def __init__(self): optparse.IndentedHelpFormatter.__init__(self) @@ -1061,7 +1078,8 @@ commands = { "debug" : P4Debug(), "clean-tags" : P4CleanTags(), "submit" : P4Submit(), - "sync" : P4Sync() + "sync" : P4Sync(), + "rebase" : P4Rebase() } if len(sys.argv[1:]) == 0: diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 30e2cb9a55..5f7251c2d6 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -40,12 +40,24 @@ newer changes from the Perforce depot by just calling git-p4 sync -in your git repository. +in your git repository. By default the "p4" branch is updated. It is recommended to run 'git repack -a -d -f' from time to time when using incremental imports to optimally combine the individual git packs that each incremental import creates through the use of git-fast-import. +Updating +======== + +A common working pattern is to fetch the latest changes from the Perforce depot +and merge them with local uncommitted changes. The recommended way is to use +git's rebase mechanism to preserve linear history. git-p4 provides a convenient + + git-p4 rebase + +command that calls git-p4 sync followed by git rebase to rebase the current +working branch. + Submitting ========== -- cgit v1.2.3 From 1f52af6c732bc17415474c426dc009f9e6a36a83 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 00:07:02 +0200 Subject: Provide a tree summary after git-p4 rebase Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index aa85800d69..170af90abc 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -974,7 +974,7 @@ class P4Sync(Command): if len(changes) == 0: if not self.silent: print "no changes to import!" - sys.exit(1) + return True cnt = 1 for change in changes: @@ -1053,7 +1053,9 @@ class P4Rebase(Command): sync = P4Sync() sync.run([]) print "Rebasing the current branch" + oldHead = os.popen("git rev-parse HEAD").read()[:-1] system("git rebase p4") + system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True class HelpFormatter(optparse.IndentedHelpFormatter): -- cgit v1.2.3 From cb53e1f8e9a78ab3f980250d2388a62ae2a42d77 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 00:12:02 +0200 Subject: Turn off potentially slow label detection by default Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 170af90abc..fcdfe22a45 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -417,7 +417,8 @@ class P4Sync(Command): optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--known-branches", dest="knownBranches"), optparse.make_option("--data-cache", dest="dataCache", action="store_true"), - optparse.make_option("--command-cache", dest="commandCache", action="store_true") + optparse.make_option("--command-cache", dest="commandCache", action="store_true"), + optparse.make_option("--detect-labels", dest="detectLabels", action="store_true") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -437,6 +438,7 @@ class P4Sync(Command): self.committedChanges = Set() self.branch = "" self.detectBranches = False + self.detectLabels = False self.changesFile = "" def p4File(self, depotPath): @@ -892,7 +894,9 @@ class P4Sync(Command): self.globalPrefix += "/" self.getUserMap() - self.getLabels(); + self.labels = {} + if self.detectLabels: + self.getLabels(); if len(self.changeRange) == 0: try: -- cgit v1.2.3 From 68ed351ab51c8b5731665fe6a8a7cc9651edf6c9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 09:00:55 +0200 Subject: Honor --silent for labels Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index fcdfe22a45..65660e1351 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -807,7 +807,7 @@ class P4Sync(Command): self.labels = {} l = p4CmdList("labels %s..." % self.globalPrefix) - if len(l) > 0: + if len(l) > 0 and not silent: print "Finding files belonging to labels in %s" % self.globalPrefix for output in l: -- cgit v1.2.3 From f9a3a4f796461276bbbcfef965984086e8e00b46 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 10:08:26 +0200 Subject: Added git-p4 clone convenience command Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 57 ++++++++++++++++++++++++++++++++++++++++-- contrib/fast-import/git-p4.txt | 23 ++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 65660e1351..0a22d9a2e4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -440,6 +440,7 @@ class P4Sync(Command): self.detectBranches = False self.detectLabels = False self.changesFile = "" + self.tagLastChange = True def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -826,7 +827,6 @@ class P4Sync(Command): self.globalPrefix = "" self.changeRange = "" self.initialParent = "" - self.tagLastChange = True if len(self.branch) == 0: self.branch = "p4" @@ -1062,6 +1062,58 @@ class P4Rebase(Command): system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True +class P4Clone(P4Sync): + def __init__(self): + P4Sync.__init__(self) + self.description = "Creates a new git repository and imports from Perforce into it" + self.usage = "usage: %prog [options] //depot/path[@revRange] [directory]" + self.needsGit = False + self.tagLastChange = False + + def run(self, args): + if len(args) < 1: + return False + depotPath = args[0] + dir = "" + if len(args) == 2: + dir = args[1] + elif len(args) > 2: + return False + + if not depotPath.startswith("//"): + return False + + if len(dir) == 0: + dir = depotPath + atPos = dir.rfind("@") + if atPos != -1: + dir = dir[0:atPos] + hashPos = dir.rfind("#") + if hashPos != -1: + dir = dir[0:hashPos] + + if dir.endswith("..."): + dir = dir[:-3] + + if dir.endswith("/"): + dir = dir[:-1] + + slashPos = dir.rfind("/") + if slashPos != -1: + dir = dir[slashPos + 1:] + + print "Importing from %s into %s" % (depotPath, dir) + os.makedirs(dir) + os.chdir(dir) + system("git init") + if not P4Sync.run(self, [depotPath]): + return False + os.wait() + if self.branch != "master": + system("git branch master p4") + system("git checkout -f") + return True + class HelpFormatter(optparse.IndentedHelpFormatter): def __init__(self): optparse.IndentedHelpFormatter.__init__(self) @@ -1085,7 +1137,8 @@ commands = { "clean-tags" : P4CleanTags(), "submit" : P4Submit(), "sync" : P4Sync(), - "rebase" : P4Rebase() + "rebase" : P4Rebase(), + "clone" : P4Clone() } if len(sys.argv[1:]) == 0: diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 5f7251c2d6..99ae85bd7b 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -10,7 +10,25 @@ done using "git-p4 submit". Importing ========= -The procedure is simple: +You can simply start with + + git-p4 clone //depot/path/project + +or + + git-p4 clone //depot/path/project myproject + +This will create an empty git repository in a subdirectory called "project" (or +"myproject" with the second command), import the head revision from the +specified perforce path into a git "p4" branch, create a master branch off it +and check it out. If you want the entire history (not just the head revision) then +you can simply append a "@all" to the depot path: + + git-p4 clone //depot/project/main@all myproject + + + +If you want more control you can also use the git-p4 sync command directly: mkdir repo-git cd repo-git @@ -31,6 +49,9 @@ a big import. This may take a while. Support for Perforce integrations is still work in progress. Don't bother trying it unless you want to hack on it :) +For convenience there's also the git-p4 clone command that works similar to +git-clone and combines the creation of the git repository with the the initial +import and the branch setup Incremental Imports =================== -- cgit v1.2.3 From c45b1cfe1eda2cc67c4f07a0cd6986911d7c2fd8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 10:13:32 +0200 Subject: Fix file determination for #head imports Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0a22d9a2e4..28b088544b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -938,7 +938,8 @@ class P4Sync(Command): newestRevision = change if info["action"] == "delete": - fileCnt = fileCnt + 1 + # don't increase the file cnt, otherwise details["depotFile123"] will have gaps! + #fileCnt = fileCnt + 1 continue for prop in [ "depotFile", "rev", "action", "type" ]: -- cgit v1.2.3 From 10c3211b81a2f67c3853900e462333933f910696 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 10:15:47 +0200 Subject: fix variable usage (oops) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 28b088544b..72fa48af04 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -808,7 +808,7 @@ class P4Sync(Command): self.labels = {} l = p4CmdList("labels %s..." % self.globalPrefix) - if len(l) > 0 and not silent: + if len(l) > 0 and not self.silent: print "Finding files belonging to labels in %s" % self.globalPrefix for output in l: -- cgit v1.2.3 From 7243b350b3469eb78be950331290ea16e57c3de8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 10:21:56 +0200 Subject: Added a simple example of usage to the "documentation" :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 99ae85bd7b..4f6a680f95 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -103,3 +103,25 @@ continue importing the remaining changes with After submitting you should sync your perforce import branch ("p4" or "origin") from Perforce using git-p4's sync command. + + +Example +======= + +# Clone a repository + git-p4 clone //depot/path/project +# Enter the newly cloned directory + cd project +# Do some work... + vi foo.h +# ... and commit locally to gi + git commit foo.h +# In the meantime somebody submitted changes to the Perforce depot. Rebase your latest +# changes against the latest changes in Perforce: + git-p4 rebase +# Submit your locally committed changes back to Perforce + git-p4 submit +# ... and synchronize with Perforce + git-p4 rebase + + -- cgit v1.2.3 From 80b5910fac79b22ccc8eabb2262562c913d5603c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 9 Apr 2007 12:43:40 +0200 Subject: Allow for convenient rebasing after git-p4 submit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 72fa48af04..4fbfaf11b6 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -352,6 +352,7 @@ class P4Submit(Command): sys.exit(128) print "Perforce checkout for depot path %s located at %s" % (depotPath, clientPath) + oldWorkingDirectory = os.getcwd() os.chdir(clientPath) response = raw_input("Do you want to sync %s with p4 sync? (y/n) " % clientPath) if response == "y" or response == "yes": @@ -403,6 +404,11 @@ class P4Submit(Command): print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." system("p4 edit ... >/dev/null") system("p4 revert ... >/dev/null") + response = raw_input("Do you want to sync from Perforce now using git-p4 rebase (y/n)? ") + if response == "y" or response == "yes": + os.chdir(oldWorkingDirectory) + rebase = P4Rebase() + rebase.run([]) os.remove(self.configFile) return True -- cgit v1.2.3 From fd4ca86a0b920ddde464e787ef2350126f083d75 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 13 Apr 2007 22:21:10 +0200 Subject: Print an error message of some sort if git fast-import fails. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 4fbfaf11b6..6db757ae43 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -958,6 +958,7 @@ class P4Sync(Command): try: self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) except IOError: + print "IO error with git fast-import. Is your git version recent enough?" print self.gitError.read() else: -- cgit v1.2.3 From f291b4e3d47fb03a65c0dfcfbd28f8fabb6187c1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 14 Apr 2007 11:21:50 +0200 Subject: Fix the timezone formatting. Now qgit also displays (parses) it correctly. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6db757ae43..44a07c27ce 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -922,10 +922,7 @@ class P4Sync(Command): except: pass - self.tz = - time.timezone / 36 - tzsign = ("%s" % self.tz)[0] - if tzsign != '+' and tzsign != '-': - self.tz = "+" + ("%s" % self.tz) + self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git fast-import") -- cgit v1.2.3 From 8b72ca0f76213eb375840e6b9069b260d97f8286 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 14 Apr 2007 16:05:54 +0200 Subject: Removed the old patch apply code from git-p4 submit. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 44a07c27ce..3202a819ca 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -170,7 +170,6 @@ class P4Submit(Command): optparse.make_option("--log-substitutions", dest="substFile"), optparse.make_option("--noninteractive", action="store_false"), optparse.make_option("--dry-run", action="store_true"), - optparse.make_option("--apply-as-patch", action="store_true", dest="applyAsPatch") ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -181,7 +180,6 @@ class P4Submit(Command): self.substFile = "" self.firstTime = True self.origin = "" - self.applyAsPatch = True self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -202,10 +200,6 @@ class P4Submit(Command): self.config["commits"] = commits - if not self.applyAsPatch: - print "Creating temporary p4-sync branch from %s ..." % self.origin - system("git checkout -f -b p4-sync %s" % self.origin) - def prepareLogMessage(self, template, message): result = "" @@ -250,11 +244,7 @@ class P4Submit(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - if self.applyAsPatch: - system("git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) - else: - system("git diff-files --name-only -z | git update-index --remove -z --stdin") - system("git cherry-pick --no-commit \"%s\"" % id) + system("git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) for f in filesToAdd: system("p4 add %s" % f) @@ -397,13 +387,6 @@ class P4Submit(Command): print "No changes found to apply between %s and current HEAD" % self.origin else: print "All changes applied!" - if not self.applyAsPatch: - print "Deleting temporary p4-sync branch and going back to %s" % self.master - system("git checkout %s" % self.master) - system("git branch -D p4-sync") - print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." - system("p4 edit ... >/dev/null") - system("p4 revert ... >/dev/null") response = raw_input("Do you want to sync from Perforce now using git-p4 rebase (y/n)? ") if response == "y" or response == "yes": os.chdir(oldWorkingDirectory) -- cgit v1.2.3 From 5e80dd4d7e34fdd507997615d7fdefc2411571eb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 14 Apr 2007 16:09:43 +0200 Subject: Slightly improved formatting of the raw_input questions. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3202a819ca..9c9852c75f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -277,7 +277,7 @@ class P4Submit(Command): firstIteration = True while response == "e": if not firstIteration: - response = raw_input("Do you want to submit this change (y/e/n)? ") + response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o ") firstIteration = False if response == "e": [handle, fileName] = tempfile.mkstemp() @@ -344,7 +344,7 @@ class P4Submit(Command): print "Perforce checkout for depot path %s located at %s" % (depotPath, clientPath) oldWorkingDirectory = os.getcwd() os.chdir(clientPath) - response = raw_input("Do you want to sync %s with p4 sync? (y/n) " % clientPath) + response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % clientPath) if response == "y" or response == "yes": system("p4 sync ...") @@ -387,7 +387,7 @@ class P4Submit(Command): print "No changes found to apply between %s and current HEAD" % self.origin else: print "All changes applied!" - response = raw_input("Do you want to sync from Perforce now using git-p4 rebase (y/n)? ") + response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") if response == "y" or response == "yes": os.chdir(oldWorkingDirectory) rebase = P4Rebase() -- cgit v1.2.3 From 90865adc01213520980459ddc261a019f673cce8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 15 Apr 2007 09:34:15 +0200 Subject: A new attempt at fixing the child-fast-import-process-not-finished race condition in the clone command Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9c9852c75f..b77cb20e3f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -907,7 +907,10 @@ class P4Sync(Command): self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) - self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git fast-import") + importProcess = popen2.Popen3("git fast-import", capturestderr = True) + self.gitOutput = importProcess.fromchild + self.gitStream = importProcess.tochild + self.gitError = importProcess.childerr if len(self.revision) > 0: print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) @@ -1028,6 +1031,7 @@ class P4Sync(Command): self.gitStream.close() self.gitOutput.close() self.gitError.close() + importProcess.wait() os.popen("git repo-config p4.depotpath %s" % self.globalPrefix).read() if len(self.initialTag) > 0: @@ -1096,7 +1100,6 @@ class P4Clone(P4Sync): system("git init") if not P4Sync.run(self, [depotPath]): return False - os.wait() if self.branch != "master": system("git branch master p4") system("git checkout -f") -- cgit v1.2.3 From 51a2640afdd12475642728dc3576966abe0dba6d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 15 Apr 2007 09:59:56 +0200 Subject: Handle patch errors in git-p4 submit better. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b77cb20e3f..fb13469ce2 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -244,7 +244,33 @@ class P4Submit(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - system("git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) + diffcmd = "git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\"" % (id, id) + patchcmd = diffcmd + " | patch -p1" + + if os.system(patchcmd + " --dry-run --silent") != 0: + print "Unfortunately applying the change failed!" + print "What do you want to do?" + response = "x" + while response != "s" and response != "a" and response != "w": + response = raw_input("[s]kip this patch / [a]pply the patch forcibly and with .rej files / [w]rite the patch to a file (patch.txt) ") + if response == "s": + print "Skipping! Good luck with the next patches..." + return + elif response == "a": + os.system(patchcmd) + if len(filesToAdd) > 0: + print "You may also want to call p4 add on the following files:" + print " ".join(filesToAdd) + if len(filesToDelete): + print "The following files should be scheduled for deletion with p4 delete:" + print " ".join(filesToDelete) + die("Please resolve and submit the conflict manually and continue afterwards with git-p4 submit --continue") + elif response == "w": + system(diffcmd + " > patch.txt") + print "Patch saved to patch.txt in %s !" % self.clientPath + die("Please resolve and submit the conflict manually and continue afterwards with git-p4 submit --continue") + + system(patchcmd) for f in filesToAdd: system("p4 add %s" % f) @@ -335,16 +361,16 @@ class P4Submit(Command): print "Internal error: cannot locate perforce depot path from existing branches" sys.exit(128) - clientPath = p4Where(depotPath) + self.clientPath = p4Where(depotPath) - if len(clientPath) == 0: + if len(self.clientPath) == 0: print "Error: Cannot locate perforce checkout of %s in client view" % depotPath sys.exit(128) - print "Perforce checkout for depot path %s located at %s" % (depotPath, clientPath) + print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath) oldWorkingDirectory = os.getcwd() - os.chdir(clientPath) - response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % clientPath) + os.chdir(self.clientPath) + response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath) if response == "y" or response == "yes": system("p4 sync ...") -- cgit v1.2.3 From 9398e5aa1698cdb518f7118e6f6eeebbea7f5e58 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 20 Apr 2007 02:08:47 -0400 Subject: Contribute a fairly paranoid update hook I'm using a variant of this update hook in a corporate environment where we perform some validations of the commits and tags that are being pushed. The model is a "central repository" type setup, where users are given access to push to specific branches within the shared central repository. In this particular installation we run a specially patched git-receive-pack in setuid mode via SSH, allowing all writes into the repository as the repository owner, but only if this hook blesses it. One of the major checks we perform with this hook is that the 'committer' line of a commit, or the 'tagger' line of a new annotated tag actually correlates to the UNIX user who is performing the push. Users can falsify these lines on their local repositories, but the central repository that management trusts will reject all such forgery attempts. Of course 'author' lines are still allowed to be any value, as sometimes changes do come from other individuals. Another nice feature of this hook is the access control lists for all repositories on the system can also be stored and tracked in a supporting Git repository, which can also be access controlled by itself. This allows full auditing of who-had-what-when-and-why, thanks to git-blame's data mining capabilities. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 284 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 contrib/hooks/update-paranoid (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid new file mode 100644 index 0000000000..5ee1835c80 --- /dev/null +++ b/contrib/hooks/update-paranoid @@ -0,0 +1,284 @@ +#!/usr/bin/perl + +use strict; +use File::Spec; + +$ENV{PATH} = '/opt/git/bin'; +my $acl_git = '/vcs/acls.git'; +my $acl_branch = 'refs/heads/master'; +my $debug = 0; + +=doc +Invoked as: update refname old-sha1 new-sha1 + +This script is run by git-receive-pack once for each ref that the +client is trying to modify. If we exit with a non-zero exit value +then the update for that particular ref is denied, but updates for +other refs in the same run of receive-pack may still be allowed. + +We are run after the objects have been uploaded, but before the +ref is actually modified. We take advantage of that fact when we +look for "new" commits and tags (the new objects won't show up in +`rev-list --all`). + +This script loads and parses the content of the config file +"users/$this_user.acl" from the $acl_branch commit of $acl_git ODB. +The acl file is a git-config style file, but uses a slightly more +restricted syntax as the Perl parser contained within this script +is not nearly as permissive as git-config. + +Example: + + [user] + committer = John Doe + committer = John R. Doe + + [repository "acls"] + allow = heads/master + allow = CDUR for heads/jd/ + allow = C for ^tags/v\\d+$ + +For all new commit or tag objects the committer (or tagger) line +within the object must exactly match one of the user.committer +values listed in the acl file ("HEAD:users/$this_user.acl"). + +For a branch to be modified an allow line within the matching +repository section must be matched for both the refname and the +opcode. + +Repository sections are matched on the basename of the repository +(after removing the .git suffix). + +The opcode abbrevations are: + + C: create new ref + D: delete existing ref + U: fast-forward existing ref (no commit loss) + R: rewind/rebase existing ref (commit loss) + +if no opcodes are listed before the "for" keyword then "U" (for +fast-forward update only) is assumed as this is the most common +usage. + +Refnames are matched by always assuming a prefix of "refs/". +This hook forbids pushing or deleting anything not under "refs/". + +Refnames that start with ^ are Perl regular expressions, and the ^ +is kept as part of the regexp. \\ is needed to get just one \, so +\\d expands to \d in Perl. The 3rd allow line above is an example. + +Refnames that don't start with ^ but that end with / are prefix +matches (2nd allow line above); all other refnames are strict +equality matches (1st allow line). + +Anything pushed to "heads/" (ok, really "refs/heads/") must be +a commit. Tags are not permitted here. + +Anything pushed to "tags/" (err, really "refs/tags/") must be an +annotated tag. Commits, blobs, trees, etc. are not permitted here. +Annotated tag signatures aren't checked, nor are they required. + +The special subrepository of 'info/new-commit-check' can +be created and used to allow users to push new commits and +tags from another local repository to this one, even if they +aren't the committer/tagger of those objects. In a nut shell +the info/new-commit-check directory is a Git repository whose +objects/info/alternates file lists this repository and all other +possible sources, and whose refs subdirectory contains symlinks +to this repository's refs subdirectory, and to all other possible +sources refs subdirectories. Yes, this means that you cannot +use packed-refs in those repositories as they won't be resolved +correctly. + +=cut + +my $git_dir = $ENV{GIT_DIR}; +my $new_commit_check = "$git_dir/info/new-commit-check"; +my $ref = $ARGV[0]; +my $old = $ARGV[1]; +my $new = $ARGV[2]; +my $new_type; +my ($this_user) = getpwuid $<; # REAL_USER_ID +my $repository_name; +my %user_committer; +my @allow_rules; + +sub deny ($) { + print STDERR "-Deny- $_[0]\n" if $debug; + print STDERR "\ndenied: $_[0]\n\n"; + exit 1; +} + +sub grant ($) { + print STDERR "-Grant- $_[0]\n" if $debug; + exit 0; +} + +sub info ($) { + print STDERR "-Info- $_[0]\n" if $debug; +} + +sub parse_config ($$) { + my ($data, $fn) = @_; + info "Loading $fn"; + open(I,'-|','git',"--git-dir=$acl_git",'cat-file','blob',$fn); + my $section = ''; + while () { + chomp; + if (/^\s*$/ || /^\s*#/) { + } elsif (/^\[([a-z]+)\]$/i) { + $section = $1; + } elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) { + $section = "$1.$2"; + } elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) { + push @{$data->{"$section.$1"}}, $2; + } else { + deny "bad config file line $. in $fn"; + } + } + close I; +} + +sub all_new_committers () { + local $ENV{GIT_DIR} = $git_dir; + $ENV{GIT_DIR} = $new_commit_check if -d $new_commit_check; + + info "Getting committers of new commits."; + my %used; + open(T,'-|','git','rev-list','--pretty=raw',$new,'--not','--all'); + while () { + next unless s/^committer //; + chop; + s/>.*$/>/; + info "Found $_." unless $used{$_}++; + } + close T; + info "No new commits." unless %used; + keys %used; +} + +sub all_new_taggers () { + my %exists; + open(T,'-|','git','for-each-ref','--format=%(objectname)','refs/tags'); + while () { + chop; + $exists{$_} = 1; + } + close T; + + info "Getting taggers of new tags."; + my %used; + my $obj = $new; + my $obj_type = $new_type; + while ($obj_type eq 'tag') { + last if $exists{$obj}; + $obj_type = ''; + open(T,'-|','git','cat-file','tag',$obj); + while () { + chop; + if (/^object ([a-z0-9]{40})$/) { + $obj = $1; + } elsif (/^type (.+)$/) { + $obj_type = $1; + } elsif (s/^tagger //) { + s/>.*$/>/; + info "Found $_." unless $used{$_}++; + last; + } + } + close T; + } + info "No new tags." unless %used; + keys %used; +} + +sub check_committers (@) { + my @bad; + foreach (@_) { push @bad, $_ unless $user_committer{$_}; } + if (@bad) { + print STDERR "\n"; + print STDERR "You are not $_.\n" foreach (sort @bad); + deny "You cannot push changes not committed by you."; + } +} + +sub git_value (@) { + open(T,'-|','git',@_); local $_ = ; chop; close T; + $_; +} + +deny "No GIT_DIR inherited from caller" unless $git_dir; +deny "Need a ref name" unless $ref; +deny "Refusing funny ref $ref" unless $ref =~ s,^refs/,,; +deny "Bad old value $old" unless $old =~ /^[a-z0-9]{40}$/; +deny "Bad new value $new" unless $new =~ /^[a-z0-9]{40}$/; +deny "Cannot determine who you are." unless $this_user; + +$repository_name = File::Spec->rel2abs($git_dir); +$repository_name =~ m,/([^/]+)(?:\.git|/\.git)$,; +$repository_name = $1; +info "Updating in '$repository_name'."; + +my $op; +if ($old =~ /^0{40}$/) { $op = 'C'; } +elsif ($new =~ /^0{40}$/) { $op = 'D'; } +else { $op = 'R'; } + +# This is really an update (fast-forward) if the +# merge base of $old and $new is $old. +# +$op = 'U' if ($op eq 'R' + && $ref =~ m,^heads/, + && $old eq git_value('merge-base',$old,$new)); + +# Load the user's ACL file. +{ + my %data = ('user.committer' => []); + parse_config(\%data, "$acl_branch:users/$this_user.acl"); + %user_committer = map {$_ => $_} @{$data{'user.committer'}}; + my $rules = $data{"repository.$repository_name.allow"} || []; + foreach (@$rules) { + if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { + my $ops = $1; + my $ref = $2; + $ops =~ s/ //g; + $ref =~ s/\\\\/\\/g; + push @allow_rules, [$ops, $ref]; + } elsif (/^for\s+([^\s]+)$/) { + # Mentioned, but nothing granted? + } elsif (/^[^\s]+$/) { + s/\\\\/\\/g; + push @allow_rules, ['U', $_]; + } + } +} + +if ($op ne 'D') { + $new_type = git_value('cat-file','-t',$new); + + if ($ref =~ m,^heads/,) { + deny "$ref must be a commit." unless $new_type eq 'commit'; + } elsif ($ref =~ m,^tags/,) { + deny "$ref must be an annotated tag." unless $new_type eq 'tag'; + } + + check_committers (all_new_committers); + check_committers (all_new_taggers) if $new_type eq 'tag'; +} + +info "$this_user wants $op for $ref"; +foreach my $acl_entry (@allow_rules) { + my ($acl_ops, $acl_n) = @$acl_entry; + next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen. + next unless $acl_n; + next unless $op =~ /^[$acl_ops]$/; + + grant "Allowed by: $acl_ops for $acl_n" + if ( + ($acl_n eq $ref) + || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n) + || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:) + ); +} +close A; +deny "You are not permitted to $op $ref"; -- cgit v1.2.3 From 413689d36f9b98c57bff9510ac4cdfb40e4ef9fc Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 19 Apr 2007 13:16:58 +0200 Subject: git.el: Add a commit description to the reflog. Add a description of the commit to the reflog using the first line of the log message, the same way the git-commit script does it. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2f9995ea39..f60017948f 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -345,9 +345,15 @@ and returns the process output as a string." (let ((str (git-call-process-env-string nil "symbolic-ref" ref))) (and str (car (split-string str "\n"))))) -(defun git-update-ref (ref val &optional oldval) +(defun git-update-ref (ref newval &optional oldval reason) "Update a reference by calling git-update-ref." - (apply #'git-call-process-env nil nil "update-ref" ref val (if oldval (list oldval)))) + (let ((args (and oldval (list oldval)))) + (push newval args) + (push ref args) + (when reason + (push reason args) + (push "-m" args)) + (eq 0 (apply #'git-call-process-env nil nil "update-ref" args)))) (defun git-read-tree (tree &optional index-file) "Read a tree into the index file." @@ -364,8 +370,10 @@ and returns the process output as a string." "Call git-commit-tree with buffer as input and return the resulting commit SHA1." (let ((author-name (git-get-committer-name)) (author-email (git-get-committer-email)) + (subject "commit (initial): ") author-date log-start log-end args coding-system-for-write) (when head + (setq subject "commit: ") (push "-p" args) (push head args)) (with-current-buffer buffer @@ -384,22 +392,29 @@ and returns the process output as a string." (goto-char (point-min)) (while (re-search-forward "^Parent: +\\([0-9a-f]+\\)" nil t) (unless (string-equal head (match-string 1)) + (setq subject "commit (merge): ") (push "-p" args) (push (match-string 1) args)))) (setq log-start (point-min))) (setq log-end (point-max)) + (goto-char log-start) + (when (re-search-forward ".*$" nil t) + (setq subject (concat subject (match-string 0)))) (setq coding-system-for-write buffer-file-coding-system)) - (git-get-string-sha1 - (with-output-to-string - (with-current-buffer standard-output - (let ((env `(("GIT_AUTHOR_NAME" . ,author-name) - ("GIT_AUTHOR_EMAIL" . ,author-email) - ("GIT_COMMITTER_NAME" . ,(git-get-committer-name)) - ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email))))) - (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env)) - (apply #'git-run-command-region - buffer log-start log-end env - "commit-tree" tree (nreverse args)))))))) + (let ((commit + (git-get-string-sha1 + (with-output-to-string + (with-current-buffer standard-output + (let ((env `(("GIT_AUTHOR_NAME" . ,author-name) + ("GIT_AUTHOR_EMAIL" . ,author-email) + ("GIT_COMMITTER_NAME" . ,(git-get-committer-name)) + ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email))))) + (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env)) + (apply #'git-run-command-region + buffer log-start log-end env + "commit-tree" tree (nreverse args)))))))) + (and (git-update-ref "HEAD" commit head subject) + commit)))) (defun git-empty-db-p () "Check if the git db is empty (no commit done yet)." @@ -662,7 +677,6 @@ and returns the process output as a string." (if (or (not (string-equal tree head-tree)) (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? ")) (let ((commit (git-commit-tree buffer tree head))) - (git-update-ref "HEAD" commit head) (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) -- cgit v1.2.3 From 2c9750cc8b902a55669183e05533207dd7ec71fd Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 19 Apr 2007 22:26:03 +0530 Subject: gitview: annotation support List files modifed as a part of the commit in the diff window Support annotation of the file listed in the diff window Support history browsing in the annotation window. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 260 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 256 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 521b2fcd32..2d80e2bad2 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -10,7 +10,8 @@ GUI browser for git repository This program is based on bzrk by Scott James Remnant """ __copyright__ = "Copyright (C) 2006 Hewlett-Packard Development Company, L.P." -__author__ = "Aneesh Kumar K.V " +__copyright__ = "Copyright (C) 2007 Aneesh Kumar K.V 0): + # set at result_line + count-1 the sha1 as commit_sha1 + self.count = self.count - 1 + iter = self.model.iter_nth_child(None, self.result_line + self.count-1) + self.model.set(iter, 0, self.commit_sha1, 1, filename, 3, self.source_line) + + + def annotate(self, filename, commit_sha1, line_num): + # verify the commit_sha1 specified has this filename + + fp = os.popen("git ls-tree "+ commit_sha1 + " -- " + filename) + line = string.strip(fp.readline()) + if line == '': + # pop up the message the file is not there as a part of the commit + fp.close() + dialog = gtk.MessageDialog(parent=None, flags=0, + type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE, + message_format=None) + dialog.set_markup("The file %s is not present in the parent commit %s" % (filename, commit_sha1)) + dialog.run() + dialog.destroy() + return + + fp.close() + + vpan = gtk.VPaned(); + self.window.add(vpan); + vpan.show() + + scrollwin = gtk.ScrolledWindow() + scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrollwin.set_shadow_type(gtk.SHADOW_IN) + vpan.pack1(scrollwin, True, True); + scrollwin.show() + + self.model = gtk.TreeStore(str, str, str, int) + self.treeview = gtk.TreeView(self.model) + self.treeview.set_rules_hint(True) + self.treeview.set_search_column(0) + self.treeview.connect("cursor-changed", self._treeview_cursor_cb) + self.treeview.connect("row-activated", self._treeview_row_activated) + scrollwin.add(self.treeview) + self.treeview.show() + + cell = gtk.CellRendererText() + cell.set_property("width-chars", 10) + cell.set_property("ellipsize", pango.ELLIPSIZE_END) + column = gtk.TreeViewColumn("Commit") + column.set_resizable(True) + column.pack_start(cell, expand=True) + column.add_attribute(cell, "text", 0) + self.treeview.append_column(column) + + cell = gtk.CellRendererText() + cell.set_property("width-chars", 20) + cell.set_property("ellipsize", pango.ELLIPSIZE_END) + column = gtk.TreeViewColumn("File Name") + column.set_resizable(True) + column.pack_start(cell, expand=True) + column.add_attribute(cell, "text", 1) + self.treeview.append_column(column) + + cell = gtk.CellRendererText() + cell.set_property("width-chars", 20) + cell.set_property("ellipsize", pango.ELLIPSIZE_END) + column = gtk.TreeViewColumn("Data") + column.set_resizable(True) + column.pack_start(cell, expand=True) + column.add_attribute(cell, "text", 2) + self.treeview.append_column(column) + + # The commit message window + scrollwin = gtk.ScrolledWindow() + scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrollwin.set_shadow_type(gtk.SHADOW_IN) + vpan.pack2(scrollwin, True, True); + scrollwin.show() + + commit_text = gtk.TextView() + self.commit_buffer = gtk.TextBuffer() + commit_text.set_buffer(self.commit_buffer) + scrollwin.add(commit_text) + commit_text.show() + + self.window.show() + + self.add_file_data(filename, commit_sha1, line_num) + + fp = os.popen("git blame --incremental -- " + filename + " " + commit_sha1) + flags = fcntl.fcntl(fp.fileno(), fcntl.F_GETFL) + fcntl.fcntl(fp.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK) + self.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready) + + class DiffWindow: """Diff window. This object represents and manages a single window containing the @@ -355,6 +537,7 @@ class DiffWindow: height = int(monitor.height * 0.66) self.window.set_default_size(width, height) + self.construct() def construct(self): @@ -371,10 +554,12 @@ class DiffWindow: vbox.pack_start(menu_bar, expand=False, fill=True) menu_bar.show() + hpan = gtk.HPaned() + scrollwin = gtk.ScrolledWindow() scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) scrollwin.set_shadow_type(gtk.SHADOW_IN) - vbox.pack_start(scrollwin, expand=True, fill=True) + hpan.pack1(scrollwin, True, True) scrollwin.show() if have_gtksourceview: @@ -388,11 +573,77 @@ class DiffWindow: self.buffer = gtk.TextBuffer() sourceview = gtk.TextView(self.buffer) + sourceview.set_editable(False) sourceview.modify_font(pango.FontDescription("Monospace")) scrollwin.add(sourceview) sourceview.show() + # The file hierarchy: a scrollable treeview + scrollwin = gtk.ScrolledWindow() + scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrollwin.set_shadow_type(gtk.SHADOW_IN) + scrollwin.set_size_request(20, -1) + hpan.pack2(scrollwin, True, True) + scrollwin.show() + + self.model = gtk.TreeStore(str, str, str) + self.treeview = gtk.TreeView(self.model) + self.treeview.set_search_column(1) + self.treeview.connect("cursor-changed", self._treeview_clicked) + scrollwin.add(self.treeview) + self.treeview.show() + + cell = gtk.CellRendererText() + cell.set_property("width-chars", 20) + column = gtk.TreeViewColumn("Select to annotate") + column.pack_start(cell, expand=True) + column.add_attribute(cell, "text", 0) + self.treeview.append_column(column) + + vbox.pack_start(hpan, expand=True, fill=True) + hpan.show() + + def _treeview_clicked(self, *args): + """Callback for when the treeview cursor changes.""" + (path, col) = self.treeview.get_cursor() + specific_file = self.model[path][1] + commit_sha1 = self.model[path][2] + if specific_file == None : + return + elif specific_file == "" : + specific_file = None + + window = AnnotateWindow(); + window.annotate(specific_file, commit_sha1, 1) + + + def commit_files(self, commit_sha1, parent_sha1): + self.model.clear() + add = self.model.append(None, [ "Added", None, None]) + dele = self.model.append(None, [ "Deleted", None, None]) + mod = self.model.append(None, [ "Modified", None, None]) + diff_tree = re.compile('^(:.{6}) (.{6}) (.{40}) (.{40}) (A|D|M)\s(.+)$') + fp = os.popen("git diff-tree -r --no-commit-id " + parent_sha1 + " " + commit_sha1) + while 1: + line = string.strip(fp.readline()) + if line == '': + break + m = diff_tree.match(line) + if not m: + continue + + attr = m.group(5) + filename = m.group(6) + if attr == "A": + self.model.append(add, [filename, filename, commit_sha1]) + elif attr == "D": + self.model.append(dele, [filename, filename, commit_sha1]) + elif attr == "M": + self.model.append(mod, [filename, filename, commit_sha1]) + fp.close() + + self.treeview.expand_all() def set_diff(self, commit_sha1, parent_sha1, encoding): """Set the differences showed by this window. @@ -406,6 +657,7 @@ class DiffWindow: fp = os.popen("git diff-tree -p " + parent_sha1 + " " + commit_sha1) self.buffer.set_text(unicode(fp.read(), encoding).encode('utf-8')) fp.close() + self.commit_files(commit_sha1, parent_sha1) self.window.show() def save_menu_response(self, widget, string): @@ -425,7 +677,7 @@ class DiffWindow: class GitView: """ This is the main class """ - version = "0.8" + version = "0.9" def __init__(self, with_diff=0): self.with_diff = with_diff @@ -590,7 +842,7 @@ class GitView: dialog = gtk.AboutDialog() dialog.set_name("Gitview") dialog.set_version(GitView.version) - dialog.set_authors(["Aneesh Kumar K.V "]) + dialog.set_authors(["Aneesh Kumar K.V "]) dialog.set_website("http://www.kernel.org/pub/software/scm/git/") dialog.set_copyright("Use and distribute under the terms of the GNU General Public License") dialog.set_wrap_license(True) -- cgit v1.2.3 From 2122591b3b5c6d93d3052a3151afcfa3146ede84 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 23 Apr 2007 17:18:16 -0700 Subject: Add clean.requireForce option, and add -f option to git-clean to override it Add a new configuration option clean.requireForce. If set, git-clean will refuse to run, unless forced with the new -f option, or not acting due to -n. Signed-off-by: Josh Triplett Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7c03403484..46356e8a27 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -790,6 +790,7 @@ _git_config () core.legacyHeaders core.packedGitWindowSize core.packedGitLimit + clean.requireForce color.branch color.branch.current color.branch.local -- cgit v1.2.3 From 46f6178a3f4e7a5bf8b47da0f3cc5c998d907ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Apr 2007 13:51:04 +0200 Subject: fix importing of subversion tars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add a / between the prefix and name fields of the tar archive if prefix is non-empty. Signed-off-by: Uwe Kleine-König Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 5585a8b2c5..184214689d 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -64,7 +64,12 @@ foreach my $tar_file (@ARGV) } print FI "\n"; - my $path = "$prefix$name"; + my $path; + if ($prefix) { + $path = "$prefix/$name"; + } else { + $path = "$name"; + } $files{$path} = [$next_mark++, $mode]; $commit_time = $mtime if $mtime > $commit_time; -- cgit v1.2.3 From 8e404f82abdfd912ad43c6fae7c218ff75a5d122 Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Thu, 26 Apr 2007 22:35:39 +0100 Subject: post-receive-email example hook: fastforward should have been fast_forward Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 65160153ee..edb30f6f0f 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -302,7 +302,7 @@ generate_update_branch_email() # List all of the revisions that were removed by this update, in a fast forward # update, this list will be empty, because rev-list O ^N is empty. For a non # fast forward, O ^N is the list of removed revisions - fastforward="" + fast_forward="" rev="" for rev in $(git rev-list $newrev..$oldrev) do -- cgit v1.2.3 From 024e5b31af6f06d39542ab1a44de358d7734388b Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Thu, 26 Apr 2007 22:36:24 +0100 Subject: post-receive-email example hook: detect rewind-only updates and output sensible message Sometimes a non-fast-forward update doesn't add new commits, it merely removes old commits. This patch adds support for detecting that and outputting a more correct message. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 77 ++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 23 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index edb30f6f0f..e175b426a4 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -327,36 +327,67 @@ generate_update_branch_email() if [ -z "$fastforward" ]; then echo " from $oldrev ($oldrev_type)" else + # 1. Existing revisions were removed. In this case newrev is a + # subset of oldrev - this is the reverse of a fast-forward, + # a rewind + # 2. New revisions were added on top of an old revision, this is + # a rewind and addition. + + # (1) certainly happened, (2) possibly. When (2) hasn't happened, + # we set a flag to indicate that no log printout is required. + echo "" - echo "This update added new revisions after undoing old revisions. That is to" - echo "say, the old revision is not a strict subset of the new revision. This" - echo "situation occurs when you --force push a change and generate a" - echo "repository containing something like this:" - echo "" - echo " * -- * -- B -- O -- O -- O ($oldrev)" - echo " \\" - echo " N -- N -- N ($newrev)" - echo "" - echo "When this happens we assume that you've already had alert emails for all" - echo "of the O revisions, and so we here report only the revisions in the N" - echo "branch from the common base, B." + + # Find the common ancestor of the old and new revisions and compare + # it with newrev + baserev=$(git merge-base $oldrev $newrev) + rewind_only="" + if [ "$baserev" = "$newrev" ]; then + echo "This update discarded existing revisions and left the branch pointing at" + echo "a previous point in the repository history." + echo "" + echo " * -- * -- N ($newrev)" + echo " \\" + echo " O -- O -- O ($oldrev)" + echo "" + echo "The removed revisions are not necessarilly gone - if another reference" + echo "still refers to them they will stay in the repository." + rewind_only=1 + else + echo "This update added new revisions after undoing existing revisions. That is" + echo "to say, the old revision is not a strict subset of the new revision. This" + echo "situation occurs when you --force push a change and generate a repository" + echo "containing something like this:" + echo "" + echo " * -- * -- B -- O -- O -- O ($oldrev)" + echo " \\" + echo " N -- N -- N ($newrev)" + echo "" + echo "When this happens we assume that you've already had alert emails for all" + echo "of the O revisions, and so we here report only the revisions in the N" + echo "branch from the common base, B." + fi fi echo "" - echo "Those revisions listed above that are new to this repository have" - echo "not appeared on any other notification email; so we list those" - echo "revisions in full, below." + if [ -z "$rewind_only" ]; then + echo "Those revisions listed above that are new to this repository have" + echo "not appeared on any other notification email; so we list those" + echo "revisions in full, below." - echo "" - echo $LOGBEGIN - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $oldrev..$newrev + echo "" + echo $LOGBEGIN + git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + git rev-list --pretty --stdin $oldrev..$newrev - # XXX: Need a way of detecting whether git rev-list actually outputted - # anything, so that we can issue a "no new revisions added by this - # update" message + # XXX: Need a way of detecting whether git rev-list actually outputted + # anything, so that we can issue a "no new revisions added by this + # update" message - echo $LOGEND + echo $LOGEND + else + echo "No new revisions were added by this update." + fi # The diffstat is shown from the old revision to the new revision. This # is to show the truth of what happened in this change. There's no point -- cgit v1.2.3 From c855195cd014f8b5a32a32b36cb77319654583a3 Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Thu, 26 Apr 2007 22:37:16 +0100 Subject: post-receive-email example hook: sed command for getting description was wrong The sed command that extracted the first line of the project description didn't include the -n switch and hence the project name was being printed twice. This was ruining the email header generation because it was assumed that the description was only one line and was included in the subject. This turned the subject into a two line item and prematurely finished the header. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index e175b426a4..d1bef9125b 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -587,7 +587,7 @@ if [ -z "$GIT_DIR" ]; then exit 1 fi -projectdesc=$(sed -e '1p' "$GIT_DIR/description") +projectdesc=$(sed -ne '1p' "$GIT_DIR/description") # Check if the description is unchanged from it's default, and shorten it to a # more manageable length if it is if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null -- cgit v1.2.3 From 87859f34434dda61cabb03447efd1dd2fe7ebac7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 29 Apr 2007 01:59:47 +0200 Subject: import-tars: be nice to wrong directory modes Some tars seem to have modes 0755 for directories, not 01000755. Do not generate an empty object for them, but ignore them. Noticed by riddochc on IRC. Signed-off-by: Johannes Schindelin Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 5585a8b2c5..e84647770a 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -52,6 +52,7 @@ foreach my $tar_file (@ARGV) Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; last unless $name; + next if $name =~ '/$'; $mode = oct $mode; $size = oct $size; $mtime = oct $mtime; -- cgit v1.2.3 From d0c32b63394992f8dd083a4f2f380ab190dbb2ca Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 29 Apr 2007 00:31:14 -0700 Subject: Fix import-tars fix. This heeds advice from our resident Perl expert to make sure the script is not confused with a string that ends with /\n Signed-off-by: Junio C Hamano --- contrib/fast-import/import-tars.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index e84647770a..82a90429c8 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -52,7 +52,7 @@ foreach my $tar_file (@ARGV) Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; last unless $name; - next if $name =~ '/$'; + next if $name =~ m{/\z}; $mode = oct $mode; $size = oct $size; $mtime = oct $mtime; -- cgit v1.2.3 From ff5dba20e324fb394b7077d0b1d1c5eb4357959f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 1 May 2007 18:28:38 +0200 Subject: Doc cleanups. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 4f6a680f95..d36a1cf18c 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -4,8 +4,8 @@ Usage ===== git-p4 supports two main modes: Importing from Perforce to a Git repository is -done using "git-p4 sync". Submitting changes from Git back to Perforce is -done using "git-p4 submit". +done using "git-p4 sync" or "git-p4 rebase". Submitting changes from Git back +to Perforce is done using "git-p4 submit". Importing ========= @@ -49,10 +49,6 @@ a big import. This may take a while. Support for Perforce integrations is still work in progress. Don't bother trying it unless you want to hack on it :) -For convenience there's also the git-p4 clone command that works similar to -git-clone and combines the creation of the git repository with the the initial -import and the branch setup - Incremental Imports =================== -- cgit v1.2.3 From 1c094184da5c7bf572c907633279890bd15d1952 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 1 May 2007 23:15:48 +0200 Subject: Micro cleanup Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index fb13469ce2..9adc66a05d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -74,7 +74,7 @@ def extractLogMessageFromGitCommit(commit): for log in os.popen("git cat-file commit %s" % commit).readlines(): if not foundTitle: if len(log) == 1: - foundTitle = 1 + foundTitle = True continue logMessage += log -- cgit v1.2.3 From 8f8725314dbed75dc1e603646ea6a2c630025f6e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 1 May 2007 23:23:00 +0200 Subject: cleanup, renamed self.globalPrefix to self.depotPath Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 80 +++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 40 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9adc66a05d..6b74feeb64 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -465,9 +465,9 @@ class P4Sync(Command): fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] - if not path.startswith(self.globalPrefix): + if not path.startswith(self.depotPath): # if not self.silent: - # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.globalPrefix, change) + # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.depotPath, change) fnum = fnum + 1 continue @@ -491,7 +491,7 @@ class P4Sync(Command): branches = Set() for file in files: - relativePath = file["path"][len(self.globalPrefix):] + relativePath = file["path"][len(self.depotPath):] # strip off the filename relativePath = relativePath[0:relativePath.rfind("/")] @@ -577,7 +577,7 @@ class P4Sync(Command): sys.exit(1); sourceLog = sourceLog[0] - relPath = source[len(self.globalPrefix):] + relPath = source[len(self.depotPath):] # strip off the filename relPath = relPath[0:relPath.rfind("/")] @@ -737,7 +737,7 @@ class P4Sync(Command): sys.exit(1); sourceLog = sourceLog[0] - relPath = source[len(self.globalPrefix):] + relPath = source[len(self.depotPath):] # strip off the filename relPath = relPath[0:relPath.rfind("/")] @@ -749,13 +749,13 @@ class P4Sync(Command): def changeIsBranchMerge(self, sourceBranch, destinationBranch, change): sourceFiles = {} - for file in p4CmdList("files %s...@%s" % (self.globalPrefix + sourceBranch + "/", change)): + for file in p4CmdList("files %s...@%s" % (self.depotPath + sourceBranch + "/", change)): if file["action"] == "delete": continue sourceFiles[file["depotFile"]] = file destinationFiles = {} - for file in p4CmdList("files %s...@%s" % (self.globalPrefix + destinationBranch + "/", change)): + for file in p4CmdList("files %s...@%s" % (self.depotPath + destinationBranch + "/", change)): destinationFiles[file["depotFile"]] = file for fileName in sourceFiles.keys(): @@ -822,9 +822,9 @@ class P4Sync(Command): def getLabels(self): self.labels = {} - l = p4CmdList("labels %s..." % self.globalPrefix) + l = p4CmdList("labels %s..." % self.depotPath) if len(l) > 0 and not self.silent: - print "Finding files belonging to labels in %s" % self.globalPrefix + print "Finding files belonging to labels in %s" % self.depotPath for output in l: label = output["label"] @@ -839,7 +839,7 @@ class P4Sync(Command): self.labels[newestChange] = [output, revisions] def run(self, args): - self.globalPrefix = "" + self.depotPath = "" self.changeRange = "" self.initialParent = "" @@ -855,7 +855,7 @@ class P4Sync(Command): [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) if len(self.previousDepotPath) > 0 and len(p4Change) > 0: p4Change = int(p4Change) + 1 - self.globalPrefix = self.previousDepotPath + self.depotPath = self.previousDepotPath self.changeRange = "@%s,#head" % p4Change self.initialParent = self.branch self.tagLastChange = False @@ -864,49 +864,49 @@ class P4Sync(Command): self.branch = "refs/heads/" + self.branch - if len(self.globalPrefix) == 0: - self.globalPrefix = self.previousDepotPath = os.popen("git repo-config --get p4.depotpath").read() + if len(self.depotPath) == 0: + self.depotPath = self.previousDepotPath = os.popen("git repo-config --get p4.depotpath").read() - if len(self.globalPrefix) != 0: - self.globalPrefix = self.globalPrefix[:-1] + if len(self.depotPath) != 0: + self.depotPath = self.depotPath[:-1] - if len(args) == 0 and len(self.globalPrefix) != 0: + if len(args) == 0 and len(self.depotPath) != 0: if not self.silent: - print "Depot path: %s" % self.globalPrefix + print "Depot path: %s" % self.depotPath elif len(args) != 1: return False else: - if len(self.globalPrefix) != 0 and self.globalPrefix != args[0]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.globalPrefix, args[0]) + if len(self.depotPath) != 0 and self.depotPath != args[0]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.depotPath, args[0]) sys.exit(1) - self.globalPrefix = args[0] + self.depotPath = args[0] self.revision = "" self.users = {} self.lastChange = 0 self.initialTag = "" - if self.globalPrefix.find("@") != -1: - atIdx = self.globalPrefix.index("@") - self.changeRange = self.globalPrefix[atIdx:] + if self.depotPath.find("@") != -1: + atIdx = self.depotPath.index("@") + self.changeRange = self.depotPath[atIdx:] if self.changeRange == "@all": self.changeRange = "" elif self.changeRange.find(",") == -1: self.revision = self.changeRange self.changeRange = "" - self.globalPrefix = self.globalPrefix[0:atIdx] - elif self.globalPrefix.find("#") != -1: - hashIdx = self.globalPrefix.index("#") - self.revision = self.globalPrefix[hashIdx:] - self.globalPrefix = self.globalPrefix[0:hashIdx] + self.depotPath = self.depotPath[0:atIdx] + elif self.depotPath.find("#") != -1: + hashIdx = self.depotPath.index("#") + self.revision = self.depotPath[hashIdx:] + self.depotPath = self.depotPath[0:hashIdx] elif len(self.previousDepotPath) == 0: self.revision = "#head" - if self.globalPrefix.endswith("..."): - self.globalPrefix = self.globalPrefix[:-3] + if self.depotPath.endswith("..."): + self.depotPath = self.depotPath[:-3] - if not self.globalPrefix.endswith("/"): - self.globalPrefix += "/" + if not self.depotPath.endswith("/"): + self.depotPath += "/" self.getUserMap() self.labels = {} @@ -939,15 +939,15 @@ class P4Sync(Command): self.gitError = importProcess.childerr if len(self.revision) > 0: - print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) + print "Doing initial import of %s from revision %s" % (self.depotPath, self.revision) details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = "Initial import of %s from the state at revision %s" % (self.globalPrefix, self.revision) + details["desc"] = "Initial import of %s from the state at revision %s" % (self.depotPath, self.revision) details["change"] = self.revision newestRevision = 0 fileCnt = 0 - for info in p4CmdList("files %s...%s" % (self.globalPrefix, self.revision)): + for info in p4CmdList("files %s...%s" % (self.depotPath, self.revision)): change = int(info["change"]) if change > newestRevision: newestRevision = change @@ -965,7 +965,7 @@ class P4Sync(Command): details["change"] = newestRevision try: - self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) + self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPath) except IOError: print "IO error with git fast-import. Is your git version recent enough?" print self.gitError.read() @@ -984,7 +984,7 @@ class P4Sync(Command): changes.sort() else: - output = os.popen("p4 changes %s...%s" % (self.globalPrefix, self.changeRange)).readlines() + output = os.popen("p4 changes %s...%s" % (self.depotPath, self.changeRange)).readlines() for line in output: changeNum = line.split(" ")[1] @@ -1011,7 +1011,7 @@ class P4Sync(Command): if self.detectBranches: for branch in self.branchesForCommit(files): self.knownBranches.add(branch) - branchPrefix = self.globalPrefix + branch + "/" + branchPrefix = self.depotPath + branch + "/" filesForCommit = self.extractFilesInCommitToBranch(files, branchPrefix) @@ -1040,7 +1040,7 @@ class P4Sync(Command): merged = "refs/heads/" + merged self.commit(description, files, branch, branchPrefix, parent, merged) else: - self.commit(description, files, self.branch, self.globalPrefix, self.initialParent) + self.commit(description, files, self.branch, self.depotPath, self.initialParent) self.initialParent = "" except IOError: print self.gitError.read() @@ -1059,7 +1059,7 @@ class P4Sync(Command): self.gitError.close() importProcess.wait() - os.popen("git repo-config p4.depotpath %s" % self.globalPrefix).read() + os.popen("git repo-config p4.depotpath %s" % self.depotPath).read() if len(self.initialTag) > 0: os.popen("git tag -d %s" % self.initialTag).read() -- cgit v1.2.3 From 28359251395c1647bf40adcdabbe4d85d7b085af Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 1 May 2007 23:26:19 +0200 Subject: Cleanup, removed the old tagging code Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6b74feeb64..9927fa142e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -455,7 +455,6 @@ class P4Sync(Command): self.detectBranches = False self.detectLabels = False self.changesFile = "" - self.tagLastChange = True def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -858,15 +857,11 @@ class P4Sync(Command): self.depotPath = self.previousDepotPath self.changeRange = "@%s,#head" % p4Change self.initialParent = self.branch - self.tagLastChange = False if not self.silent: print "Performing incremental import into %s git branch" % self.branch self.branch = "refs/heads/" + self.branch - if len(self.depotPath) == 0: - self.depotPath = self.previousDepotPath = os.popen("git repo-config --get p4.depotpath").read() - if len(self.depotPath) != 0: self.depotPath = self.depotPath[:-1] @@ -884,7 +879,6 @@ class P4Sync(Command): self.revision = "" self.users = {} self.lastChange = 0 - self.initialTag = "" if self.depotPath.find("@") != -1: atIdx = self.depotPath.index("@") @@ -927,7 +921,6 @@ class P4Sync(Command): self.rev = int(output[tagIdx + 9 : endPos]) + 1 self.changeRange = "@%s,#head" % self.rev self.initialParent = os.popen("git rev-parse %s" % self.branch).read()[:-1] - self.initialTag = "p4/%s" % (int(self.rev) - 1) except: pass @@ -1049,20 +1042,12 @@ class P4Sync(Command): if not self.silent: print "" - if self.tagLastChange: - self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) - self.gitStream.write("from %s\n\n" % self.branch); - self.gitStream.close() self.gitOutput.close() self.gitError.close() importProcess.wait() - os.popen("git repo-config p4.depotpath %s" % self.depotPath).read() - if len(self.initialTag) > 0: - os.popen("git tag -d %s" % self.initialTag).read() - return True class P4Rebase(Command): @@ -1086,7 +1071,6 @@ class P4Clone(P4Sync): self.description = "Creates a new git repository and imports from Perforce into it" self.usage = "usage: %prog [options] //depot/path[@revRange] [directory]" self.needsGit = False - self.tagLastChange = False def run(self, args): if len(args) < 1: -- cgit v1.2.3 From 775477aa1da94cb9fb9b9afdc217231a0cd42ac1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 1 May 2007 23:42:44 +0200 Subject: Teach import-tars about GNU tar's @LongLink extension. This extension allows GNU tar to process file names in excess of the 100 characters defined by the original tar standard. It does this by faking a file, named '././@LongLink' containing the true file name, and then adding the file with a truncated name. The idea is that tar without this extension will write out a file with the long file name, and write the contents into a file with truncated name. Unfortunately, GNU tar does a lousy job at times. When truncating results in a _directory_ name, it will happily use _that_ as a truncated name for the file. An example where this actually happens is gcc-4.1.2, where the full path of the file WeThrowThisExceptionHelper.java truncates _exactly_ before the basename. So, we have to support that ad-hoc extension. This bug was noticed by Chris Riddoch on IRC. Signed-off-by: Johannes Schindelin Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 82a90429c8..e46492048c 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -52,6 +52,25 @@ foreach my $tar_file (@ARGV) Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; last unless $name; + if ($name eq '././@LongLink') { + # GNU tar extension + if (read(I, $_, 512) != 512) { + die ('Short archive'); + } + $name = unpack 'Z257', $_; + next unless $name; + + my $dummy; + if (read(I, $_, 512) != 512) { + die ('Short archive'); + } + ($dummy, $mode, $uid, $gid, $size, $mtime, + $chksum, $typeflag, $linkname, $magic, + $version, $uname, $gname, $devmajor, $devminor, + $prefix) = unpack 'Z100 Z8 Z8 Z8 Z12 Z12 + Z8 Z1 Z100 Z6 + Z2 Z32 Z32 Z8 Z8 Z*', $_; + } next if $name =~ m{/\z}; $mode = oct $mode; $size = oct $size; -- cgit v1.2.3 From a844b7406f17ac6e069e617509a2997d7e128500 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 May 2007 20:14:17 +0200 Subject: Document some implementation details, for the curious... :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index d36a1cf18c..ff0da9d0c8 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -121,3 +121,20 @@ Example git-p4 rebase +Implementation Details... +========================= + +* Changesets from Perforce are imported using git fast-import. +* The import does not require anything from the Perforce client view as it just uses + "p4 print //depot/path/file#revision" to get the actual file contents. +* Every imported changeset has a special [git-p4...] line at the + end of the log message that gives information about the corresponding + Perforce change number and is also used by git-p4 itself to find out + where to continue importing when doing incremental imports. + Basically when syncing it extracts the perforce change number of the + latest commit in the "p4" branch and uses "p4 changes //depot/path/...@changenum,#head" + to find out which changes need to be imported. +* git-p4 submit uses "git rev-list" to pick the commits between the "p4" branch + and the current branch. + The commits themselves are applied using git diff-tree ... | patch -p1 + -- cgit v1.2.3 From d966e6aa66d397908b4fbf69cef2e2da88737321 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 7 May 2007 21:13:40 -0400 Subject: Properly handle '0' filenames in import-tars Randal L. Schwartz pointed out multiple times that we should be testing the length of the name string here, not if it is "true". The problem is the string '0' is actually false in Perl when we try to evaluate it in this context, as '0' is 0 numerically and the number 0 is treated as a false value. This would cause us to break out of the import loop early if anyone had a file or directory named "0". Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index e46492048c..f0b9a43abd 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -51,7 +51,7 @@ foreach my $tar_file (@ARGV) $prefix) = unpack 'Z100 Z8 Z8 Z8 Z12 Z12 Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; - last unless $name; + last unless length($name); if ($name eq '././@LongLink') { # GNU tar extension if (read(I, $_, 512) != 512) { -- cgit v1.2.3 From 084835805565726c825f0853626a33a0263066bf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 14:31:06 +0200 Subject: Use the subprocess module instead of popen2 to make it work on Windows. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9927fa142e..a2f582f8c9 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -8,7 +8,7 @@ # License: MIT # -import optparse, sys, os, marshal, popen2, shelve +import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time from sets import Set; @@ -926,10 +926,10 @@ class P4Sync(Command): self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) - importProcess = popen2.Popen3("git fast-import", capturestderr = True) - self.gitOutput = importProcess.fromchild - self.gitStream = importProcess.tochild - self.gitError = importProcess.childerr + importProcess = subprocess.Popen(["git", "fast-import"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); + self.gitOutput = importProcess.stdout + self.gitStream = importProcess.stdin + self.gitError = importProcess.stderr if len(self.revision) > 0: print "Doing initial import of %s from revision %s" % (self.depotPath, self.revision) -- cgit v1.2.3 From ac1fde55a71e166a86468582961908ab0d01413b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 14:42:56 +0200 Subject: Added a little .bat wrapper from Marius Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.bat | 1 + 1 file changed, 1 insertion(+) create mode 100644 contrib/fast-import/git-p4.bat (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.bat b/contrib/fast-import/git-p4.bat new file mode 100644 index 0000000000..666eb73016 --- /dev/null +++ b/contrib/fast-import/git-p4.bat @@ -0,0 +1 @@ +python "%~d0%~p0git-p4" %* -- cgit v1.2.3 From caace111129545559bf798d83ac8cfd596d36f66 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 14:57:57 +0200 Subject: Make sure all popen calls use binary mode (for Windows) and also make gitBranchExists work on Windows. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a2f582f8c9..d83ce1ae95 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -14,6 +14,9 @@ from sets import Set; gitdir = os.environ.get("GIT_DIR", "") +def mypopen(command): + return os.popen(command, "rb"); + def p4CmdList(cmd): cmd = "p4 -G %s" % cmd pipe = os.popen(cmd, "rb") @@ -57,7 +60,7 @@ def die(msg): sys.exit(1) def currentGitBranch(): - return os.popen("git name-rev HEAD").read().split(" ")[1][:-1] + return mypopen("git name-rev HEAD").read().split(" ")[1][:-1] def isValidGitDir(path): if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): @@ -71,7 +74,7 @@ def system(cmd): def extractLogMessageFromGitCommit(commit): logMessage = "" foundTitle = False - for log in os.popen("git cat-file commit %s" % commit).readlines(): + for log in mypopen("git cat-file commit %s" % commit).readlines(): if not foundTitle: if len(log) == 1: foundTitle = True @@ -100,9 +103,8 @@ def extractDepotPathAndChangeFromGitLog(log): return values.get("depot-path"), values.get("change") def gitBranchExists(branch): - if os.system("git rev-parse %s 2>/dev/null >/dev/null" % branch) == 0: - return True - return False + proc = subprocess.Popen(["git", "rev-parse", branch], stderr=subprocess.PIPE, stdout=subprocess.PIPE); + return proc.wait() == 0; class Command: def __init__(self): @@ -146,7 +148,7 @@ class P4CleanTags(Command): caretIdx = len(output) - 1 rev = int(output[tagIdx + 9 : caretIdx]) - allTags = os.popen("git tag -l p4/").readlines() + allTags = mypopen("git tag -l p4/").readlines() for i in range(len(allTags)): allTags[i] = int(allTags[i][3:-1]) @@ -155,7 +157,7 @@ class P4CleanTags(Command): allTags.remove(rev) for rev in allTags: - print os.popen("git tag -d p4/%s" % rev).read() + print mypopen("git tag -d p4/%s" % rev).read() print "%s tags removed." % len(allTags) return True @@ -194,7 +196,7 @@ class P4Submit(Command): die("Cannot start sync. Previous sync config found at %s" % self.configFile) commits = [] - for line in os.popen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + for line in mypopen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): commits.append(line[:-1]) commits.reverse() @@ -224,8 +226,8 @@ class P4Submit(Command): return result def apply(self, id): - print "Applying %s" % (os.popen("git log --max-count=1 --pretty=oneline %s" % id).read()) - diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + print "Applying %s" % (mypopen("git log --max-count=1 --pretty=oneline %s" % id).read()) + diff = mypopen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() filesToAdd = set() filesToDelete = set() for line in diff: @@ -282,11 +284,11 @@ class P4Submit(Command): logMessage = logMessage.replace("\n", "\n\t") logMessage = logMessage[:-1] - template = os.popen("p4 change -o").read() + template = mypopen("p4 change -o").read() if self.interactive: submitTemplate = self.prepareLogMessage(template, logMessage) - diff = os.popen("p4 diff -du ...").read() + diff = mypopen("p4 diff -du ...").read() for newFile in filesToAdd: diff += "==== new file ====\n" @@ -323,7 +325,7 @@ class P4Submit(Command): print submitTemplate raw_input("Press return to continue...") else: - pipe = os.popen("p4 submit -i", "w") + pipe = mypopen("p4 submit -i", "w") pipe.write(submitTemplate) pipe.close() else: @@ -920,7 +922,7 @@ class P4Sync(Command): endPos = caretIdx self.rev = int(output[tagIdx + 9 : endPos]) + 1 self.changeRange = "@%s,#head" % self.rev - self.initialParent = os.popen("git rev-parse %s" % self.branch).read()[:-1] + self.initialParent = mypopen("git rev-parse %s" % self.branch).read()[:-1] except: pass @@ -977,7 +979,7 @@ class P4Sync(Command): changes.sort() else: - output = os.popen("p4 changes %s...%s" % (self.depotPath, self.changeRange)).readlines() + output = mypopen("p4 changes %s...%s" % (self.depotPath, self.changeRange)).readlines() for line in output: changeNum = line.split(" ")[1] @@ -1060,7 +1062,7 @@ class P4Rebase(Command): sync = P4Sync() sync.run([]) print "Rebasing the current branch" - oldHead = os.popen("git rev-parse HEAD").read()[:-1] + oldHead = mypopen("git rev-parse HEAD").read()[:-1] system("git rebase p4") system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True @@ -1176,7 +1178,7 @@ if cmd.needsGit: if len(gitdir) == 0: gitdir = ".git" if not isValidGitDir(gitdir): - cdup = os.popen("git rev-parse --show-cdup").read()[:-1] + cdup = mypopen("git rev-parse --show-cdup").read()[:-1] if isValidGitDir(cdup + "/" + gitdir): os.chdir(cdup) -- cgit v1.2.3 From 25df95cce470d74cdc5e8905a298bbc36c7ec6d0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 15:15:39 +0200 Subject: Make submitting work on Windows. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d83ce1ae95..d134a28189 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -9,7 +9,7 @@ # import optparse, sys, os, marshal, popen2, subprocess, shelve -import tempfile, getopt, sha, os.path, time +import tempfile, getopt, sha, os.path, time, platform from sets import Set; gitdir = os.environ.get("GIT_DIR", "") @@ -299,7 +299,10 @@ class P4Submit(Command): diff += "+" + line f.close() - separatorLine = "######## everything below this line is just the diff #######\n" + separatorLine = "######## everything below this line is just the diff #######" + if platform.system() == "Windows": + separatorLine += "\r" + separatorLine += "\n" response = "e" firstIteration = True @@ -312,9 +315,12 @@ class P4Submit(Command): tmpFile = os.fdopen(handle, "w+") tmpFile.write(submitTemplate + separatorLine + diff) tmpFile.close() - editor = os.environ.get("EDITOR", "vi") + defaultEditor = "vi" + if platform.system() == "Windows": + defaultEditor = "notepad" + editor = os.environ.get("EDITOR", defaultEditor); system(editor + " " + fileName) - tmpFile = open(fileName, "r") + tmpFile = open(fileName, "rb") message = tmpFile.read() tmpFile.close() os.remove(fileName) @@ -325,7 +331,7 @@ class P4Submit(Command): print submitTemplate raw_input("Press return to continue...") else: - pipe = mypopen("p4 submit -i", "w") + pipe = os.popen("p4 submit -i", "wb") pipe.write(submitTemplate) pipe.close() else: -- cgit v1.2.3 From 42890f6291ab8b718dec5a3346875c45724d4ebb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 16:07:02 +0200 Subject: Converted to unix newlines Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.bat b/contrib/fast-import/git-p4.bat index 666eb73016..6524a54c67 100644 --- a/contrib/fast-import/git-p4.bat +++ b/contrib/fast-import/git-p4.bat @@ -1 +1 @@ -python "%~d0%~p0git-p4" %* +python "%~d0%~p0git-p4" %* -- cgit v1.2.3 From 95962f318e0f00cd4e2ebb54531c3d738aa39ece Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Tue, 15 May 2007 15:51:25 +0200 Subject: Make the command call silent Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.bat b/contrib/fast-import/git-p4.bat index 6524a54c67..9f97e884f5 100644 --- a/contrib/fast-import/git-p4.bat +++ b/contrib/fast-import/git-p4.bat @@ -1 +1 @@ -python "%~d0%~p0git-p4" %* +@python "%~d0%~p0git-p4" %* -- cgit v1.2.3 From cd6cc0d31845576082fabc7f246a99988aca6d26 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 16:15:26 +0200 Subject: Fix git-p4 clone //depot/project (head import) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d134a28189..2633f29942 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -849,6 +849,7 @@ class P4Sync(Command): self.depotPath = "" self.changeRange = "" self.initialParent = "" + self.previousDepotPath = "" if len(self.branch) == 0: self.branch = "p4" -- cgit v1.2.3 From 81f2373f89895a47ed0251ac0856798514cfb618 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 23:06:43 +0200 Subject: Make git-p4 work with bare repositories. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2633f29942..84cdca1aa2 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1185,9 +1185,7 @@ if cmd.needsGit: if len(gitdir) == 0: gitdir = ".git" if not isValidGitDir(gitdir): - cdup = mypopen("git rev-parse --show-cdup").read()[:-1] - if isValidGitDir(cdup + "/" + gitdir): - os.chdir(cdup) + gitdir = mypopen("git rev-parse --git-dir").read()[:-1] if not isValidGitDir(gitdir): if isValidGitDir(gitdir + "/.git"): -- cgit v1.2.3 From d336c15835c924ba8d153edbfceca88d5c748582 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 16 May 2007 09:41:26 +0200 Subject: Added the possibility of skipping patches during git-p4 submit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 84cdca1aa2..eba7a67c68 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -230,11 +230,13 @@ class P4Submit(Command): diff = mypopen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() filesToAdd = set() filesToDelete = set() + editedFiles = set() for line in diff: modifier = line[0] path = line[1:].strip() if modifier == "M": - system("p4 edit %s" % path) + system("p4 edit \"%s\"" % path) + editedFiles.add(path) elif modifier == "A": filesToAdd.add(path) if path in filesToDelete: @@ -308,7 +310,7 @@ class P4Submit(Command): firstIteration = True while response == "e": if not firstIteration: - response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o ") + response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o/[s]kip ") firstIteration = False if response == "e": [handle, fileName] = tempfile.mkstemp() @@ -334,6 +336,15 @@ class P4Submit(Command): pipe = os.popen("p4 submit -i", "wb") pipe.write(submitTemplate) pipe.close() + elif response == "s": + for f in editedFiles: + system("p4 revert \"%s\"" % f); + for f in filesToAdd: + system("p4 revert \"%s\"" % f); + system("rm %s" %f) + for f in filesToDelete: + system("p4 delete \"%s\"" % f); + return else: print "Not submitting!" self.interactive = False -- cgit v1.2.3 From c3c46244518178bac49caf21d6ba3a782292bb10 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 16 May 2007 09:43:13 +0200 Subject: Give a better hint if git-p4 submit fails Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index eba7a67c68..c48b257577 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -193,7 +193,7 @@ class P4Submit(Command): def start(self): if len(self.config) > 0 and not self.reset: - die("Cannot start sync. Previous sync config found at %s" % self.configFile) + die("Cannot start sync. Previous sync config found at %s\nIf you want to start submitting again from scratch maybe you want to call git-p4 submit --reset" % self.configFile) commits = [] for line in mypopen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): -- cgit v1.2.3 From dc1a93b6dce1d38e6e0ddd8980d8ccd64937fcb1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 16 May 2007 12:12:39 +0200 Subject: Fix calling git-p4 rebase from within a subdirectory (git rebase wants to be in toplevel) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c48b257577..ca6c623809 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1197,6 +1197,8 @@ if cmd.needsGit: gitdir = ".git" if not isValidGitDir(gitdir): gitdir = mypopen("git rev-parse --git-dir").read()[:-1] + if os.path.exists(gitdir): + os.chdir(mypopen("git rev-parse --show-cdup").read()[:-1]); if not isValidGitDir(gitdir): if isValidGitDir(gitdir + "/.git"): -- cgit v1.2.3 From ca0affe7bbc0ef507e2ec01c75e4f54d309dfad7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 16 May 2007 13:15:34 +0200 Subject: A little todo note before I forget it :), based on a suggestion from Lars. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ca6c623809..70366ff8c0 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -7,6 +7,8 @@ # 2007 Trolltech ASA # License: MIT # +# TODO: Add an option to sync/rebase to fetch and rebase from origin first. +# import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform -- cgit v1.2.3 From df8cfac815a1fae75afd20a86beb194d9d947971 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 16 May 2007 17:22:26 +0100 Subject: import-tars: Use the "Link indicator" to identify directories Earlier, we used the mode to determine if a name was associated with a directory. This fails, since some tar programs do not set the mode correctly. However, the link indicator _has_ to be set correctly. Noticed by Chris Riddoch. Signed-off-by: Johannes Schindelin Acked-by: Junio C Hamano Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index f0b9a43abd..5bfd205225 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -75,7 +75,7 @@ foreach my $tar_file (@ARGV) $mode = oct $mode; $size = oct $size; $mtime = oct $mtime; - next if $mode & 0040000; + next if $typeflag == 5; # directory print FI "blob\n", "mark :$next_mark\n", "data $size\n"; while ($size > 0 && read(I, $_, 512) == 512) { -- cgit v1.2.3 From 5c4153e4880a90c05612521e9ee575a308d207db Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 07:42:38 +0200 Subject: Fixing syncing (gitdir discovery / cd) for bare repositories Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 70366ff8c0..239304857d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1200,7 +1200,9 @@ if cmd.needsGit: if not isValidGitDir(gitdir): gitdir = mypopen("git rev-parse --git-dir").read()[:-1] if os.path.exists(gitdir): - os.chdir(mypopen("git rev-parse --show-cdup").read()[:-1]); + cdup = mypopen("git rev-parse --show-cdup").read()[:-1]; + if len(cdup) > 0: + os.chdir(cdup); if not isValidGitDir(gitdir): if isValidGitDir(gitdir + "/.git"): -- cgit v1.2.3 From f9162f6a4cc9f4af3e527412b364a8eda4502920 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 09:02:45 +0200 Subject: Always pass a sha1 for the initial parent so that git-fast-import doesn't think it's creating a new branch from itself. It's a sensible error in general but in the case of incremental imports we have to apply force :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 239304857d..1f549b5c62 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -865,7 +865,7 @@ class P4Sync(Command): self.previousDepotPath = "" if len(self.branch) == 0: - self.branch = "p4" + self.branch = "refs/remotes/p4" if len(args) == 0: if not gitBranchExists(self.branch) and gitBranchExists("origin"): @@ -878,11 +878,12 @@ class P4Sync(Command): p4Change = int(p4Change) + 1 self.depotPath = self.previousDepotPath self.changeRange = "@%s,#head" % p4Change - self.initialParent = self.branch + self.initialParent = mypopen("git rev-parse %s" % self.branch).read()[:-1] if not self.silent: print "Performing incremental import into %s git branch" % self.branch - self.branch = "refs/heads/" + self.branch + if not self.branch.startswith("refs/"): + self.branch = "refs/heads/" + self.branch if len(self.depotPath) != 0: self.depotPath = self.depotPath[:-1] -- cgit v1.2.3 From 463e8af65577b1df4495cb08640840234ac80c86 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 09:13:54 +0200 Subject: Clean up code duplication for revision parsing and fix previous commit to not import into remotes/p4 (yet!). Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1f549b5c62..e18f3cb8ab 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -69,6 +69,9 @@ def isValidGitDir(path): return True; return False +def parseRevision(ref): + return mypopen("git rev-parse %s" % ref).read()[:-1] + def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) @@ -865,7 +868,7 @@ class P4Sync(Command): self.previousDepotPath = "" if len(self.branch) == 0: - self.branch = "refs/remotes/p4" + self.branch = "p4" if len(args) == 0: if not gitBranchExists(self.branch) and gitBranchExists("origin"): @@ -878,7 +881,7 @@ class P4Sync(Command): p4Change = int(p4Change) + 1 self.depotPath = self.previousDepotPath self.changeRange = "@%s,#head" % p4Change - self.initialParent = mypopen("git rev-parse %s" % self.branch).read()[:-1] + self.initialParent = parseRevision(self.branch) if not self.silent: print "Performing incremental import into %s git branch" % self.branch @@ -943,7 +946,7 @@ class P4Sync(Command): endPos = caretIdx self.rev = int(output[tagIdx + 9 : endPos]) + 1 self.changeRange = "@%s,#head" % self.rev - self.initialParent = mypopen("git rev-parse %s" % self.branch).read()[:-1] + self.initialParent = parseRevision(self.branch) except: pass -- cgit v1.2.3 From 8a2820def44f163ec8909863aefa5e8f98a236ea Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 19:44:50 +0200 Subject: Removed cleantags command. It doesn't have any meaning anymore. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 39 --------------------------------------- 1 file changed, 39 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e18f3cb8ab..910c69b534 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -129,44 +129,6 @@ class P4Debug(Command): print output return True -class P4CleanTags(Command): - def __init__(self): - Command.__init__(self) - self.options = [ -# optparse.make_option("--branch", dest="branch", default="refs/heads/master") - ] - self.description = "A tool to remove stale unused tags from incremental perforce imports." - def run(self, args): - branch = currentGitBranch() - print "Cleaning out stale p4 import tags..." - sout, sin, serr = popen2.popen3("git name-rev --tags `git rev-parse %s`" % branch) - output = sout.read() - try: - tagIdx = output.index(" tags/p4/") - except: - print "Cannot find any p4/* tag. Nothing to do." - sys.exit(0) - - try: - caretIdx = output.index("^") - except: - caretIdx = len(output) - 1 - rev = int(output[tagIdx + 9 : caretIdx]) - - allTags = mypopen("git tag -l p4/").readlines() - for i in range(len(allTags)): - allTags[i] = int(allTags[i][3:-1]) - - allTags.sort() - - allTags.remove(rev) - - for rev in allTags: - print mypopen("git tag -d p4/%s" % rev).read() - - print "%s tags removed." % len(allTags) - return True - class P4Submit(Command): def __init__(self): Command.__init__(self) @@ -1161,7 +1123,6 @@ def printUsage(commands): commands = { "debug" : P4Debug(), - "clean-tags" : P4CleanTags(), "submit" : P4Submit(), "sync" : P4Sync(), "rebase" : P4Rebase(), -- cgit v1.2.3 From 1c9d393d30797decad703b642fa87b67a5236af4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 20:15:47 +0200 Subject: Removed ancient and unused code to find the last imported revision from previous imports to use for the current import by looking at the p4 tags. The current approach of using the log message works better. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 910c69b534..a19ba47481 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -895,23 +895,6 @@ class P4Sync(Command): if self.detectLabels: self.getLabels(); - if len(self.changeRange) == 0: - try: - sout, sin, serr = popen2.popen3("git name-rev --tags `git rev-parse %s`" % self.branch) - output = sout.read() - if output.endswith("\n"): - output = output[:-1] - tagIdx = output.index(" tags/p4/") - caretIdx = output.find("^") - endPos = len(output) - if caretIdx != -1: - endPos = caretIdx - self.rev = int(output[tagIdx + 9 : endPos]) + 1 - self.changeRange = "@%s,#head" % self.rev - self.initialParent = parseRevision(self.branch) - except: - pass - self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) importProcess = subprocess.Popen(["git", "fast-import"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); -- cgit v1.2.3 From 8ead4fda3fbaba93aae46931285e9613a058c08b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 20:26:58 +0200 Subject: Create the origin based import branch using git update-ref instead of git branch so that it's possible to have the import branch in refs/remotes. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a19ba47481..4cd486eb3a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -836,7 +836,10 @@ class P4Sync(Command): if not gitBranchExists(self.branch) and gitBranchExists("origin"): if not self.silent: print "Creating %s branch in git repository based on origin" % self.branch - system("git branch %s origin" % self.branch) + branch = self.branch + if not branch.startswith("refs"): + branch = "refs/heads/" + branch + system("git update-ref %s origin" % branch) [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) if len(self.previousDepotPath) > 0 and len(p4Change) > 0: -- cgit v1.2.3 From c6d44cb1a1a9bca58d91ef414b9dbaa393d2de3a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 20:57:05 +0200 Subject: Changed the default p4 import branch to be refs/remotes/p4/{HEAD,master} instead of refs/heads/p4. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++++++- contrib/fast-import/git-p4.txt | 6 +++--- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 4cd486eb3a..3cc6481378 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -828,9 +828,15 @@ class P4Sync(Command): self.changeRange = "" self.initialParent = "" self.previousDepotPath = "" + # importing into default remotes/p4/* layout? + defaultImport = False if len(self.branch) == 0: - self.branch = "p4" + if gitBranchExists("refs/heads/p4"): + self.branch = "p4" + else: + self.branch = "refs/remotes/p4/master" + defaultImport = True if len(args) == 0: if not gitBranchExists(self.branch) and gitBranchExists("origin"): @@ -840,6 +846,8 @@ class P4Sync(Command): if not branch.startswith("refs"): branch = "refs/heads/" + branch system("git update-ref %s origin" % branch) + if defaultImport: + system("git symbolic-ref refs/remotes/p4/HEAD %s" % branch) [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) if len(self.previousDepotPath) > 0 and len(p4Change) > 0: diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index ff0da9d0c8..32aeb0ac0b 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -20,9 +20,9 @@ or This will create an empty git repository in a subdirectory called "project" (or "myproject" with the second command), import the head revision from the -specified perforce path into a git "p4" branch, create a master branch off it -and check it out. If you want the entire history (not just the head revision) then -you can simply append a "@all" to the depot path: +specified perforce path into a git "p4" branch (remotes/p4 actually), create a +master branch off it and check it out. If you want the entire history (not just +the head revision) then you can simply append a "@all" to the depot path: git-p4 clone //depot/project/main@all myproject -- cgit v1.2.3 From 48df6fd850b2d9dc52a8a89a9e6deecd2cf1d351 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 21:18:53 +0200 Subject: Bite the bullet and automatically convert old style refs/heads/p4 repositories to the new style refs/remotes/p4 branching. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3cc6481378..cb9961a571 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -832,10 +832,12 @@ class P4Sync(Command): defaultImport = False if len(self.branch) == 0: + self.branch = "refs/remotes/p4/master" if gitBranchExists("refs/heads/p4"): - self.branch = "p4" + system("git update-ref %s refs/heads/p4" % self.branch) + system("git symbolic-ref refs/remotes/p4/HEAD refs/remotes/p4/master") + system("git branch -D p4"); else: - self.branch = "refs/remotes/p4/master" defaultImport = True if len(args) == 0: -- cgit v1.2.3 From ef48f9093ced75ca20295f42b993bd390873573d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 22:17:49 +0200 Subject: Added support for git-p4 sync/rebase --with-origin. See git-p4.txt for details :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 23 +++++++++++++++++++++-- contrib/fast-import/git-p4.txt | 20 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index cb9961a571..e653e8cd37 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -419,7 +419,8 @@ class P4Sync(Command): optparse.make_option("--known-branches", dest="knownBranches"), optparse.make_option("--data-cache", dest="dataCache", action="store_true"), optparse.make_option("--command-cache", dest="commandCache", action="store_true"), - optparse.make_option("--detect-labels", dest="detectLabels", action="store_true") + optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), + optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -441,6 +442,7 @@ class P4Sync(Command): self.detectBranches = False self.detectLabels = False self.changesFile = "" + self.syncWithOrigin = False def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -831,6 +833,21 @@ class P4Sync(Command): # importing into default remotes/p4/* layout? defaultImport = False + if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master"): + print "Syncing with origin first as requested by calling git fetch origin" + system("git fetch origin") + [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) + [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) + if len(originPreviousDepotPath) > 0 and len(originP4Change) > 0 and len(p4Change) > 0: + if originPreviousDepotPath == p4PreviousDepotPath: + originP4Change = int(originP4Change) + p4Change = int(p4Change) + if originP4Change > p4Change: + print "origin (%s) is newer than p4 (%s). Updating p4 branch from origin." % (originP4Change, p4Change) + system("git update-ref refs/remotes/p4/master origin"); + else: + print "Cannot sync with origin. It was imported from %s while remotes/p4 was imported from %s" % (originPreviousDepotPath, p4PreviousDepotPath) + if len(self.branch) == 0: self.branch = "refs/remotes/p4/master" if gitBranchExists("refs/heads/p4"): @@ -1037,11 +1054,13 @@ class P4Sync(Command): class P4Rebase(Command): def __init__(self): Command.__init__(self) - self.options = [ ] + self.options = [ optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true") ] self.description = "Fetches the latest revision from perforce and rebases the current work (branch) against it" + self.syncWithOrigin = False def run(self, args): sync = P4Sync() + sync.syncWithOrigin = self.syncWithOrigin sync.run([]) print "Rebasing the current branch" oldHead = mypopen("git rev-parse HEAD").read()[:-1] diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 32aeb0ac0b..ac8e6cff0b 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -63,6 +63,26 @@ It is recommended to run 'git repack -a -d -f' from time to time when using incremental imports to optimally combine the individual git packs that each incremental import creates through the use of git-fast-import. + +A useful setup may be that you have a periodically updated git repository +somewhere that contains a complete import of a Perforce project. That git +repository can be used to clone the working repository from and one would +import from Perforce directly after cloning using git-p4. If the connection to +the Perforce server is slow and the working repository hasn't been synced for a +while it may be desirable to fetch changes from the origin git repository using +the efficient git protocol. git-p4 supports this through + + git-p4 sync --with-origin + +or + + git-p4 rebase --with-origin + +In that case "git fetch origin" is called and if it turns out that the origin +branch is newer than the git "p4" import branch then the latter is updated from +the former and the direct import from Perforce is resumed, which will result in +fewer changes to be imported using the slower perforce connection. + Updating ======== -- cgit v1.2.3 From 71bd9bacec6a18c2db582e1f32b1902c82359376 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 22:22:26 +0200 Subject: Removed todo item that is implemented :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 -- 1 file changed, 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e653e8cd37..a21d902ea9 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -7,8 +7,6 @@ # 2007 Trolltech ASA # License: MIT # -# TODO: Add an option to sync/rebase to fetch and rebase from origin first. -# import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform -- cgit v1.2.3 From 05094f987c2f141ec9b873ad6d6303b0aca173a4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 May 2007 20:32:35 +0200 Subject: Fix branch setup after initial clone. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a21d902ea9..2f3615bd72 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -828,8 +828,6 @@ class P4Sync(Command): self.changeRange = "" self.initialParent = "" self.previousDepotPath = "" - # importing into default remotes/p4/* layout? - defaultImport = False if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master"): print "Syncing with origin first as requested by calling git fetch origin" @@ -850,10 +848,9 @@ class P4Sync(Command): self.branch = "refs/remotes/p4/master" if gitBranchExists("refs/heads/p4"): system("git update-ref %s refs/heads/p4" % self.branch) - system("git symbolic-ref refs/remotes/p4/HEAD refs/remotes/p4/master") system("git branch -D p4"); - else: - defaultImport = True + if not gitBranchExists("refs/remotes/p4/HEAD"): + system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) if len(args) == 0: if not gitBranchExists(self.branch) and gitBranchExists("origin"): @@ -863,8 +860,6 @@ class P4Sync(Command): if not branch.startswith("refs"): branch = "refs/heads/" + branch system("git update-ref %s origin" % branch) - if defaultImport: - system("git symbolic-ref refs/remotes/p4/HEAD %s" % branch) [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) if len(self.previousDepotPath) > 0 and len(p4Change) > 0: -- cgit v1.2.3 From 66c6a9b559a883460b1aeed7dadec713be17588c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 May 2007 20:39:38 +0200 Subject: Removed unused cache variables. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ---- 1 file changed, 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2f3615bd72..e164edef55 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -415,8 +415,6 @@ class P4Sync(Command): optparse.make_option("--changesfile", dest="changesFile"), optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--known-branches", dest="knownBranches"), - optparse.make_option("--data-cache", dest="dataCache", action="store_true"), - optparse.make_option("--command-cache", dest="commandCache", action="store_true"), optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true") ] @@ -430,8 +428,6 @@ class P4Sync(Command): self.usage += " //depot/path[@revRange]" - self.dataCache = False - self.commandCache = False self.silent = False self.knownBranches = Set() self.createdBranches = Set() -- cgit v1.2.3 From 4b97ffb1e462c2334b38047fbf2b7e8b24c36aed Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 May 2007 21:45:23 +0200 Subject: Started rewriting the branch detection, based on "p4 branches" and "p4 branch -o foo". Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 287 +++++++-------------------------------------- 1 file changed, 45 insertions(+), 242 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e164edef55..e993d3f693 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -414,9 +414,9 @@ class P4Sync(Command): optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"), optparse.make_option("--changesfile", dest="changesFile"), optparse.make_option("--silent", dest="silent", action="store_true"), - optparse.make_option("--known-branches", dest="knownBranches"), optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), - optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true") + optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true"), + optparse.make_option("--verbose", dest="verbose", action="store_true") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -429,7 +429,6 @@ class P4Sync(Command): self.usage += " //depot/path[@revRange]" self.silent = False - self.knownBranches = Set() self.createdBranches = Set() self.committedChanges = Set() self.branch = "" @@ -437,6 +436,7 @@ class P4Sync(Command): self.detectLabels = False self.changesFile = "" self.syncWithOrigin = False + self.verbose = False def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -461,120 +461,25 @@ class P4Sync(Command): fnum = fnum + 1 return files - def isSubPathOf(self, first, second): - if not first.startswith(second): - return False - if first == second: - return True - return first[len(second)] == "/" - def branchesForCommit(self, files): branches = Set() for file in files: - relativePath = file["path"][len(self.depotPath):] - # strip off the filename - relativePath = relativePath[0:relativePath.rfind("/")] - - # if len(branches) == 0: - # branches.add(relativePath) - # knownBranches.add(relativePath) - # continue - - ###### this needs more testing :) - knownBranch = False - for branch in branches: - if relativePath == branch: - knownBranch = True - break - # if relativePath.startswith(branch): - if self.isSubPathOf(relativePath, branch): - knownBranch = True - break - # if branch.startswith(relativePath): - if self.isSubPathOf(branch, relativePath): - branches.remove(branch) - break - - if knownBranch: - continue - - for branch in self.knownBranches: - #if relativePath.startswith(branch): - if self.isSubPathOf(relativePath, branch): - if len(branches) == 0: - relativePath = branch - else: - knownBranch = True - break + path = file["path"][len(self.depotPath):] - if knownBranch: - continue - - branches.add(relativePath) - self.knownBranches.add(relativePath) + for branch in self.knownBranches.keys(): + if path.startswith(branch): + branches.add(branch) return branches - def findBranchParent(self, branchPrefix, files): - for file in files: - path = file["path"] - if not path.startswith(branchPrefix): - continue - action = file["action"] - if action != "integrate" and action != "branch": - continue - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] - # if branchAction == "branch into" or branchAction == "ignored": - # continue # ignore for branching - - if not branchAction.endswith(" from"): - continue # ignore for branching - # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) - # sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(self.depotPath):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for branch in self.knownBranches: - if self.isSubPathOf(relPath, branch): - # print "determined parent branch branch %s due to change in file %s" % (branch, source) - return branch - # else: - # print "%s is not a subpath of branch %s" % (relPath, branch) - - return "" - - def commit(self, details, files, branch, branchPrefix, parent = "", merged = ""): + def commit(self, details, files, branch, branchPrefix, parent = ""): epoch = details["time"] author = details["user"] + if self.verbose: + print "commit into %s" % branch + self.gitStream.write("commit %s\n" % branch) # gitStream.write("mark :%s\n" % details["change"]) self.committedChanges.add(int(details["change"])) @@ -592,11 +497,10 @@ class P4Sync(Command): self.gitStream.write("EOT\n\n") if len(parent) > 0: + if self.verbose: + print "parent %s" % parent self.gitStream.write("from %s\n" % parent) - if len(merged) > 0: - self.gitStream.write("merge %s\n" % merged) - for file in files: path = file["path"] if not path.startswith(branchPrefix): @@ -680,118 +584,6 @@ class P4Sync(Command): return newFiles - def findBranchSourceHeuristic(self, files, branch, branchPrefix): - for file in files: - action = file["action"] - if action != "integrate" and action != "branch": - continue - path = file["path"] - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] - - if not branchAction.endswith(" from"): - continue # ignore for branching - # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) - # sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(self.depotPath):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for candidate in self.knownBranches: - if self.isSubPathOf(relPath, candidate) and candidate != branch: - return candidate - - return "" - - def changeIsBranchMerge(self, sourceBranch, destinationBranch, change): - sourceFiles = {} - for file in p4CmdList("files %s...@%s" % (self.depotPath + sourceBranch + "/", change)): - if file["action"] == "delete": - continue - sourceFiles[file["depotFile"]] = file - - destinationFiles = {} - for file in p4CmdList("files %s...@%s" % (self.depotPath + destinationBranch + "/", change)): - destinationFiles[file["depotFile"]] = file - - for fileName in sourceFiles.keys(): - integrations = [] - deleted = False - integrationCount = 0 - for integration in p4CmdList("integrated \"%s\"" % fileName): - toFile = integration["fromFile"] # yes, it's true, it's fromFile - if not toFile in destinationFiles: - continue - destFile = destinationFiles[toFile] - if destFile["action"] == "delete": - # print "file %s has been deleted in %s" % (fileName, toFile) - deleted = True - break - integrationCount += 1 - if integration["how"] == "branch from": - continue - - if int(integration["change"]) == change: - integrations.append(integration) - continue - if int(integration["change"]) > change: - continue - - destRev = int(destFile["rev"]) - - startRev = integration["startFromRev"][1:] - if startRev == "none": - startRev = 0 - else: - startRev = int(startRev) - - endRev = integration["endFromRev"][1:] - if endRev == "none": - endRev = 0 - else: - endRev = int(endRev) - - initialBranch = (destRev == 1 and integration["how"] != "branch into") - inRange = (destRev >= startRev and destRev <= endRev) - newer = (destRev > startRev and destRev > endRev) - - if initialBranch or inRange or newer: - integrations.append(integration) - - if deleted: - continue - - if len(integrations) == 0 and integrationCount > 1: - print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) - return False - - return True - def getUserMap(self): self.users = {} @@ -819,6 +611,27 @@ class P4Sync(Command): self.labels[newestChange] = [output, revisions] + def getBranchMapping(self): + # map from branch depot path to parent branch + self.knownBranches = {} + + for info in p4CmdList("branches"): + details = p4Cmd("branch -o %s" % info["branch"]) + viewIdx = 0 + while details.has_key("View%s" % viewIdx): + paths = details["View%s" % viewIdx].split(" ") + viewIdx = viewIdx + 1 + # require standard //depot/foo/... //depot/bar/... mapping + if len(paths) != 2 or not paths[0].endswith("/...") or not paths[1].endswith("/..."): + continue + source = paths[0] + destination = paths[1] + if source.startswith(self.depotPath) and destination.startswith(self.depotPath): + source = source[len(self.depotPath):-4] + destination = destination[len(self.depotPath):-4] + self.knownBranches[destination] = source + self.knownBranches[source] = source + def run(self, args): self.depotPath = "" self.changeRange = "" @@ -914,6 +727,9 @@ class P4Sync(Command): if self.detectLabels: self.getLabels(); + if self.detectBranches: + self.getBranchMapping(); + self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) importProcess = subprocess.Popen(["git", "fast-import"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); @@ -993,35 +809,22 @@ class P4Sync(Command): files = self.extractFilesFromCommit(description) if self.detectBranches: for branch in self.branchesForCommit(files): - self.knownBranches.add(branch) branchPrefix = self.depotPath + branch + "/" - filesForCommit = self.extractFilesInCommitToBranch(files, branchPrefix) - - merged = "" parent = "" - ########### remove cnt!!! - if branch not in self.createdBranches and cnt > 2: + + filesForCommit = self.extractFilesInCommitToBranch(files, branch) + + if branch not in self.createdBranches : self.createdBranches.add(branch) - parent = self.findBranchParent(branchPrefix, files) + parent = self.knownBranches[branch] if parent == branch: parent = "" - # elif len(parent) > 0: - # print "%s branched off of %s" % (branch, parent) - - if len(parent) == 0: - merged = self.findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) - if len(merged) > 0: - print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) - if not self.changeIsBranchMerge(merged, branch, int(description["change"])): - merged = "" - branch = "refs/heads/" + branch + branch = "refs/remotes/p4/" + branch if len(parent) > 0: - parent = "refs/heads/" + parent - if len(merged) > 0: - merged = "refs/heads/" + merged - self.commit(description, files, branch, branchPrefix, parent, merged) + parent = "refs/remotes/p4/" + parent + self.commit(description, files, branch, branchPrefix, parent) else: self.commit(description, files, self.branch, self.depotPath, self.initialParent) self.initialParent = "" -- cgit v1.2.3 From 8f9b2e082b54787676bdad793ca013ba4fb4e407 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 May 2007 22:13:26 +0200 Subject: Give branches a nice project prefix and don't bail out on clone if we failed to detect the master branch. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e993d3f693..7d42739637 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -729,6 +729,7 @@ class P4Sync(Command): if self.detectBranches: self.getBranchMapping(); + self.branchPrefix = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) @@ -815,12 +816,23 @@ class P4Sync(Command): filesForCommit = self.extractFilesInCommitToBranch(files, branch) - if branch not in self.createdBranches : + if branch not in self.createdBranches: self.createdBranches.add(branch) parent = self.knownBranches[branch] if parent == branch: parent = "" + # main branch? use master + if branch == "main": + branch = "master" + else: + branch = self.branchPrefix + branch + + if parent == "main": + parent = "master" + elif len(parent) > 0: + parent = self.branchPrefix + parent + branch = "refs/remotes/p4/" + branch if len(parent) > 0: parent = "refs/remotes/p4/" + parent @@ -906,8 +918,11 @@ class P4Clone(P4Sync): if not P4Sync.run(self, [depotPath]): return False if self.branch != "master": - system("git branch master p4") - system("git checkout -f") + if gitBranchExists("refs/remotes/p4/master"): + system("git branch master refs/remotes/p4/master") + system("git checkout -f") + else: + print "Could not detect main branch. No checkout/master branch created." return True class HelpFormatter(optparse.IndentedHelpFormatter): -- cgit v1.2.3 From 29bdbac1cd5fc4126b62c9a030403d56ae43c204 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 19 May 2007 10:23:12 +0200 Subject: More work on the incremental importing of multiple branches. Improved error detection by checking the exit code of git-fast-import. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 90 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 7d42739637..2c1dc9e2b3 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -612,8 +612,7 @@ class P4Sync(Command): self.labels[newestChange] = [output, revisions] def getBranchMapping(self): - # map from branch depot path to parent branch - self.knownBranches = {} + self.projectName = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] for info in p4CmdList("branches"): details = p4Cmd("branch -o %s" % info["branch"]) @@ -629,16 +628,34 @@ class P4Sync(Command): if source.startswith(self.depotPath) and destination.startswith(self.depotPath): source = source[len(self.depotPath):-4] destination = destination[len(self.depotPath):-4] - self.knownBranches[destination] = source - self.knownBranches[source] = source + if destination not in self.knownBranches: + self.knownBranches[destination] = source + if source not in self.knownBranches: + self.knownBranches[source] = source + + def listExistingP4GitBranches(self): + self.p4BranchesInGit = [] + + for line in mypopen("git rev-parse --symbolic --remotes").readlines(): + if line.startswith("p4/") and line != "p4/HEAD\n": + branch = line[3:-1] + self.p4BranchesInGit.append(branch) + self.initialParents["refs/remotes/p4/" + branch] = parseRevision(line[:-1]) def run(self, args): self.depotPath = "" self.changeRange = "" self.initialParent = "" self.previousDepotPath = "" + # map from branch depot path to parent branch + self.knownBranches = {} + self.initialParents = {} + + self.listExistingP4GitBranches() + + if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master") and not self.detectBranches: + ### needs to be ported to multi branch import - if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master"): print "Syncing with origin first as requested by calling git fetch origin" system("git fetch origin") [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) @@ -662,7 +679,8 @@ class P4Sync(Command): system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) if len(args) == 0: - if not gitBranchExists(self.branch) and gitBranchExists("origin"): + if not gitBranchExists(self.branch) and gitBranchExists("origin") and not self.detectBranches: + ### needs to be ported to multi branch import if not self.silent: print "Creating %s branch in git repository based on origin" % self.branch branch = self.branch @@ -670,11 +688,33 @@ class P4Sync(Command): branch = "refs/heads/" + branch system("git update-ref %s origin" % branch) - [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) - if len(self.previousDepotPath) > 0 and len(p4Change) > 0: - p4Change = int(p4Change) + 1 + if self.verbose: + print "branches: %s" % self.p4BranchesInGit + + p4Change = 0 + for branch in self.p4BranchesInGit: + depotPath, change = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("refs/remotes/p4/" + branch)) + + if self.verbose: + print "path %s change %s" % (depotPath, change) + + if len(depotPath) > 0 and len(change) > 0: + change = int(change) + 1 + p4Change = max(p4Change, change) + + if len(self.previousDepotPath) == 0: + self.previousDepotPath = depotPath + else: + i = 0 + l = min(len(self.previousDepotPath), len(depotPath)) + while i < l and self.previousDepotPath[i] == depotPath[i]: + i = i + 1 + self.previousDepotPath = self.previousDepotPath[:i] + + if p4Change > 0: self.depotPath = self.previousDepotPath - self.changeRange = "@%s,#head" % p4Change + #self.changeRange = "@%s,#head" % p4Change + self.changeRange = "@%s,%s" % (p4Change, p4Change + 10) self.initialParent = parseRevision(self.branch) if not self.silent: print "Performing incremental import into %s git branch" % self.branch @@ -729,7 +769,13 @@ class P4Sync(Command): if self.detectBranches: self.getBranchMapping(); - self.branchPrefix = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] + if self.verbose: + print "p4-git branches: %s" % self.p4BranchesInGit + print "initial parents: %s" % self.initialParents + for b in self.p4BranchesInGit: + if b != "master": + b = b[len(self.projectName):] + self.createdBranches.add(b) self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) @@ -784,6 +830,8 @@ class P4Sync(Command): changes.sort() else: + if self.verbose: + print "Getting p4 changes for %s...%s" % (self.depotPath, self.changeRange) output = mypopen("p4 changes %s...%s" % (self.depotPath, self.changeRange)).readlines() for line in output: @@ -816,26 +864,39 @@ class P4Sync(Command): filesForCommit = self.extractFilesInCommitToBranch(files, branch) + if self.verbose: + print "branch is %s" % branch + if branch not in self.createdBranches: self.createdBranches.add(branch) parent = self.knownBranches[branch] if parent == branch: parent = "" + elif self.verbose: + print "parent determined through known branches: %s" % parent # main branch? use master if branch == "main": branch = "master" else: - branch = self.branchPrefix + branch + branch = self.projectName + branch if parent == "main": parent = "master" elif len(parent) > 0: - parent = self.branchPrefix + parent + parent = self.projectName + parent branch = "refs/remotes/p4/" + branch if len(parent) > 0: parent = "refs/remotes/p4/" + parent + + if self.verbose: + print "looking for initial parent for %s; current parent is %s" % (branch, parent) + + if len(parent) == 0 and branch in self.initialParents: + parent = self.initialParents[branch] + del self.initialParents[branch] + self.commit(description, files, branch, branchPrefix, parent) else: self.commit(description, files, self.branch, self.depotPath, self.initialParent) @@ -849,9 +910,10 @@ class P4Sync(Command): self.gitStream.close() + if importProcess.wait() != 0: + die("fast-import failed: %s" % self.gitError.read()) self.gitOutput.close() self.gitError.close() - importProcess.wait() return True -- cgit v1.2.3 From d5904674d1a52620a702b4f5efa1afe21f8a5947 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 19 May 2007 11:07:32 +0200 Subject: Cleanup/speed up the branch<> file split and removed change range limitation that I added for debugging (oops). Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2c1dc9e2b3..35c5f9c696 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -461,15 +461,17 @@ class P4Sync(Command): fnum = fnum + 1 return files - def branchesForCommit(self, files): - branches = Set() + def splitFilesIntoBranches(self, files): + branches = {} for file in files: path = file["path"][len(self.depotPath):] for branch in self.knownBranches.keys(): if path.startswith(branch): - branches.add(branch) + if branch not in branches: + branches[branch] = [] + branches[branch].append(file["path"]) return branches @@ -574,16 +576,6 @@ class P4Sync(Command): if not self.silent: print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change) - def extractFilesInCommitToBranch(self, files, branchPrefix): - newFiles = [] - - for file in files: - path = file["path"] - if path.startswith(branchPrefix): - newFiles.append(file) - - return newFiles - def getUserMap(self): self.users = {} @@ -713,8 +705,7 @@ class P4Sync(Command): if p4Change > 0: self.depotPath = self.previousDepotPath - #self.changeRange = "@%s,#head" % p4Change - self.changeRange = "@%s,%s" % (p4Change, p4Change + 10) + self.changeRange = "@%s,#head" % p4Change self.initialParent = parseRevision(self.branch) if not self.silent: print "Performing incremental import into %s git branch" % self.branch @@ -857,12 +848,13 @@ class P4Sync(Command): try: files = self.extractFilesFromCommit(description) if self.detectBranches: - for branch in self.branchesForCommit(files): + branches = self.splitFilesIntoBranches(files) + for branch in branches.keys(): branchPrefix = self.depotPath + branch + "/" parent = "" - filesForCommit = self.extractFilesInCommitToBranch(files, branch) + filesForCommit = branches[branch] if self.verbose: print "branch is %s" % branch -- cgit v1.2.3 From 71b112d4a4e070170af7b5a50647f7dac9b48e56 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 19 May 2007 11:54:11 +0200 Subject: More cleanups and speedups for labels and branches Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 35c5f9c696..49114d2c6f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -461,17 +461,32 @@ class P4Sync(Command): fnum = fnum + 1 return files - def splitFilesIntoBranches(self, files): + def splitFilesIntoBranches(self, commit): branches = {} - for file in files: - path = file["path"][len(self.depotPath):] + fnum = 0 + while commit.has_key("depotFile%s" % fnum): + path = commit["depotFile%s" % fnum] + if not path.startswith(self.depotPath): + # if not self.silent: + # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.depotPath, change) + fnum = fnum + 1 + continue + + file = {} + file["path"] = path + file["rev"] = commit["rev%s" % fnum] + file["action"] = commit["action%s" % fnum] + file["type"] = commit["type%s" % fnum] + fnum = fnum + 1 + + relPath = path[len(self.depotPath):] for branch in self.knownBranches.keys(): - if path.startswith(branch): + if relPath.startswith(branch): if branch not in branches: branches[branch] = [] - branches[branch].append(file["path"]) + branches[branch].append(file) return branches @@ -542,6 +557,8 @@ class P4Sync(Command): label = self.labels[change] labelDetails = label[0] labelRevisions = label[1] + if self.verbose: + print "Change %s is labelled %s" % (change, labelDetails) files = p4CmdList("files %s...@%s" % (branchPrefix, change)) @@ -595,13 +612,15 @@ class P4Sync(Command): label = output["label"] revisions = {} newestChange = 0 - for file in p4CmdList("files //...@%s" % label): + if self.verbose: + print "Querying files for label %s" % label + for file in p4CmdList("files %s...@%s" % (self.depotPath, label)): revisions[file["depotFile"]] = file["rev"] change = int(file["change"]) if change > newestChange: newestChange = change - self.labels[newestChange] = [output, revisions] + self.labels[int(newestChange)] = [output, revisions] def getBranchMapping(self): self.projectName = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] @@ -846,9 +865,8 @@ class P4Sync(Command): cnt = cnt + 1 try: - files = self.extractFilesFromCommit(description) if self.detectBranches: - branches = self.splitFilesIntoBranches(files) + branches = self.splitFilesIntoBranches(description) for branch in branches.keys(): branchPrefix = self.depotPath + branch + "/" @@ -889,8 +907,9 @@ class P4Sync(Command): parent = self.initialParents[branch] del self.initialParents[branch] - self.commit(description, files, branch, branchPrefix, parent) + self.commit(description, filesForCommit, branch, branchPrefix, parent) else: + files = self.extractFilesFromCommit(description) self.commit(description, files, self.branch, self.depotPath, self.initialParent) self.initialParent = "" except IOError: -- cgit v1.2.3 From 9bda3a8556681c90309ec34e2ee5ae50306b61a6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 19 May 2007 12:05:40 +0200 Subject: Removed unused variable, more cleanups Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 49114d2c6f..f76d198d64 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -551,9 +551,7 @@ class P4Sync(Command): change = int(details["change"]) - self.lastChange = change - - if change in self.labels: + if self.labels.has_key(change): label = self.labels[change] labelDetails = label[0] labelRevisions = label[1] @@ -620,7 +618,10 @@ class P4Sync(Command): if change > newestChange: newestChange = change - self.labels[int(newestChange)] = [output, revisions] + self.labels[newestChange] = [output, revisions] + + if self.verbose: + print "Label changes: %s" % self.labels.keys() def getBranchMapping(self): self.projectName = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] @@ -748,7 +749,6 @@ class P4Sync(Command): self.revision = "" self.users = {} - self.lastChange = 0 if self.depotPath.find("@") != -1: atIdx = self.depotPath.index("@") -- cgit v1.2.3 From b607e71efdd23cd676645b5d7bf49d985834fab8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 10:55:54 +0200 Subject: Cache the output of "p4 users" for faster syncs on high latency links. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f76d198d64..e5e7c6be12 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -501,6 +501,8 @@ class P4Sync(Command): # gitStream.write("mark :%s\n" % details["change"]) self.committedChanges.add(int(details["change"])) committer = "" + if author not in self.users: + self.getUserMapFromPerforceServer() if author in self.users: committer = "%s %s %s" % (self.users[author], epoch, self.tz) else: @@ -591,7 +593,7 @@ class P4Sync(Command): if not self.silent: print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change) - def getUserMap(self): + def getUserMapFromPerforceServer(self): self.users = {} for output in p4CmdList("users"): @@ -599,6 +601,23 @@ class P4Sync(Command): continue self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" + cache = open(gitdir + "/p4-usercache.txt", "wb") + for user in self.users.keys(): + cache.write("%s\t%s\n" % (user, self.users[user])) + cache.close(); + + def loadUserMapFromCache(self): + self.users = {} + try: + cache = open(gitdir + "/p4-usercache.txt", "rb") + lines = cache.readlines() + cache.close() + for line in lines: + entry = line[:-1].split("\t") + self.users[entry[0]] = entry[1] + except IOError: + self.getUserMapFromPerforceServer() + def getLabels(self): self.labels = {} @@ -772,7 +791,7 @@ class P4Sync(Command): if not self.depotPath.endswith("/"): self.depotPath += "/" - self.getUserMap() + self.loadUserMapFromCache() self.labels = {} if self.detectLabels: self.getLabels(); -- cgit v1.2.3 From 59fa4171090ae8dcc7087ac617dca40e66f9b33a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 15:15:34 +0200 Subject: Fix gitdir not being set when cloning. Needed for writing the p4 users cache. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e5e7c6be12..14be55bcfc 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -972,6 +972,8 @@ class P4Clone(P4Sync): self.needsGit = False def run(self, args): + global gitdir + if len(args) < 1: return False depotPath = args[0] @@ -1007,6 +1009,7 @@ class P4Clone(P4Sync): os.makedirs(dir) os.chdir(dir) system("git init") + gitdir = os.getcwd() if not P4Sync.run(self, [depotPath]): return False if self.branch != "master": -- cgit v1.2.3 From 64ffb06a9c940e57a215d313cd58f702d695e919 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 15:24:01 +0200 Subject: Oops, not only /set/ gitdir on clone, also set it /correctly/ :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 14be55bcfc..80d966fb30 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1009,7 +1009,7 @@ class P4Clone(P4Sync): os.makedirs(dir) os.chdir(dir) system("git init") - gitdir = os.getcwd() + gitdir = os.getcwd() + "/.git" if not P4Sync.run(self, [depotPath]): return False if self.branch != "master": -- cgit v1.2.3 From 47a130b7bf74b8f61d9fee01f5dbb745d8f44b29 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 16:33:21 +0200 Subject: Use git format-patch and git apply --apply when extracting patches from git and applying them to a Perforce checkout. This should make it possible to apply git commits with binary files that cannot be handled by path. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 80d966fb30..0b1df09cb8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -213,10 +213,13 @@ class P4Submit(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - diffcmd = "git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\"" % (id, id) - patchcmd = diffcmd + " | patch -p1" + diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) + patchcmd = diffcmd + " | git apply " + tryPatchCmd = diffcmd + "--check -" + applyPatchCmd = diffcmd + "--check --apply -" + print mypopen(diffcmd).read() - if os.system(patchcmd + " --dry-run --silent") != 0: + if os.system(tryPatchCmd) != 0: print "Unfortunately applying the change failed!" print "What do you want to do?" response = "x" @@ -226,7 +229,7 @@ class P4Submit(Command): print "Skipping! Good luck with the next patches..." return elif response == "a": - os.system(patchcmd) + os.system(applyPatchCmd) if len(filesToAdd) > 0: print "You may also want to call p4 add on the following files:" print " ".join(filesToAdd) @@ -239,7 +242,7 @@ class P4Submit(Command): print "Patch saved to patch.txt in %s !" % self.clientPath die("Please resolve and submit the conflict manually and continue afterwards with git-p4 submit --continue") - system(patchcmd) + system(applyPatchCmd) for f in filesToAdd: system("p4 add %s" % f) -- cgit v1.2.3 From c1b296b9f19a62aa9e444cc454b3d62ffaa98da4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 16:55:05 +0200 Subject: Added support for git-p4 submit --direct (experimental) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 61 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0b1df09cb8..bcea4cf3de 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -137,6 +137,7 @@ class P4Submit(Command): optparse.make_option("--log-substitutions", dest="substFile"), optparse.make_option("--noninteractive", action="store_false"), optparse.make_option("--dry-run", action="store_true"), + optparse.make_option("--direct", dest="directSubmit", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -147,6 +148,7 @@ class P4Submit(Command): self.substFile = "" self.firstTime = True self.origin = "" + self.directSubmit = False self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -161,9 +163,12 @@ class P4Submit(Command): die("Cannot start sync. Previous sync config found at %s\nIf you want to start submitting again from scratch maybe you want to call git-p4 submit --reset" % self.configFile) commits = [] - for line in mypopen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): - commits.append(line[:-1]) - commits.reverse() + if self.directSubmit: + commits.append("0") + else: + for line in mypopen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + commits.append(line[:-1]) + commits.reverse() self.config["commits"] = commits @@ -191,8 +196,12 @@ class P4Submit(Command): return result def apply(self, id): - print "Applying %s" % (mypopen("git log --max-count=1 --pretty=oneline %s" % id).read()) - diff = mypopen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + if self.directSubmit: + print "Applying local change in working directory/index" + diff = self.diffStatus + else: + print "Applying %s" % (mypopen("git log --max-count=1 --pretty=oneline %s" % id).read()) + diff = mypopen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() filesToAdd = set() filesToDelete = set() editedFiles = set() @@ -213,11 +222,13 @@ class P4Submit(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) + if self.directSubmit: + diffcmd = "cat \"%s\"" % self.diffFile + else: + diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) patchcmd = diffcmd + " | git apply " - tryPatchCmd = diffcmd + "--check -" - applyPatchCmd = diffcmd + "--check --apply -" - print mypopen(diffcmd).read() + tryPatchCmd = patchcmd + "--check -" + applyPatchCmd = patchcmd + "--check --apply -" if os.system(tryPatchCmd) != 0: print "Unfortunately applying the change failed!" @@ -250,9 +261,11 @@ class P4Submit(Command): system("p4 revert %s" % f) system("p4 delete %s" % f) - logMessage = extractLogMessageFromGitCommit(id) - logMessage = logMessage.replace("\n", "\n\t") - logMessage = logMessage[:-1] + logMessage = "" + if not self.directSubmit: + logMessage = extractLogMessageFromGitCommit(id) + logMessage = logMessage.replace("\n", "\n\t") + logMessage = logMessage[:-1] template = mypopen("p4 change -o").read() @@ -356,6 +369,15 @@ class P4Submit(Command): print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath) oldWorkingDirectory = os.getcwd() + + if self.directSubmit: + self.diffStatus = mypopen("git diff -r --name-status HEAD").readlines() + patch = mypopen("git diff -p --binary --diff-filter=ACMRTUXB HEAD").read() + self.diffFile = gitdir + "/p4-git-diff" + f = open(self.diffFile, "wb") + f.write(patch) + f.close(); + os.chdir(self.clientPath) response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath) if response == "y" or response == "yes": @@ -395,14 +417,25 @@ class P4Submit(Command): self.config.close() + if self.directSubmit: + os.remove(self.diffFile) + if len(commits) == 0: if self.firstTime: print "No changes found to apply between %s and current HEAD" % self.origin else: print "All changes applied!" - response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") + response = "" + os.chdir(oldWorkingDirectory) + + if self.directSubmit: + response = raw_input("Do you want to DISCARD your git WORKING DIRECTORY CHANGES and sync from Perforce now using git-p4 rebase? [y]es/[n]o ") + if response == "y" or response == "yes": + system("git reset --hard") + + if len(response) == 0: + response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") if response == "y" or response == "yes": - os.chdir(oldWorkingDirectory) rebase = P4Rebase() rebase.run([]) os.remove(self.configFile) -- cgit v1.2.3 From 8a5fc95b43ab25e7ce0ca14cf733669e5708b448 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 23:39:40 +0200 Subject: Specifying --detect-branches is now only needed for the initial clone/sync. Afterwards it's turned on implicitly if more p4 branches than remotes/p4/master are found. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bcea4cf3de..d4bf67333f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -719,6 +719,9 @@ class P4Sync(Command): self.initialParents = {} self.listExistingP4GitBranches() + if len(self.p4BranchesInGit) > 1: + print "Importing from/into multiple branches" + self.detectBranches = True if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master") and not self.detectBranches: ### needs to be ported to multi branch import -- cgit v1.2.3 From 24f7b53fdd0bcc76453fc087932ca2cdecf47f9d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 23:42:22 +0200 Subject: Had an idea for debugging, record it :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d4bf67333f..92522417cd 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -7,6 +7,10 @@ # 2007 Trolltech ASA # License: MIT # +# TODO: * implement git-p4 rollback for debugging +# to roll back all p4 remote branches to a commit older or equal to +# the specified change. +# import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform -- cgit v1.2.3 From b1561ee25628c074b3afac738ead387181d3b311 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 23:52:51 +0200 Subject: Another (potentially life-saving) idea for submit --direct Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 92522417cd..5055f32140 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -10,6 +10,12 @@ # TODO: * implement git-p4 rollback for debugging # to roll back all p4 remote branches to a commit older or equal to # the specified change. +# * for git-p4 submit --direct it would be nice to still create a +# git commit without updating HEAD before submitting to perforce. +# With the commit sha1 printed (or recoded in a .git/foo file?) +# it's possible to recover if anything goes wrong instead of potentially +# loosing a change entirely because it was never comitted to git and +# the p4 submit failed (or resulted in lots of conflicts, etc.) # import optparse, sys, os, marshal, popen2, subprocess, shelve -- cgit v1.2.3 From 341dc1c1793455875086373a865db4d0a132ef6e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 00:39:16 +0200 Subject: Improved output for multi branch imports and noted another little todo item Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 5055f32140..beb6529b44 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -16,6 +16,8 @@ # it's possible to recover if anything goes wrong instead of potentially # loosing a change entirely because it was never comitted to git and # the p4 submit failed (or resulted in lots of conflicts, etc.) +# * Consider making --with-origin the default, assuming that the git +# protocol is always more efficient. (needs manual testing first :) # import optparse, sys, os, marshal, popen2, subprocess, shelve @@ -729,7 +731,7 @@ class P4Sync(Command): self.initialParents = {} self.listExistingP4GitBranches() - if len(self.p4BranchesInGit) > 1: + if len(self.p4BranchesInGit) > 1 and not self.silent: print "Importing from/into multiple branches" self.detectBranches = True @@ -795,7 +797,7 @@ class P4Sync(Command): self.depotPath = self.previousDepotPath self.changeRange = "@%s,#head" % p4Change self.initialParent = parseRevision(self.branch) - if not self.silent: + if not self.silent and not self.detectBranches: print "Performing incremental import into %s git branch" % self.branch if not self.branch.startswith("refs/"): @@ -920,15 +922,17 @@ class P4Sync(Command): if len(changes) == 0: if not self.silent: - print "no changes to import!" + print "No changes to import!" return True + self.updatedBranches = set() + cnt = 1 for change in changes: description = p4Cmd("describe %s" % change) if not self.silent: - sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) sys.stdout.flush() cnt = cnt + 1 @@ -945,6 +949,8 @@ class P4Sync(Command): if self.verbose: print "branch is %s" % branch + self.updatedBranches.add(branch) + if branch not in self.createdBranches: self.createdBranches.add(branch) parent = self.knownBranches[branch] @@ -984,8 +990,13 @@ class P4Sync(Command): print self.gitError.read() sys.exit(1) - if not self.silent: - print "" + if not self.silent: + print "" + if len(self.updatedBranches) > 0: + sys.stdout.write("Updated branches: ") + for b in self.updatedBranches: + sys.stdout.write("%s " % b) + sys.stdout.write("\n") self.gitStream.close() -- cgit v1.2.3 From 56d99c67d1ad95326902f3a739ab2a3c7bb2d6fe Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Sat, 19 May 2007 23:35:21 +0200 Subject: Update bash completion to ignore some more plumbing commands [sp: Modified Jonas' original patch to keep checkout-index as a a valid completion.] Signed-off-by: Jonas Fonseca Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 46356e8a27..35b1ff9157 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -262,6 +262,7 @@ __git_commands () applypatch) : ask gittus;; archimport) : import;; cat-file) : plumbing;; + check-attr) : plumbing;; check-ref-format) : plumbing;; commit-tree) : plumbing;; convert-objects) : plumbing;; @@ -271,8 +272,10 @@ __git_commands () daemon) : daemon;; fast-import) : import;; fsck-objects) : plumbing;; + fetch--tool) : plumbing;; fetch-pack) : plumbing;; fmt-merge-msg) : plumbing;; + for-each-ref) : plumbing;; hash-object) : plumbing;; http-*) : transport;; index-pack) : plumbing;; -- cgit v1.2.3 From 33be3e6550c7051c3ac4bc624c7dacfad6974b4f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 08:44:16 +0200 Subject: Fix conversion from old style heads/p4 to remotes/p4/master Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index beb6529b44..7489c91081 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -730,11 +730,6 @@ class P4Sync(Command): self.knownBranches = {} self.initialParents = {} - self.listExistingP4GitBranches() - if len(self.p4BranchesInGit) > 1 and not self.silent: - print "Importing from/into multiple branches" - self.detectBranches = True - if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master") and not self.detectBranches: ### needs to be ported to multi branch import @@ -760,6 +755,12 @@ class P4Sync(Command): if not gitBranchExists("refs/remotes/p4/HEAD"): system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) + # this needs to be called after the conversion from heads/p4 to remotes/p4/master + self.listExistingP4GitBranches() + if len(self.p4BranchesInGit) > 1 and not self.silent: + print "Importing from/into multiple branches" + self.detectBranches = True + if len(args) == 0: if not gitBranchExists(self.branch) and gitBranchExists("origin") and not self.detectBranches: ### needs to be ported to multi branch import -- cgit v1.2.3 From dc5240369610a6f72eab9b59447889dfd69b31c5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 09:34:56 +0200 Subject: Fix error detection with git-p4 submit when the requested depot path is not in the client view. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 7489c91081..73da5d2b27 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -55,6 +55,8 @@ def p4Where(depotPath): if not depotPath.endswith("/"): depotPath += "/" output = p4Cmd("where %s..." % depotPath) + if output["code"] == "error": + return "" clientPath = "" if "path" in output: clientPath = output.get("path") -- cgit v1.2.3 From faf1bd202640263b06c99c335269971aa9d9ead1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 10:05:30 +0200 Subject: Fix git symbolic-ref warning on initial clone Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 73da5d2b27..35a513fcb8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -732,6 +732,8 @@ class P4Sync(Command): self.knownBranches = {} self.initialParents = {} + createP4HeadRef = False; + if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master") and not self.detectBranches: ### needs to be ported to multi branch import @@ -754,8 +756,9 @@ class P4Sync(Command): if gitBranchExists("refs/heads/p4"): system("git update-ref %s refs/heads/p4" % self.branch) system("git branch -D p4"); + # create it /after/ importing, when master exists if not gitBranchExists("refs/remotes/p4/HEAD"): - system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) + createP4HeadRef = True # this needs to be called after the conversion from heads/p4 to remotes/p4/master self.listExistingP4GitBranches() @@ -1008,6 +1011,9 @@ class P4Sync(Command): self.gitOutput.close() self.gitError.close() + if createP4HeadRef: + system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) + return True class P4Rebase(Command): -- cgit v1.2.3 From cbf5efa61a30d6454f5739cee3498f98a01210da Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 10:08:11 +0200 Subject: Detect with git-p4 submit --direct when there are no changes in the working directory Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 35a513fcb8..f08ee6da44 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -386,6 +386,9 @@ class P4Submit(Command): if self.directSubmit: self.diffStatus = mypopen("git diff -r --name-status HEAD").readlines() + if len(self.diffStatus) == 0: + print "No changes in working directory to submit." + return True patch = mypopen("git diff -p --binary --diff-filter=ACMRTUXB HEAD").read() self.diffFile = gitdir + "/p4-git-diff" f = open(self.diffFile, "wb") -- cgit v1.2.3 From 7944f1425c0665eef6a5b9f5cc92e15cddb42984 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 11:04:26 +0200 Subject: Make git-p4 submit --direct safer by also creating a git commit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f08ee6da44..b32d8dbfd3 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -10,12 +10,6 @@ # TODO: * implement git-p4 rollback for debugging # to roll back all p4 remote branches to a commit older or equal to # the specified change. -# * for git-p4 submit --direct it would be nice to still create a -# git commit without updating HEAD before submitting to perforce. -# With the commit sha1 printed (or recoded in a .git/foo file?) -# it's possible to recover if anything goes wrong instead of potentially -# loosing a change entirely because it was never comitted to git and -# the p4 submit failed (or resulted in lots of conflicts, etc.) # * Consider making --with-origin the default, assuming that the git # protocol is always more efficient. (needs manual testing first :) # @@ -328,9 +322,17 @@ class P4Submit(Command): print submitTemplate raw_input("Press return to continue...") else: - pipe = os.popen("p4 submit -i", "wb") - pipe.write(submitTemplate) - pipe.close() + if self.directSubmit: + print "Submitting to git first" + os.chdir(self.oldWorkingDirectory) + pipe = os.popen("git commit -a -F -", "wb") + pipe.write(submitTemplate) + pipe.close() + os.chdir(self.clientPath) + + pipe = os.popen("p4 submit -i", "wb") + pipe.write(submitTemplate) + pipe.close() elif response == "s": for f in editedFiles: system("p4 revert \"%s\"" % f); @@ -382,7 +384,7 @@ class P4Submit(Command): sys.exit(128) print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath) - oldWorkingDirectory = os.getcwd() + self.oldWorkingDirectory = os.getcwd() if self.directSubmit: self.diffStatus = mypopen("git diff -r --name-status HEAD").readlines() @@ -442,16 +444,8 @@ class P4Submit(Command): print "No changes found to apply between %s and current HEAD" % self.origin else: print "All changes applied!" - response = "" - os.chdir(oldWorkingDirectory) - - if self.directSubmit: - response = raw_input("Do you want to DISCARD your git WORKING DIRECTORY CHANGES and sync from Perforce now using git-p4 rebase? [y]es/[n]o ") - if response == "y" or response == "yes": - system("git reset --hard") - - if len(response) == 0: - response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") + os.chdir(self.oldWorkingDirectory) + response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") if response == "y" or response == "yes": rebase = P4Rebase() rebase.run([]) -- cgit v1.2.3 From 5834684d51ee6e0c21cce8d1b5df06a34a36a4f9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 22:57:06 +0200 Subject: Added a rollback command for debugging. It sets back the heads of the p4 branches to the specified p4 change number or earlier. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b32d8dbfd3..40264cdc18 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -7,10 +7,7 @@ # 2007 Trolltech ASA # License: MIT # -# TODO: * implement git-p4 rollback for debugging -# to roll back all p4 remote branches to a commit older or equal to -# the specified change. -# * Consider making --with-origin the default, assuming that the git +# TODO: * Consider making --with-origin the default, assuming that the git # protocol is always more efficient. (needs manual testing first :) # @@ -135,6 +132,35 @@ class P4Debug(Command): print output return True +class P4RollBack(Command): + def __init__(self): + Command.__init__(self) + self.options = [ + ] + self.description = "A tool to debug the multi-branch import. Don't use :)" + + def run(self, args): + if len(args) != 1: + return False + maxChange = int(args[0]) + for line in mypopen("git rev-parse --symbolic --remotes").readlines(): + if line.startswith("p4/") and line != "p4/HEAD\n": + ref = "refs/remotes/" + line[:-1] + log = extractLogMessageFromGitCommit(ref) + depotPath, change = extractDepotPathAndChangeFromGitLog(log) + changed = False + while len(change) > 0 and int(change) > maxChange: + changed = True + print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange) + system("git update-ref %s \"%s^\"" % (ref, ref)) + log = extractLogMessageFromGitCommit(ref) + depotPath, change = extractDepotPathAndChangeFromGitLog(log) + + if changed: + print "%s is at %s" % (ref, change) + + return True + class P4Submit(Command): def __init__(self): Command.__init__(self) @@ -1109,7 +1135,8 @@ commands = { "submit" : P4Submit(), "sync" : P4Sync(), "rebase" : P4Rebase(), - "clone" : P4Clone() + "clone" : P4Clone(), + "rollback" : P4RollBack() } if len(sys.argv[1:]) == 0: -- cgit v1.2.3 From af8da89cb7225c7938a836de0812467c059ca52d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 23:25:51 +0200 Subject: Fix branch detection in multi-branch imports Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 40264cdc18..515f7a906f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -556,7 +556,7 @@ class P4Sync(Command): relPath = path[len(self.depotPath):] for branch in self.knownBranches.keys(): - if relPath.startswith(branch): + if relPath.startswith(branch + "/"): # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2 if branch not in branches: branches[branch] = [] branches[branch].append(file) -- cgit v1.2.3 From 52102d4784a5f16fd84eaf98b61f714460b68693 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 23:44:24 +0200 Subject: Fixes for rollback, delete branches that did not exist at the specified p4 change Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 515f7a906f..1457396abd 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -136,8 +136,10 @@ class P4RollBack(Command): def __init__(self): Command.__init__(self) self.options = [ + optparse.make_option("--verbose", dest="verbose", action="store_true") ] self.description = "A tool to debug the multi-branch import. Don't use :)" + self.verbose = False def run(self, args): if len(args) != 1: @@ -149,15 +151,22 @@ class P4RollBack(Command): log = extractLogMessageFromGitCommit(ref) depotPath, change = extractDepotPathAndChangeFromGitLog(log) changed = False + + if len(p4Cmd("changes -m 1 %s...@%s" % (depotPath, maxChange))) == 0: + print "Branch %s did not exist at change %s, deleting." % (ref, maxChange) + system("git update-ref -d %s `git rev-parse %s`" % (ref, ref)) + continue + while len(change) > 0 and int(change) > maxChange: changed = True - print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange) + if self.verbose: + print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange) system("git update-ref %s \"%s^\"" % (ref, ref)) log = extractLogMessageFromGitCommit(ref) depotPath, change = extractDepotPathAndChangeFromGitLog(log) if changed: - print "%s is at %s" % (ref, change) + print "%s rewound to %s" % (ref, change) return True -- cgit v1.2.3 From a028a98e9ae83231f0657fdb112f7d9c0cf0b98c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 00:03:08 +0200 Subject: Added support for importing multiple branches into refs/heads instead of just refs/remotes using --import-local. Needs some further microfix but seems to work otherwise. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1457396abd..969c1fe45b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -498,7 +498,8 @@ class P4Sync(Command): optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true"), - optparse.make_option("--verbose", dest="verbose", action="store_true") + optparse.make_option("--verbose", dest="verbose", action="store_true"), + optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -519,6 +520,7 @@ class P4Sync(Command): self.changesFile = "" self.syncWithOrigin = False self.verbose = False + self.importIntoRemotes = True def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -749,11 +751,17 @@ class P4Sync(Command): def listExistingP4GitBranches(self): self.p4BranchesInGit = [] - for line in mypopen("git rev-parse --symbolic --remotes").readlines(): + cmdline = "git rev-parse --symbolic " + if self.importIntoRemotes: + cmdline += " --remotes" + else: + cmdline += " --branches" + + for line in mypopen(cmdline).readlines(): if line.startswith("p4/") and line != "p4/HEAD\n": branch = line[3:-1] self.p4BranchesInGit.append(branch) - self.initialParents["refs/remotes/p4/" + branch] = parseRevision(line[:-1]) + self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) def run(self, args): self.depotPath = "" @@ -764,9 +772,14 @@ class P4Sync(Command): self.knownBranches = {} self.initialParents = {} + if self.importIntoRemotes: + self.refPrefix = "refs/remotes/p4/" + else: + self.refPrefix = "refs/heads/p4/" + createP4HeadRef = False; - if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master") and not self.detectBranches: + if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists(self.refPrefix + "master") and not self.detectBranches: ### needs to be ported to multi branch import print "Syncing with origin first as requested by calling git fetch origin" @@ -779,17 +792,17 @@ class P4Sync(Command): p4Change = int(p4Change) if originP4Change > p4Change: print "origin (%s) is newer than p4 (%s). Updating p4 branch from origin." % (originP4Change, p4Change) - system("git update-ref refs/remotes/p4/master origin"); + system("git update-ref " + self.refPrefix + "master origin"); else: print "Cannot sync with origin. It was imported from %s while remotes/p4 was imported from %s" % (originPreviousDepotPath, p4PreviousDepotPath) if len(self.branch) == 0: - self.branch = "refs/remotes/p4/master" - if gitBranchExists("refs/heads/p4"): + self.branch = self.refPrefix + "master" + if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: system("git update-ref %s refs/heads/p4" % self.branch) system("git branch -D p4"); # create it /after/ importing, when master exists - if not gitBranchExists("refs/remotes/p4/HEAD"): + if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: createP4HeadRef = True # this needs to be called after the conversion from heads/p4 to remotes/p4/master @@ -813,7 +826,7 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: - depotPath, change = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("refs/remotes/p4/" + branch)) + depotPath, change = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.refPrefix + branch)) if self.verbose: print "path %s change %s" % (depotPath, change) @@ -1008,9 +1021,9 @@ class P4Sync(Command): elif len(parent) > 0: parent = self.projectName + parent - branch = "refs/remotes/p4/" + branch + branch = self.refPrefix + branch if len(parent) > 0: - parent = "refs/remotes/p4/" + parent + parent = self.refPrefix + parent if self.verbose: print "looking for initial parent for %s; current parent is %s" % (branch, parent) @@ -1044,7 +1057,7 @@ class P4Sync(Command): self.gitError.close() if createP4HeadRef: - system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) + system("git symbolic-ref %s/HEAD %s" % (self.refPrefix, self.branch)) return True -- cgit v1.2.3 From 01a9c9c5a80c6a1e82298870ca8e5bc73b2a828d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 00:07:35 +0200 Subject: Added support for --max-changes= to ease import debugging Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 969c1fe45b..3d97ce1a24 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -499,7 +499,8 @@ class P4Sync(Command): optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true"), optparse.make_option("--verbose", dest="verbose", action="store_true"), - optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false") + optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"), + optparse.make_option("--max-changes", dest="maxChanges") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -521,6 +522,7 @@ class P4Sync(Command): self.syncWithOrigin = False self.verbose = False self.importIntoRemotes = True + self.maxChanges = "" def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -971,6 +973,9 @@ class P4Sync(Command): changes.reverse() + if len(self.maxChanges) > 0: + changes = changes[0:min(int(self.maxChanges), len(changes))] + if len(changes) == 0: if not self.silent: print "No changes to import!" -- cgit v1.2.3 From 57284050a8123a4c2d22d435a8c6daf1b53011e8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 00:15:50 +0200 Subject: Use refs/heads/* instead of refs/heads/p4/* for local imports Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3d97ce1a24..152c3c1ca5 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -760,10 +760,15 @@ class P4Sync(Command): cmdline += " --branches" for line in mypopen(cmdline).readlines(): - if line.startswith("p4/") and line != "p4/HEAD\n": + if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"): + continue + if self.importIntoRemotes: + # strip off p4 branch = line[3:-1] - self.p4BranchesInGit.append(branch) - self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) + else: + branch = line[:-1] + self.p4BranchesInGit.append(branch) + self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) def run(self, args): self.depotPath = "" @@ -777,11 +782,11 @@ class P4Sync(Command): if self.importIntoRemotes: self.refPrefix = "refs/remotes/p4/" else: - self.refPrefix = "refs/heads/p4/" + self.refPrefix = "refs/heads/" createP4HeadRef = False; - if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists(self.refPrefix + "master") and not self.detectBranches: + if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists(self.refPrefix + "master") and not self.detectBranches and self.importIntoRemotes: ### needs to be ported to multi branch import print "Syncing with origin first as requested by calling git fetch origin" -- cgit v1.2.3 From a396b292678838b4cae1b358c62da6b4e0929fc2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 00:33:34 +0200 Subject: Doc updates Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index ac8e6cff0b..aa9f31e5fc 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -120,6 +120,13 @@ continue importing the remaining changes with After submitting you should sync your perforce import branch ("p4" or "origin") from Perforce using git-p4's sync command. +If you have changes in your working directory that you haven't committed into +git yet but that you want to commit to Perforce directly ("quick fixes") then +you do not have to go through the intermediate step of creating a git commit +first but you can just call + + git-p4 submit --direct + Example ======= @@ -156,5 +163,5 @@ Implementation Details... to find out which changes need to be imported. * git-p4 submit uses "git rev-list" to pick the commits between the "p4" branch and the current branch. - The commits themselves are applied using git diff-tree ... | patch -p1 + The commits themselves are applied using git diff/format-patch ... | git apply -- cgit v1.2.3 From 65d2ade95e6ef5a15a30d5115073477dfe03fb85 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 16:41:46 +0200 Subject: Avoid calling git symbolic-ref refs/heads/p4//HEAD (double slash) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 152c3c1ca5..5cea6cf9ac 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1067,7 +1067,7 @@ class P4Sync(Command): self.gitError.close() if createP4HeadRef: - system("git symbolic-ref %s/HEAD %s" % (self.refPrefix, self.branch)) + system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) return True -- cgit v1.2.3 From 0c66a783936d5cd7b1f28400c6b192c37690304a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 20:07:57 +0200 Subject: Make rollback work with locally imported branches Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 5cea6cf9ac..f12ad8baff 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -136,18 +136,28 @@ class P4RollBack(Command): def __init__(self): Command.__init__(self) self.options = [ - optparse.make_option("--verbose", dest="verbose", action="store_true") + optparse.make_option("--verbose", dest="verbose", action="store_true"), + optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true") ] self.description = "A tool to debug the multi-branch import. Don't use :)" self.verbose = False + self.rollbackLocalBranches = False def run(self, args): if len(args) != 1: return False maxChange = int(args[0]) - for line in mypopen("git rev-parse --symbolic --remotes").readlines(): - if line.startswith("p4/") and line != "p4/HEAD\n": - ref = "refs/remotes/" + line[:-1] + + if self.rollbackLocalBranches: + refPrefix = "refs/heads/" + lines = mypopen("git rev-parse --symbolic --branches").readlines() + else: + refPrefix = "refs/remotes/" + lines = mypopen("git rev-parse --symbolic --remotes").readlines() + + for line in lines: + if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"): + ref = refPrefix + line[:-1] log = extractLogMessageFromGitCommit(ref) depotPath, change = extractDepotPathAndChangeFromGitLog(log) changed = False -- cgit v1.2.3 From a6d5da36af9a7087cf14e717b72c66ef2c34eb3b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 23:27:31 +0200 Subject: Don't make len(p4Cmd("p4 changes -m 1 //foo/...")) == 0 succeed when the p4 command itself failed. When the p4 command failed write out the exit code in the returned dict. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f12ad8baff..89a85ebb19 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -31,7 +31,9 @@ def p4CmdList(cmd): result.append(entry) except EOFError: pass - pipe.close() + exitCode = pipe.close() + if exitCode != None: + result["p4ExitCode"] = exitCode return result -- cgit v1.2.3 From ac3e0d79eef4535bb61d79315688fb1d225dea3b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 23:32:32 +0200 Subject: Oops, fill the /list/ correct with the p4 exit code. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 89a85ebb19..6ae3bc6e5d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -33,7 +33,9 @@ def p4CmdList(cmd): pass exitCode = pipe.close() if exitCode != None: - result["p4ExitCode"] = exitCode + entry = {} + entry["p4ExitCode"] = exitCode + result.append(entry) return result -- cgit v1.2.3 From 66a2f523958129e9b697d30ed44a5174010cb42a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 23:40:48 +0200 Subject: Catch p4 errors in rollback early enough (before deleting refs!) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6ae3bc6e5d..6d016b83d4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -152,6 +152,9 @@ class P4RollBack(Command): return False maxChange = int(args[0]) + if "p4ExitCode" in p4Cmd("p4 changes -m 1"): + die("Problems executing p4"); + if self.rollbackLocalBranches: refPrefix = "refs/heads/" lines = mypopen("git rev-parse --symbolic --branches").readlines() -- cgit v1.2.3 From ad192f2888816454df14b8a35945fb418bcb9e13 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 23:44:19 +0200 Subject: Fix p4 execution in git-p4 rollback. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6d016b83d4..b2341b7ec4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -152,7 +152,7 @@ class P4RollBack(Command): return False maxChange = int(args[0]) - if "p4ExitCode" in p4Cmd("p4 changes -m 1"): + if "p4ExitCode" in p4Cmd("changes -m 1"): die("Problems executing p4"); if self.rollbackLocalBranches: -- cgit v1.2.3 From b3fd1b28083cded86fd141c812cfd6d215ef4d5b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 23:53:14 +0200 Subject: Fix multi-branch import with --silent. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b2341b7ec4..d8b7080b4b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -831,8 +831,9 @@ class P4Sync(Command): # this needs to be called after the conversion from heads/p4 to remotes/p4/master self.listExistingP4GitBranches() - if len(self.p4BranchesInGit) > 1 and not self.silent: - print "Importing from/into multiple branches" + if len(self.p4BranchesInGit) > 1: + if not self.silent: + print "Importing from/into multiple branches" self.detectBranches = True if len(args) == 0: -- cgit v1.2.3 From ebd8116870247fbc9fb27656d28f1b8a43aae766 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 May 2007 00:24:52 +0200 Subject: Load the user map from p4 only once at run-time. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d8b7080b4b..9e9d623a3c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -697,6 +697,8 @@ class P4Sync(Command): print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change) def getUserMapFromPerforceServer(self): + if self.userMapFromPerforceServer: + return self.users = {} for output in p4CmdList("users"): @@ -708,9 +710,11 @@ class P4Sync(Command): for user in self.users.keys(): cache.write("%s\t%s\n" % (user, self.users[user])) cache.close(); + self.userMapFromPerforceServer = True def loadUserMapFromCache(self): self.users = {} + self.userMapFromPerforceServer = False try: cache = open(gitdir + "/p4-usercache.txt", "rb") lines = cache.readlines() -- cgit v1.2.3 From 5cfb4fe525034b758ca39a32d05cc8b2a53d2a13 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:01:02 -0400 Subject: Hide the plumbing diff-{files,index,tree} from bash completion The diff-* programs are meant to be plumbing for the diff frontend; most end users aren't invoking these commands directly. Consequently we should avoid showing them as possible completions. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 35b1ff9157..9944745028 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -270,6 +270,9 @@ __git_commands () cvsimport) : import;; cvsserver) : daemon;; daemon) : daemon;; + diff-files) : plumbing;; + diff-index) : plumbing;; + diff-tree) : plumbing;; fast-import) : import;; fsck-objects) : plumbing;; fetch--tool) : plumbing;; @@ -950,7 +953,6 @@ _git () commit) _git_commit ;; config) _git_config ;; diff) _git_diff ;; - diff-tree) _git_diff_tree ;; fetch) _git_fetch ;; format-patch) _git_format_patch ;; gc) _git_gc ;; @@ -995,7 +997,6 @@ complete -o default -o nospace -F _git_cherry git-cherry complete -o default -o nospace -F _git_cherry_pick git-cherry-pick complete -o default -o nospace -F _git_commit git-commit complete -o default -o nospace -F _git_diff git-diff -complete -o default -o nospace -F _git_diff_tree git-diff-tree complete -o default -o nospace -F _git_fetch git-fetch complete -o default -o nospace -F _git_format_patch git-format-patch complete -o default -o nospace -F _git_gc git-gc @@ -1026,7 +1027,6 @@ complete -o default -o nospace -F _git git.exe complete -o default -o nospace -F _git_branch git-branch.exe complete -o default -o nospace -F _git_cherry git-cherry.exe complete -o default -o nospace -F _git_diff git-diff.exe -complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe complete -o default -o nospace -F _git_format_patch git-format-patch.exe complete -o default -o nospace -F _git_log git-log.exe complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe -- cgit v1.2.3 From 1fd6bec9bcb2d7fc710a9866bd53ea52f43d4428 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:25:34 -0400 Subject: Teach bash completion about git-shortlog We've had completion for git-log for quite some time, but just today I noticed we don't have it for the new builtin shortlog that runs git-log internally. This is indeed a handy thing to have completion for, especially when your branch names are of the Very-Very-Long-and-Hard/To-Type/Variety/That-Some-Use. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9944745028..d75f47a1ac 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -896,6 +896,26 @@ _git_reset () __gitcomp "$(__git_refs)" } +_git_shortlog () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --max-count= --max-age= --since= --after= + --min-age= --before= --until= + --no-merges + --author= --committer= --grep= + --all-match + --not --all + --numbered --summary + " + return + ;; + esac + __git_complete_revlist +} + _git_show () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -967,6 +987,7 @@ _git () rebase) _git_rebase ;; remote) _git_remote ;; reset) _git_reset ;; + shortlog) _git_shortlog ;; show) _git_show ;; show-branch) _git_log ;; whatchanged) _git_log ;; @@ -1012,6 +1033,7 @@ complete -o default -o nospace -F _git_rebase git-rebase complete -o default -o nospace -F _git_config git-config complete -o default -o nospace -F _git_remote git-remote complete -o default -o nospace -F _git_reset git-reset +complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged @@ -1034,6 +1056,7 @@ complete -o default -o nospace -F _git_merge_base git-merge-base.exe complete -o default -o nospace -F _git_name_rev git-name-rev.exe complete -o default -o nospace -F _git_push git-push.exe complete -o default -o nospace -F _git_config git-config +complete -o default -o nospace -F _git_shortlog git-shortlog.exe complete -o default -o nospace -F _git_show git-show.exe complete -o default -o nospace -F _git_log git-show-branch.exe complete -o default -o nospace -F _git_log git-whatchanged.exe -- cgit v1.2.3 From bfbd131f52ff8373ffabbb30f275b4af8213f5f0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:26:58 -0400 Subject: Remove a duplicate --not option in bash completion This was just me being silly; I put the --not option into the completion list twice. There's no duplicates shown in the shell as the shell removes them before showing them to the user. But we really don't need the duplicates in the source script either. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d75f47a1ac..24b18183fd 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -579,7 +579,7 @@ _git_log () __gitcomp " --max-count= --max-age= --since= --after= --min-age= --before= --until= - --root --not --topo-order --date-order + --root --topo-order --date-order --no-merges --abbrev-commit --abbrev= --relative-date -- cgit v1.2.3 From c70680ce7cec72a465468c223d43c08f5254d31f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:36:46 -0400 Subject: Update bash completion header documentation 1) Added a note about supporting the long options for most commands, as we have been doing so for quite some time. 2) Include a notice that these routines are covered by the GPL, as that may not be obvious, even though they are distributed as part of the core Git distribution. 3) Added a short section on how to send patches to the routines, and to whom they should get sent to. Currently that is me, as I am the active maintainer. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 24b18183fd..48ba3f8e81 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1,8 +1,9 @@ # # bash completion support for core Git. # -# Copyright (C) 2006,2007 Shawn Pearce +# Copyright (C) 2006,2007 Shawn O. Pearce # Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/). +# Distributed under the GNU General Public License, version 2.0. # # The contained completion routines provide support for completing: # @@ -11,6 +12,7 @@ # *) .git/remotes file names # *) git 'subcommands' # *) tree paths within 'ref:path/to/file' expressions +# *) common --long-options # # To use these routines: # @@ -31,6 +33,17 @@ # are currently in a git repository. The %s token will be # the name of the current branch. # +# To submit patches: +# +# *) Read Documentation/SubmittingPatches +# *) Send all patches to the current maintainer: +# +# "Shawn O. Pearce" +# +# *) Always CC the Git mailing list: +# +# git@vger.kernel.org +# __gitdir () { -- cgit v1.2.3 From fb72759b7de504f077250fd5bd557e3b9e2a5682 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:46:49 -0400 Subject: Teach bash completion about 'git remote update' Recently the git-remote command grew an update subcommand, which can be used to execute git-fetch across multiple repositories in a single step. These can be configured with the 'remotes.*' configuration options, so we can offer completion for any name that matches and appears to be useful to git-remote update. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 48ba3f8e81..d6252068c4 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -877,13 +877,13 @@ _git_remote () while [ $c -lt $COMP_CWORD ]; do i="${COMP_WORDS[c]}" case "$i" in - add|show|prune) command="$i"; break ;; + add|show|prune|update) command="$i"; break ;; esac c=$((++c)) done if [ $c -eq $COMP_CWORD -a -z "$command" ]; then - __gitcomp "add show prune" + __gitcomp "add show prune update" return fi @@ -891,6 +891,18 @@ _git_remote () show|prune) __gitcomp "$(__git_remotes)" ;; + update) + local i c='' IFS=$'\n' + for i in $(git --git-dir="$(__gitdir)" config --list); do + case "$i" in + remotes.*) + i="${i#remotes.}" + c="$c ${i/=*/}" + ;; + esac + done + __gitcomp "$c" + ;; *) COMPREPLY=() ;; -- cgit v1.2.3 From 8f87fae6459b7d5b8058691911f1c18c59a5dcbd Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:51:30 -0400 Subject: Teach bash completion about recent log long options (Somewhat) recently git-log learned about --reverse (to show commits in the opposite order) and a looong time ago I think it learned about --raw (to show the raw diff, rather than a unified diff). These are both useful options, so we should make them easy for the user to complete. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d6252068c4..0b8cb5f0e6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -592,13 +592,13 @@ _git_log () __gitcomp " --max-count= --max-age= --since= --after= --min-age= --before= --until= - --root --topo-order --date-order + --root --topo-order --date-order --reverse --no-merges --abbrev-commit --abbrev= --relative-date --author= --committer= --grep= --all-match - --pretty= --name-status --name-only + --pretty= --name-status --name-only --raw --not --all " return -- cgit v1.2.3 From 12977705b3da7eb63cdfd890f838b971e8d81485 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 02:07:45 -0400 Subject: Update bash completion for git-config options A few new configuration options grew out of the woodwork during the 1.5.2 series. Most of these are pretty easy to support a completion of, so we do so. I wanted to also add completion support for the part of merge..name but to do that we have to look at all of the .gitattributes files and guess what the unique set of strings would be. Since this appears to be non-trivial I'm punting on it at this time. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0b8cb5f0e6..9e72f0f7b1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -764,9 +764,11 @@ _git_config () case "$cur" in --*) __gitcomp " - --global --list --replace-all + --global --system + --list --replace-all --get --get-all --get-regexp --add --unset --unset-all + --remove-section --rename-section " return ;; @@ -785,7 +787,10 @@ _git_config () remote.*.*) local pfx="${cur%.*}." cur="${cur##*.}" - __gitcomp "url fetch push" "$pfx" "$cur" + __gitcomp " + url fetch push skipDefaultUpdate + receivepack uploadpack tagopt + " "$pfx" "$cur" return ;; remote.*) @@ -835,6 +840,9 @@ _git_config () format.headers gitcvs.enabled gitcvs.logfile + gitcvs.allbinary + gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dvpass + gc.packrefs gc.reflogexpire gc.reflogexpireunreachable gc.rerereresolved @@ -851,9 +859,11 @@ _git_config () i18n.commitEncoding i18n.logOutputEncoding log.showroot + merge.tool merge.summary merge.verbosity pack.window + pack.depth pull.octopus pull.twohead repack.useDeltaBaseOffset -- cgit v1.2.3 From c1f9197f372a5378c3dbb36adeedf37ae175ecf1 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Thu, 24 May 2007 14:07:55 +0200 Subject: Replace \r\n with \n when importing from p4 on Windows Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9e9d623a3c..09b3cb5573 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -540,6 +540,7 @@ class P4Sync(Command): self.verbose = False self.importIntoRemotes = True self.maxChanges = "" + self.isWindows = (platform.system() == "Windows") def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -647,6 +648,9 @@ class P4Sync(Command): data = self.p4File(depotPath) + if self.isWindows and file["type"].endswith("text"): + data = data.replace("\r\n", "\n") + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) self.gitStream.write("data %s\n" % len(data)) self.gitStream.write(data) -- cgit v1.2.3 From d1874ed33bc346dca8b86891757703c908634aad Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 May 2007 21:23:04 +0200 Subject: Fix creating the remotes/p4 branches based on origin/* for the multi-branch import Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 09b3cb5573..b587e79b48 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -795,6 +795,20 @@ class P4Sync(Command): self.p4BranchesInGit.append(branch) self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) + def createBranchesFromOrigin(self): + if not self.silent: + print "Creating branch(es) in %s based on origin branch(es)" % self.refPrefix + + for line in mypopen("git rev-parse --symbolic --remotes"): + if (not line.startswith("origin/")) or line.endswith("HEAD\n"): + continue + headName = line[len("origin/"):-1] + remoteHead = self.refPrefix + headName + if not os.path.exists(gitdir + "/" + remoteHead): + if self.verbose: + print "creating %s" % remoteHead + system("git update-ref %s origin/%s" % (remoteHead, headName)) + def run(self, args): self.depotPath = "" self.changeRange = "" @@ -841,18 +855,14 @@ class P4Sync(Command): self.listExistingP4GitBranches() if len(self.p4BranchesInGit) > 1: if not self.silent: - print "Importing from/into multiple branches" + print "Importing from/into multiple branches" self.detectBranches = True if len(args) == 0: - if not gitBranchExists(self.branch) and gitBranchExists("origin") and not self.detectBranches: - ### needs to be ported to multi branch import - if not self.silent: - print "Creating %s branch in git repository based on origin" % self.branch - branch = self.branch - if not branch.startswith("refs"): - branch = "refs/heads/" + branch - system("git update-ref %s origin" % branch) + if len(self.p4BranchesInGit) == 0: + self.createBranchesFromOrigin() + self.listExistingP4GitBranches() + return True if self.verbose: print "branches: %s" % self.p4BranchesInGit -- cgit v1.2.3 From 2cc58fd99ae5bc5845792393f232853da1ff6082 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 May 2007 22:10:40 +0200 Subject: Forgot to remove this return statement from debugging Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b587e79b48..bfd950d53d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -862,7 +862,6 @@ class P4Sync(Command): if len(self.p4BranchesInGit) == 0: self.createBranchesFromOrigin() self.listExistingP4GitBranches() - return True if self.verbose: print "branches: %s" % self.p4BranchesInGit -- cgit v1.2.3 From abcd790fe9bc168041dcc2a0e678d22a135d33c8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 May 2007 22:25:36 +0200 Subject: Added support for --with-origin with multi-branch imports Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 58 ++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 30 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bfd950d53d..0597daa849 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -795,19 +795,37 @@ class P4Sync(Command): self.p4BranchesInGit.append(branch) self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) - def createBranchesFromOrigin(self): + def createOrUpdateBranchesFromOrigin(self): if not self.silent: - print "Creating branch(es) in %s based on origin branch(es)" % self.refPrefix + print "Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix for line in mypopen("git rev-parse --symbolic --remotes"): if (not line.startswith("origin/")) or line.endswith("HEAD\n"): continue headName = line[len("origin/"):-1] remoteHead = self.refPrefix + headName + originHead = "origin/" + headName + + update = False if not os.path.exists(gitdir + "/" + remoteHead): if self.verbose: print "creating %s" % remoteHead - system("git update-ref %s origin/%s" % (remoteHead, headName)) + update = True + else: + [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) + [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead)) + if len(originPreviousDepotPath) > 0 and len(originP4Change) > 0 and len(p4Change) > 0: + if originPreviousDepotPath == p4PreviousDepotPath: + originP4Change = int(originP4Change) + p4Change = int(p4Change) + if originP4Change > p4Change: + print "%s (%s) is newer than %s (%s). Updating p4 branch from origin." % (originHead, originP4Change, remoteHead, p4Change) + update = True + else: + print "Ignoring: %s was imported from %s while %s was imported from %s" % (originHead, originPreviousDepotPath, remoteHead, p4PreviousDepotPath) + + if update: + system("git update-ref %s %s" % (remoteHead, originHead)) def run(self, args): self.depotPath = "" @@ -825,23 +843,6 @@ class P4Sync(Command): createP4HeadRef = False; - if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists(self.refPrefix + "master") and not self.detectBranches and self.importIntoRemotes: - ### needs to be ported to multi branch import - - print "Syncing with origin first as requested by calling git fetch origin" - system("git fetch origin") - [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) - [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) - if len(originPreviousDepotPath) > 0 and len(originP4Change) > 0 and len(p4Change) > 0: - if originPreviousDepotPath == p4PreviousDepotPath: - originP4Change = int(originP4Change) - p4Change = int(p4Change) - if originP4Change > p4Change: - print "origin (%s) is newer than p4 (%s). Updating p4 branch from origin." % (originP4Change, p4Change) - system("git update-ref " + self.refPrefix + "master origin"); - else: - print "Cannot sync with origin. It was imported from %s while remotes/p4 was imported from %s" % (originPreviousDepotPath, p4PreviousDepotPath) - if len(self.branch) == 0: self.branch = self.refPrefix + "master" if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: @@ -851,17 +852,14 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: createP4HeadRef = True - # this needs to be called after the conversion from heads/p4 to remotes/p4/master - self.listExistingP4GitBranches() - if len(self.p4BranchesInGit) > 1: - if not self.silent: - print "Importing from/into multiple branches" - self.detectBranches = True - if len(args) == 0: - if len(self.p4BranchesInGit) == 0: - self.createBranchesFromOrigin() - self.listExistingP4GitBranches() + self.createOrUpdateBranchesFromOrigin() + self.listExistingP4GitBranches() + + if len(self.p4BranchesInGit) > 1: + if not self.silent: + print "Importing from/into multiple branches" + self.detectBranches = True if self.verbose: print "branches: %s" % self.p4BranchesInGit -- cgit v1.2.3 From 10f880f8d4e2c6390928a0bcc8b43161b5f845b2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 May 2007 22:28:28 +0200 Subject: Oops, fix --with-origin to /really/ also call git fetch :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0597daa849..08af23f9fd 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -841,6 +841,10 @@ class P4Sync(Command): else: self.refPrefix = "refs/heads/" + if self.syncWithOrigin: + print "Syncing with origin first as requested by calling git fetch origin" + system("git fetch origin") + createP4HeadRef = False; if len(self.branch) == 0: -- cgit v1.2.3 From 65c5f3e3f2dfb470c16628032235222a492e3239 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 08:44:41 +0200 Subject: Avoid creating non-p4 branches in remotes/p4 off of remotes/origin Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 08af23f9fd..1cce38a6f7 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -802,19 +802,23 @@ class P4Sync(Command): for line in mypopen("git rev-parse --symbolic --remotes"): if (not line.startswith("origin/")) or line.endswith("HEAD\n"): continue + headName = line[len("origin/"):-1] remoteHead = self.refPrefix + headName originHead = "origin/" + headName + [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) + if len(originPreviousDepotPath) == 0 or len(originP4Change) == 0: + continue + update = False if not os.path.exists(gitdir + "/" + remoteHead): if self.verbose: print "creating %s" % remoteHead update = True else: - [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead)) - if len(originPreviousDepotPath) > 0 and len(originP4Change) > 0 and len(p4Change) > 0: + if len(p4Change) > 0: if originPreviousDepotPath == p4PreviousDepotPath: originP4Change = int(originP4Change) p4Change = int(p4Change) -- cgit v1.2.3 From 4280e5333354c6dddcd994bdacd3c6a11ac2da5e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 08:49:18 +0200 Subject: Make git-p4 work with packed refs (don't use os.path.exists to check for the existance of a ref) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1cce38a6f7..e8a5c1fa31 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -412,7 +412,7 @@ class P4Submit(Command): if len(args) == 0: self.master = currentGitBranch() - if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): + if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master): die("Detecting current git branch failed!") elif len(args) == 1: self.master = args[0] @@ -812,7 +812,7 @@ class P4Sync(Command): continue update = False - if not os.path.exists(gitdir + "/" + remoteHead): + if not gitBranchExists(remoteHead): if self.verbose: print "creating %s" % remoteHead update = True -- cgit v1.2.3 From 417a7a6fc8c3d77e3e9a3bb0e11cb2eea978e20b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 10:28:46 +0200 Subject: Make --with-origin also work without origin :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e8a5c1fa31..30ee68c609 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -846,8 +846,9 @@ class P4Sync(Command): self.refPrefix = "refs/heads/" if self.syncWithOrigin: - print "Syncing with origin first as requested by calling git fetch origin" - system("git fetch origin") + if gitBranchExists("origin"): + print "Syncing with origin first as requested by calling git fetch origin" + system("git fetch origin") createP4HeadRef = False; -- cgit v1.2.3 From 01265103fe3966abd720ebb0bba1882a5701f327 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 10:36:10 +0200 Subject: Make --with-origin the default for syncing. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 ++++++++++------ contrib/fast-import/git-p4.txt | 14 +++----------- 2 files changed, 13 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 30ee68c609..ed5a5c593c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -118,6 +118,9 @@ def gitBranchExists(branch): proc = subprocess.Popen(["git", "rev-parse", branch], stderr=subprocess.PIPE, stdout=subprocess.PIPE); return proc.wait() == 0; +def gitConfig(key): + return mypopen("git config %s" % key).read()[:-1] + class Command: def __init__(self): self.usage = "usage: %prog [options]" @@ -514,7 +517,6 @@ class P4Sync(Command): optparse.make_option("--changesfile", dest="changesFile"), optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), - optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true"), optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"), optparse.make_option("--max-changes", dest="maxChanges") @@ -536,12 +538,15 @@ class P4Sync(Command): self.detectBranches = False self.detectLabels = False self.changesFile = "" - self.syncWithOrigin = False + self.syncWithOrigin = True self.verbose = False self.importIntoRemotes = True self.maxChanges = "" self.isWindows = (platform.system() == "Windows") + if gitConfig("git-p4.syncFromOrigin") == "false": + self.syncWithOrigin = False + def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -847,7 +852,8 @@ class P4Sync(Command): if self.syncWithOrigin: if gitBranchExists("origin"): - print "Syncing with origin first as requested by calling git fetch origin" + if not self.silent: + print "Syncing with origin first by calling git fetch origin" system("git fetch origin") createP4HeadRef = False; @@ -1116,13 +1122,11 @@ class P4Sync(Command): class P4Rebase(Command): def __init__(self): Command.__init__(self) - self.options = [ optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true") ] + self.options = [ ] self.description = "Fetches the latest revision from perforce and rebases the current work (branch) against it" - self.syncWithOrigin = False def run(self, args): sync = P4Sync() - sync.syncWithOrigin = self.syncWithOrigin sync.run([]) print "Rebasing the current branch" oldHead = mypopen("git rev-parse HEAD").read()[:-1] diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index aa9f31e5fc..c315158d8d 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -70,18 +70,10 @@ repository can be used to clone the working repository from and one would import from Perforce directly after cloning using git-p4. If the connection to the Perforce server is slow and the working repository hasn't been synced for a while it may be desirable to fetch changes from the origin git repository using -the efficient git protocol. git-p4 supports this through +the efficient git protocol. git-p4 supports this setup by calling "git fetch origin" +by default if there is an origin branch. You can disable this using - git-p4 sync --with-origin - -or - - git-p4 rebase --with-origin - -In that case "git fetch origin" is called and if it turns out that the origin -branch is newer than the git "p4" import branch then the latter is updated from -the former and the direct import from Perforce is resumed, which will result in -fewer changes to be imported using the slower perforce connection. + git config git-p4.syncFromOrigin false Updating ======== -- cgit v1.2.3 From d414c74afd8610c8c22dadf62b5ba8c6efa59e75 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 11:36:42 +0200 Subject: Shortcut the case where we have no origin branch Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ed5a5c593c..d99237c632 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -844,6 +844,7 @@ class P4Sync(Command): # map from branch depot path to parent branch self.knownBranches = {} self.initialParents = {} + self.hasOrigin = gitBranchExists("origin") if self.importIntoRemotes: self.refPrefix = "refs/remotes/p4/" @@ -851,7 +852,7 @@ class P4Sync(Command): self.refPrefix = "refs/heads/" if self.syncWithOrigin: - if gitBranchExists("origin"): + if self.hasOrigin: if not self.silent: print "Syncing with origin first by calling git fetch origin" system("git fetch origin") @@ -868,7 +869,8 @@ class P4Sync(Command): createP4HeadRef = True if len(args) == 0: - self.createOrUpdateBranchesFromOrigin() + if self.hasOrigin: + self.createOrUpdateBranchesFromOrigin() self.listExistingP4GitBranches() if len(self.p4BranchesInGit) > 1: -- cgit v1.2.3 From 877db584aae9816671147da45c30c31748ef287f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 19:43:38 +0200 Subject: Forgot to remove this TODO item when I made --with-origin the default :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 --- 1 file changed, 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d99237c632..0946965043 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -7,9 +7,6 @@ # 2007 Trolltech ASA # License: MIT # -# TODO: * Consider making --with-origin the default, assuming that the git -# protocol is always more efficient. (needs manual testing first :) -# import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform -- cgit v1.2.3 From cb4f1280dd5691890abea4521bff0a3d6e3facfd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 22:34:30 +0200 Subject: Added git-p4 submit --trust-me-like-a-fool for the adventurous users :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0946965043..dbd5b3bcf1 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -195,9 +195,9 @@ class P4Submit(Command): optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), optparse.make_option("--log-substitutions", dest="substFile"), - optparse.make_option("--noninteractive", action="store_false"), optparse.make_option("--dry-run", action="store_true"), optparse.make_option("--direct", dest="directSubmit", action="store_true"), + optparse.make_option("--trust-me-like-a-fool", dest="trustMeLikeAFool", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -209,6 +209,7 @@ class P4Submit(Command): self.firstTime = True self.origin = "" self.directSubmit = False + self.trustMeLikeAFool = False self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -348,6 +349,9 @@ class P4Submit(Command): separatorLine += "\n" response = "e" + if self.trustMeLikeAFool: + response = "y" + firstIteration = True while response == "e": if not firstIteration: -- cgit v1.2.3 From a3c55c09ecae2a9c852c8c10e668c901639e845b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 27 May 2007 15:48:01 +0200 Subject: Fix creation of refs/remotes/p4/HEAD symbolic ref Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index dbd5b3bcf1..aeefadcd66 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -858,8 +858,6 @@ class P4Sync(Command): print "Syncing with origin first by calling git fetch origin" system("git fetch origin") - createP4HeadRef = False; - if len(self.branch) == 0: self.branch = self.refPrefix + "master" if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: @@ -867,7 +865,7 @@ class P4Sync(Command): system("git branch -D p4"); # create it /after/ importing, when master exists if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: - createP4HeadRef = True + system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) if len(args) == 0: if self.hasOrigin: @@ -1117,9 +1115,6 @@ class P4Sync(Command): self.gitOutput.close() self.gitError.close() - if createP4HeadRef: - system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) - return True class P4Rebase(Command): -- cgit v1.2.3 From ce6f33c83537a04a3fb7557d7a74fe81ebccd7e4 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 16:46:29 -0300 Subject: Cleanups - don't use dir (python builtin) - use re for munging depotPath into destination Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index aeefadcd66..910e4ff3cd 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -10,6 +10,7 @@ import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform +import re from sets import Set; gitdir = os.environ.get("GIT_DIR", "") @@ -842,6 +843,7 @@ class P4Sync(Command): self.changeRange = "" self.initialParent = "" self.previousDepotPath = "" + # map from branch depot path to parent branch self.knownBranches = {} self.initialParents = {} @@ -1145,37 +1147,26 @@ class P4Clone(P4Sync): if len(args) < 1: return False depotPath = args[0] - dir = "" + destination = "" if len(args) == 2: - dir = args[1] + destination = args[1] elif len(args) > 2: return False if not depotPath.startswith("//"): return False - if len(dir) == 0: - dir = depotPath - atPos = dir.rfind("@") - if atPos != -1: - dir = dir[0:atPos] - hashPos = dir.rfind("#") - if hashPos != -1: - dir = dir[0:hashPos] - - if dir.endswith("..."): - dir = dir[:-3] - - if dir.endswith("/"): - dir = dir[:-1] + depotDir = re.sub("(@[^@]*)$", "", depotPath) + depotDir = re.sub("(#[^#]*)$", "", depotDir) + depotDir = re.sub(r"\.\.\.$,", "", depotDir) + depotDir = re.sub(r"/$", "", depotDir) - slashPos = dir.rfind("/") - if slashPos != -1: - dir = dir[slashPos + 1:] + if not destination: + destination = os.path.split(depotDir)[-1] - print "Importing from %s into %s" % (depotPath, dir) - os.makedirs(dir) - os.chdir(dir) + print "Importing from %s into %s" % (depotPath, destination) + os.makedirs(destination) + os.chdir(destination) system("git init") gitdir = os.getcwd() + "/.git" if not P4Sync.run(self, [depotPath]): -- cgit v1.2.3 From cebdf5af319ce638862fe2e2cd2797840962ddbb Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 16:53:11 -0300 Subject: reformatting: break long lines. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 48 +++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 910e4ff3cd..aba4752d4e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -222,7 +222,9 @@ class P4Submit(Command): def start(self): if len(self.config) > 0 and not self.reset: - die("Cannot start sync. Previous sync config found at %s\nIf you want to start submitting again from scratch maybe you want to call git-p4 submit --reset" % self.configFile) + die("Cannot start sync. Previous sync config found at %s\n" + "If you want to start submitting again from scratch " + "maybe you want to call git-p4 submit --reset" % self.configFile) commits = [] if self.directSubmit: @@ -297,7 +299,8 @@ class P4Submit(Command): print "What do you want to do?" response = "x" while response != "s" and response != "a" and response != "w": - response = raw_input("[s]kip this patch / [a]pply the patch forcibly and with .rej files / [w]rite the patch to a file (patch.txt) ") + response = raw_input("[s]kip this patch / [a]pply the patch forcibly " + "and with .rej files / [w]rite the patch to a file (patch.txt) ") if response == "s": print "Skipping! Good luck with the next patches..." return @@ -309,11 +312,13 @@ class P4Submit(Command): if len(filesToDelete): print "The following files should be scheduled for deletion with p4 delete:" print " ".join(filesToDelete) - die("Please resolve and submit the conflict manually and continue afterwards with git-p4 submit --continue") + die("Please resolve and submit the conflict manually and " + + "continue afterwards with git-p4 submit --continue") elif response == "w": system(diffcmd + " > patch.txt") print "Patch saved to patch.txt in %s !" % self.clientPath - die("Please resolve and submit the conflict manually and continue afterwards with git-p4 submit --continue") + die("Please resolve and submit the conflict manually and " + "continue afterwards with git-p4 submit --continue") system(applyPatchCmd) @@ -407,7 +412,9 @@ class P4Submit(Command): file = open(fileName, "w+") file.write(self.prepareLogMessage(template, logMessage)) file.close() - print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) + print ("Perforce submit template written as %s. " + + "Please review/edit and then use p4 submit -i < %s to submit directly!" + % (fileName, fileName)) def run(self, args): global gitdir @@ -634,7 +641,6 @@ class P4Sync(Command): for file in files: path = file["path"] if not path.startswith(branchPrefix): - # if not silent: # print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) continue rev = file["rev"] @@ -701,11 +707,13 @@ class P4Sync(Command): else: if not self.silent: - print "Tag %s does not match with change %s: files do not match." % (labelDetails["label"], change) + print ("Tag %s does not match with change %s: files do not match." + % (labelDetails["label"], change)) else: if not self.silent: - print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change) + print ("Tag %s does not match with change %s: file count is different." + % (labelDetails["label"], change)) def getUserMapFromPerforceServer(self): if self.userMapFromPerforceServer: @@ -854,11 +862,10 @@ class P4Sync(Command): else: self.refPrefix = "refs/heads/" - if self.syncWithOrigin: - if self.hasOrigin: - if not self.silent: - print "Syncing with origin first by calling git fetch origin" - system("git fetch origin") + if self.syncWithOrigin and self.hasOrigin: + if not self.silent: + print "Syncing with origin first by calling git fetch origin" + system("git fetch origin") if len(self.branch) == 0: self.branch = self.refPrefix + "master" @@ -884,7 +891,8 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: - depotPath, change = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.refPrefix + branch)) + logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch) + (depotPath, change) = extractDepotPathAndChangeFromGitLog(logMsg) if self.verbose: print "path %s change %s" % (depotPath, change) @@ -922,7 +930,8 @@ class P4Sync(Command): return False else: if len(self.depotPath) != 0 and self.depotPath != args[0]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.depotPath, args[0]) + print ("previous import used depot path %s and now %s was specified. " + "This doesn't work!" % (self.depotPath, args[0])) sys.exit(1) self.depotPath = args[0] @@ -968,7 +977,8 @@ class P4Sync(Command): self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) - importProcess = subprocess.Popen(["git", "fast-import"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); + importProcess = subprocess.Popen(["git", "fast-import"], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); self.gitOutput = importProcess.stdout self.gitStream = importProcess.stdin self.gitError = importProcess.stderr @@ -977,7 +987,8 @@ class P4Sync(Command): print "Doing initial import of %s from revision %s" % (self.depotPath, self.revision) details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = "Initial import of %s from the state at revision %s" % (self.depotPath, self.revision) + details["desc"] = ("Initial import of %s from the state at revision %s" + % (self.depotPath, self.revision)) details["change"] = self.revision newestRevision = 0 @@ -1123,7 +1134,8 @@ class P4Rebase(Command): def __init__(self): Command.__init__(self) self.options = [ ] - self.description = "Fetches the latest revision from perforce and rebases the current work (branch) against it" + self.description = ("Fetches the latest revision from perforce and " + + "rebases the current work (branch) against it") def run(self, args): sync = P4Sync() -- cgit v1.2.3 From 7cb5cbefd2c8b4c24bc87c6e30906907f94726ad Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 16:55:48 -0300 Subject: rename apply() to applyCommit(); apply is a python builtin Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index aba4752d4e..bd0ea54929 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -259,7 +259,7 @@ class P4Submit(Command): return result - def apply(self, id): + def applyCommit(self, id): if self.directSubmit: print "Applying local change in working directory/index" diff = self.diffStatus @@ -494,7 +494,7 @@ class P4Submit(Command): commit = commits[0] commits = commits[1:] self.config["commits"] = commits - self.apply(commit) + self.applyCommit(commit) if not self.interactive: break -- cgit v1.2.3 From c8cbbee980ac3961d5cdae164f2cb0a6b88075e0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 28 May 2007 14:43:25 +0200 Subject: Fix my email address, this isn't really KDE related :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bd0ea54929..400edce459 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -2,8 +2,8 @@ # # git-p4.py -- A tool for bidirectional operation between a Perforce depot and git. # -# Author: Simon Hausmann -# Copyright: 2007 Simon Hausmann +# Author: Simon Hausmann +# Copyright: 2007 Simon Hausmann # 2007 Trolltech ASA # License: MIT # -- cgit v1.2.3 From b016d39756f7d82e51c35e281673ed30b95cc586 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 17:10:46 -0300 Subject: Robustness fixes for pipes - add read_pipe(), read_pipe_lines(), write_pipe(), which check pipe.close() - use throughout Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 85 +++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 27 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 400edce459..05b308f5be 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -14,9 +14,43 @@ import re from sets import Set; gitdir = os.environ.get("GIT_DIR", "") +silent = False -def mypopen(command): - return os.popen(command, "rb"); +def write_pipe (c, str): + if not silent: + sys.stderr.write ('writing pipe: %s\n' % c) + + ## todo: check return status + pipe = os.popen (c, 'w') + val = pipe.write(str) + if pipe.close (): + sys.stderr.write ('Command failed') + sys.exit (1) + + return val + +def read_pipe (c): + sys.stderr.write ('reading pipe: %s\n' % c) + ## todo: check return status + pipe = os.popen (c, 'rb') + val = pipe.read() + if pipe.close (): + sys.stderr.write ('Command failed') + sys.exit (1) + + return val + + +def read_pipe_lines (c): + sys.stderr.write ('reading pipe: %s\n' % c) + ## todo: check return status + pipe = os.popen (c, 'rb') + val = pipe.readlines() + if pipe.close (): + sys.stderr.write ('Command failed') + sys.exit (1) + + return val def p4CmdList(cmd): cmd = "p4 -G %s" % cmd @@ -67,7 +101,7 @@ def die(msg): sys.exit(1) def currentGitBranch(): - return mypopen("git name-rev HEAD").read().split(" ")[1][:-1] + return read_pipe("git name-rev HEAD").split(" ")[1][:-1] def isValidGitDir(path): if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): @@ -75,7 +109,7 @@ def isValidGitDir(path): return False def parseRevision(ref): - return mypopen("git rev-parse %s" % ref).read()[:-1] + return read_pipe("git rev-parse %s" % ref)[:-1] def system(cmd): if os.system(cmd) != 0: @@ -83,8 +117,10 @@ def system(cmd): def extractLogMessageFromGitCommit(commit): logMessage = "" + + ## fixme: title is first line of commit, not 1st paragraph. foundTitle = False - for log in mypopen("git cat-file commit %s" % commit).readlines(): + for log in read_pipe_lines("git cat-file commit %s" % commit): if not foundTitle: if len(log) == 1: foundTitle = True @@ -158,10 +194,10 @@ class P4RollBack(Command): if self.rollbackLocalBranches: refPrefix = "refs/heads/" - lines = mypopen("git rev-parse --symbolic --branches").readlines() + lines = read_pipe_lines("git rev-parse --symbolic --branches") else: refPrefix = "refs/remotes/" - lines = mypopen("git rev-parse --symbolic --remotes").readlines() + lines = read_pipe_lines("git rev-parse --symbolic --remotes") for line in lines: if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"): @@ -230,7 +266,7 @@ class P4Submit(Command): if self.directSubmit: commits.append("0") else: - for line in mypopen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): commits.append(line[:-1]) commits.reverse() @@ -264,8 +300,8 @@ class P4Submit(Command): print "Applying local change in working directory/index" diff = self.diffStatus else: - print "Applying %s" % (mypopen("git log --max-count=1 --pretty=oneline %s" % id).read()) - diff = mypopen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) + diff = read_pipe_lines("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)) filesToAdd = set() filesToDelete = set() editedFiles = set() @@ -334,11 +370,11 @@ class P4Submit(Command): logMessage = logMessage.replace("\n", "\n\t") logMessage = logMessage[:-1] - template = mypopen("p4 change -o").read() + template = read_pipe("p4 change -o") if self.interactive: submitTemplate = self.prepareLogMessage(template, logMessage) - diff = mypopen("p4 diff -du ...").read() + diff = read_pipe("p4 diff -du ...") for newFile in filesToAdd: diff += "==== new file ====\n" @@ -387,14 +423,10 @@ class P4Submit(Command): if self.directSubmit: print "Submitting to git first" os.chdir(self.oldWorkingDirectory) - pipe = os.popen("git commit -a -F -", "wb") - pipe.write(submitTemplate) - pipe.close() + write_pipe("git commit -a -F -", submitTemplate) os.chdir(self.clientPath) - pipe = os.popen("p4 submit -i", "wb") - pipe.write(submitTemplate) - pipe.close() + write_pipe("p4 submit -i", submitTemplate) elif response == "s": for f in editedFiles: system("p4 revert \"%s\"" % f); @@ -451,11 +483,11 @@ class P4Submit(Command): self.oldWorkingDirectory = os.getcwd() if self.directSubmit: - self.diffStatus = mypopen("git diff -r --name-status HEAD").readlines() + self.diffStatus = read_pipe_lines("git diff -r --name-status HEAD") if len(self.diffStatus) == 0: print "No changes in working directory to submit." return True - patch = mypopen("git diff -p --binary --diff-filter=ACMRTUXB HEAD").read() + patch = read_pipe("git diff -p --binary --diff-filter=ACMRTUXB HEAD") self.diffFile = gitdir + "/p4-git-diff" f = open(self.diffFile, "wb") f.write(patch) @@ -557,7 +589,7 @@ class P4Sync(Command): self.syncWithOrigin = False def p4File(self, depotPath): - return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + return read_pipe("p4 print -q \"%s\"" % depotPath) def extractFilesFromCommit(self, commit): files = [] @@ -799,7 +831,7 @@ class P4Sync(Command): else: cmdline += " --branches" - for line in mypopen(cmdline).readlines(): + for line in read_pipe_lines(cmdline): if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"): continue if self.importIntoRemotes: @@ -1032,7 +1064,7 @@ class P4Sync(Command): else: if self.verbose: print "Getting p4 changes for %s...%s" % (self.depotPath, self.changeRange) - output = mypopen("p4 changes %s...%s" % (self.depotPath, self.changeRange)).readlines() + output = read_pipe_lines("p4 changes %s...%s" % (self.depotPath, self.changeRange)) for line in output: changeNum = line.split(" ")[1] @@ -1141,7 +1173,7 @@ class P4Rebase(Command): sync = P4Sync() sync.run([]) print "Rebasing the current branch" - oldHead = mypopen("git rev-parse HEAD").read()[:-1] + oldHead = read_pipe("git rev-parse HEAD")[:-1] system("git rebase p4") system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True @@ -1252,9 +1284,9 @@ if cmd.needsGit: if len(gitdir) == 0: gitdir = ".git" if not isValidGitDir(gitdir): - gitdir = mypopen("git rev-parse --git-dir").read()[:-1] + gitdir = read_pipe("git rev-parse --git-dir")[:-1] if os.path.exists(gitdir): - cdup = mypopen("git rev-parse --show-cdup").read()[:-1]; + cdup = read_pipe("git rev-parse --show-cdup")[:-1]; if len(cdup) > 0: os.chdir(cdup); @@ -1268,4 +1300,3 @@ if cmd.needsGit: if not cmd.run(args): parser.print_help() - -- cgit v1.2.3 From bce4c5fc0b6aa9e599fc181c5350b461dafecee2 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 17:14:33 -0300 Subject: cleanup - use re.sub() iso. if for stripping ... - spacing nits Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 05b308f5be..8d649dd762 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -16,39 +16,39 @@ from sets import Set; gitdir = os.environ.get("GIT_DIR", "") silent = False -def write_pipe (c, str): +def write_pipe(c, str): if not silent: - sys.stderr.write ('writing pipe: %s\n' % c) + sys.stderr.write('writing pipe: %s\n' % c) ## todo: check return status - pipe = os.popen (c, 'w') + pipe = os.popen(c, 'w') val = pipe.write(str) - if pipe.close (): - sys.stderr.write ('Command failed') - sys.exit (1) + if pipe.close(): + sys.stderr.write('Command failed') + sys.exit(1) return val -def read_pipe (c): - sys.stderr.write ('reading pipe: %s\n' % c) +def read_pipe(c): + sys.stderr.write('reading pipe: %s\n' % c) ## todo: check return status - pipe = os.popen (c, 'rb') + pipe = os.popen(c, 'rb') val = pipe.read() - if pipe.close (): - sys.stderr.write ('Command failed') - sys.exit (1) + if pipe.close(): + sys.stderr.write('Command failed') + sys.exit(1) return val -def read_pipe_lines (c): - sys.stderr.write ('reading pipe: %s\n' % c) +def read_pipe_lines(c): + sys.stderr.write('reading pipe: %s\n' % c) ## todo: check return status - pipe = os.popen (c, 'rb') + pipe = os.popen(c, 'rb') val = pipe.readlines() - if pipe.close (): - sys.stderr.write ('Command failed') - sys.exit (1) + if pipe.close(): + sys.stderr.write('Command failed') + sys.exit(1) return val @@ -986,9 +986,7 @@ class P4Sync(Command): elif len(self.previousDepotPath) == 0: self.revision = "#head" - if self.depotPath.endswith("..."): - self.depotPath = self.depotPath[:-3] - + self.depotPath = re.sub ("\.\.\.$", "", self.depotPath) if not self.depotPath.endswith("/"): self.depotPath += "/" -- cgit v1.2.3 From 6754a299d8d977326e396d641106cc5db4d29db1 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 17:41:50 -0300 Subject: minor cleanups Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 8d649dd762..93c9d6417c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -30,7 +30,8 @@ def write_pipe(c, str): return val def read_pipe(c): - sys.stderr.write('reading pipe: %s\n' % c) + if not silent: + sys.stderr.write('reading pipe: %s\n' % c) ## todo: check return status pipe = os.popen(c, 'rb') val = pipe.read() @@ -42,7 +43,8 @@ def read_pipe(c): def read_pipe_lines(c): - sys.stderr.write('reading pipe: %s\n' % c) + if not silent: + sys.stderr.write('reading pipe: %s\n' % c) ## todo: check return status pipe = os.popen(c, 'rb') val = pipe.readlines() @@ -52,6 +54,14 @@ def read_pipe_lines(c): return val +def system(cmd): + if not silent: + sys.stderr.write("executing %s" % cmd) + if os.system(cmd) != 0: + die("command failed: %s" % cmd) + + + def p4CmdList(cmd): cmd = "p4 -G %s" % cmd pipe = os.popen(cmd, "rb") @@ -111,10 +121,6 @@ def isValidGitDir(path): def parseRevision(ref): return read_pipe("git rev-parse %s" % ref)[:-1] -def system(cmd): - if os.system(cmd) != 0: - die("command failed: %s" % cmd) - def extractLogMessageFromGitCommit(commit): logMessage = "" @@ -571,7 +577,6 @@ class P4Sync(Command): (a ... is not needed in the path p4 specification, it's added implicitly)""" self.usage += " //depot/path[@revRange]" - self.silent = False self.createdBranches = Set() self.committedChanges = Set() @@ -584,6 +589,7 @@ class P4Sync(Command): self.importIntoRemotes = True self.maxChanges = "" self.isWindows = (platform.system() == "Windows") + self.depotPath = None if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False @@ -611,9 +617,11 @@ class P4Sync(Command): fnum = fnum + 1 return files + def stripRepoPath(self, path): + return path[len(self.depotPath):] + def splitFilesIntoBranches(self, commit): branches = {} - fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] @@ -630,10 +638,12 @@ class P4Sync(Command): file["type"] = commit["type%s" % fnum] fnum = fnum + 1 - relPath = path[len(self.depotPath):] + relPath = self.stripRepoPath(path) for branch in self.knownBranches.keys(): - if relPath.startswith(branch + "/"): # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2 + + # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2 + if relPath.startswith(branch + "/"): if branch not in branches: branches[branch] = [] branches[branch].append(file) -- cgit v1.2.3 From 8b41a97f8a3e2778ce733cd3d7e181bc28cc43a0 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:20:53 -0300 Subject: clone and sync --keep-path to keep perforce path to module. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 93c9d6417c..51d117b789 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -11,6 +11,7 @@ import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform import re + from sets import Set; gitdir = os.environ.get("GIT_DIR", "") @@ -20,7 +21,6 @@ def write_pipe(c, str): if not silent: sys.stderr.write('writing pipe: %s\n' % c) - ## todo: check return status pipe = os.popen(c, 'w') val = pipe.write(str) if pipe.close(): @@ -32,7 +32,7 @@ def write_pipe(c, str): def read_pipe(c): if not silent: sys.stderr.write('reading pipe: %s\n' % c) - ## todo: check return status + pipe = os.popen(c, 'rb') val = pipe.read() if pipe.close(): @@ -60,8 +60,6 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) - - def p4CmdList(cmd): cmd = "p4 -G %s" % cmd pipe = os.popen(cmd, "rb") @@ -566,7 +564,8 @@ class P4Sync(Command): optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"), - optparse.make_option("--max-changes", dest="maxChanges") + optparse.make_option("--max-changes", dest="maxChanges"), + optparse.make_option("--keep-path", dest="keepRepoPath") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -590,6 +589,7 @@ class P4Sync(Command): self.maxChanges = "" self.isWindows = (platform.system() == "Windows") self.depotPath = None + self.keepRepoPath = False if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False @@ -617,8 +617,11 @@ class P4Sync(Command): fnum = fnum + 1 return files - def stripRepoPath(self, path): - return path[len(self.depotPath):] + def stripRepoPath(self, path, prefix): + if self.keepRepoPath: + prefix = re.sub("^(//[^/]+/).*", r'\1', prefix) + + return path[len(prefix):] def splitFilesIntoBranches(self, commit): branches = {} @@ -638,7 +641,7 @@ class P4Sync(Command): file["type"] = commit["type%s" % fnum] fnum = fnum + 1 - relPath = self.stripRepoPath(path) + relPath = self.stripRepoPath(path, self.depotPath) for branch in self.knownBranches.keys(): @@ -687,7 +690,7 @@ class P4Sync(Command): continue rev = file["rev"] depotPath = path + "#" + rev - relPath = path[len(branchPrefix):] + relPath = self.stripRepoPath(path, branchPrefix) action = file["action"] if file["type"] == "apple": -- cgit v1.2.3 From b76f0565bfcfedba9e8920aa5120fc97796b642f Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:29:34 -0300 Subject: use string.strip() iso. slicing. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 51d117b789..ac446a8dc4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -845,15 +845,15 @@ class P4Sync(Command): cmdline += " --branches" for line in read_pipe_lines(cmdline): + lie = line.strip() if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"): continue if self.importIntoRemotes: # strip off p4 - branch = line[3:-1] - else: - branch = line[:-1] + branch = re.sub ("^p4/", "", line) + self.p4BranchesInGit.append(branch) - self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) + self.initialParents[self.refPrefix + branch] = parseRevision(line) def createOrUpdateBranchesFromOrigin(self): if not self.silent: -- cgit v1.2.3 From b25b20656dceff6145a1286f10618ab25d717537 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: use strip() iso. slicing for removing \n Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ac446a8dc4..efa2fce29e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -15,7 +15,7 @@ import re from sets import Set; gitdir = os.environ.get("GIT_DIR", "") -silent = False +silent = True def write_pipe(c, str): if not silent: @@ -109,7 +109,7 @@ def die(msg): sys.exit(1) def currentGitBranch(): - return read_pipe("git name-rev HEAD").split(" ")[1][:-1] + return read_pipe("git name-rev HEAD").split(" ")[1].strip() def isValidGitDir(path): if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): @@ -117,7 +117,7 @@ def isValidGitDir(path): return False def parseRevision(ref): - return read_pipe("git rev-parse %s" % ref)[:-1] + return read_pipe("git rev-parse %s" % ref).strip() def extractLogMessageFromGitCommit(commit): logMessage = "" @@ -205,7 +205,8 @@ class P4RollBack(Command): for line in lines: if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"): - ref = refPrefix + line[:-1] + line = line.strip() + ref = refPrefix + line log = extractLogMessageFromGitCommit(ref) depotPath, change = extractDepotPathAndChangeFromGitLog(log) changed = False @@ -271,7 +272,7 @@ class P4Submit(Command): commits.append("0") else: for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): - commits.append(line[:-1]) + commits.append(line.strip()) commits.reverse() self.config["commits"] = commits @@ -372,7 +373,7 @@ class P4Submit(Command): if not self.directSubmit: logMessage = extractLogMessageFromGitCommit(id) logMessage = logMessage.replace("\n", "\n\t") - logMessage = logMessage[:-1] + logMessage = logMessage.strip() template = read_pipe("p4 change -o") @@ -513,7 +514,7 @@ class P4Submit(Command): if len(self.substFile) > 0: for line in open(self.substFile, "r").readlines(): - tokens = line[:-1].split("=") + tokens = line.strip().split("=") self.logSubstitutions[tokens[0]] = tokens[1] self.check() @@ -784,7 +785,7 @@ class P4Sync(Command): lines = cache.readlines() cache.close() for line in lines: - entry = line[:-1].split("\t") + entry = line.strip().split("\t") self.users[entry[0]] = entry[1] except IOError: self.getUserMapFromPerforceServer() @@ -814,7 +815,7 @@ class P4Sync(Command): print "Label changes: %s" % self.labels.keys() def getBranchMapping(self): - self.projectName = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] + self.projectName = self.depotPath[self.depotPath.strip().rfind("/") + 1:] for info in p4CmdList("branches"): details = p4Cmd("branch -o %s" % info["branch"]) @@ -848,6 +849,7 @@ class P4Sync(Command): lie = line.strip() if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"): continue + if self.importIntoRemotes: # strip off p4 branch = re.sub ("^p4/", "", line) @@ -966,7 +968,7 @@ class P4Sync(Command): self.branch = "refs/heads/" + self.branch if len(self.depotPath) != 0: - self.depotPath = self.depotPath[:-1] + self.depotPath = self.depotPath.strip() if len(args) == 0 and len(self.depotPath) != 0: if not self.silent: @@ -1184,7 +1186,7 @@ class P4Rebase(Command): sync = P4Sync() sync.run([]) print "Rebasing the current branch" - oldHead = read_pipe("git rev-parse HEAD")[:-1] + oldHead = read_pipe("git rev-parse HEAD").strip() system("git rebase p4") system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True @@ -1217,7 +1219,7 @@ class P4Clone(P4Sync): depotDir = re.sub(r"/$", "", depotDir) if not destination: - destination = os.path.split(depotDir)[-1] + destination = os.path.split(depotDir)[1] print "Importing from %s into %s" % (depotPath, destination) os.makedirs(destination) @@ -1295,9 +1297,9 @@ if cmd.needsGit: if len(gitdir) == 0: gitdir = ".git" if not isValidGitDir(gitdir): - gitdir = read_pipe("git rev-parse --git-dir")[:-1] + gitdir = read_pipe("git rev-parse --git-dir").strip() if os.path.exists(gitdir): - cdup = read_pipe("git rev-parse --show-cdup")[:-1]; + cdup = read_pipe("git rev-parse --show-cdup").strip() if len(cdup) > 0: os.chdir(cdup); -- cgit v1.2.3 From 4addad229169ece62f36c6c17cc15bdf532a60bc Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: add --verbose to all commands. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index efa2fce29e..cf9db73da6 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -15,47 +15,47 @@ import re from sets import Set; gitdir = os.environ.get("GIT_DIR", "") -silent = True +verbose = False def write_pipe(c, str): - if not silent: + if verbose: sys.stderr.write('writing pipe: %s\n' % c) pipe = os.popen(c, 'w') val = pipe.write(str) if pipe.close(): - sys.stderr.write('Command failed') + sys.stderr.write('Command %s failed\n' % c) sys.exit(1) return val -def read_pipe(c): - if not silent: +def read_pipe(c, ignore_error=False): + if verbose: sys.stderr.write('reading pipe: %s\n' % c) pipe = os.popen(c, 'rb') val = pipe.read() - if pipe.close(): - sys.stderr.write('Command failed') + if pipe.close() and not ignore_error: + sys.stderr.write('Command %s failed\n' % c) sys.exit(1) return val def read_pipe_lines(c): - if not silent: + if verbose: sys.stderr.write('reading pipe: %s\n' % c) ## todo: check return status pipe = os.popen(c, 'rb') val = pipe.readlines() if pipe.close(): - sys.stderr.write('Command failed') + sys.stderr.write('Command %s failed\n' % c) sys.exit(1) return val def system(cmd): - if not silent: + if verbose: sys.stderr.write("executing %s" % cmd) if os.system(cmd) != 0: die("command failed: %s" % cmd) @@ -157,7 +157,7 @@ def gitBranchExists(branch): return proc.wait() == 0; def gitConfig(key): - return mypopen("git config %s" % key).read()[:-1] + return read_pipe("git config %s" % key, ignore_error=True).strip() class Command: def __init__(self): @@ -168,7 +168,8 @@ class P4Debug(Command): def __init__(self): Command.__init__(self) self.options = [ - ] + optparse.make_option("--verbose", dest="verbose", action="store_true"), + ] self.description = "A tool to debug the output of p4 -G." self.needsGit = False @@ -234,6 +235,7 @@ class P4Submit(Command): Command.__init__(self) self.options = [ optparse.make_option("--continue", action="store_false", dest="firstTime"), + optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), optparse.make_option("--log-substitutions", dest="substFile"), @@ -861,11 +863,12 @@ class P4Sync(Command): if not self.silent: print "Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix - for line in mypopen("git rev-parse --symbolic --remotes"): + for line in read_pipe_lines("git rev-parse --symbolic --remotes"): + line = line.strip() if (not line.startswith("origin/")) or line.endswith("HEAD\n"): continue - headName = line[len("origin/"):-1] + headName = line[len("origin/")] remoteHead = self.refPrefix + headName originHead = "origin/" + headName @@ -1292,6 +1295,7 @@ if len(options) > 0: (cmd, args) = parser.parse_args(sys.argv[2:], cmd); +verbose = cmd.verbose if cmd.needsGit: gitdir = cmd.gitdir if len(gitdir) == 0: -- cgit v1.2.3 From 6326aa58662932212678a25c6f482a8da4195ac9 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: Extract multiple paths concurrently. This enables importing just the interesting bits of large repositories. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 262 ++++++++++++++++++++++++++------------------- 1 file changed, 153 insertions(+), 109 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index cf9db73da6..ad145e8595 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -133,24 +133,26 @@ def extractLogMessageFromGitCommit(commit): logMessage += log return logMessage -def extractDepotPathAndChangeFromGitLog(log): +def extractDepotPathsAndChangeFromGitLog(log): values = {} for line in log.split("\n"): line = line.strip() - if line.startswith("[git-p4:") and line.endswith("]"): - line = line[8:-1].strip() - for assignment in line.split(":"): - variable = assignment.strip() - value = "" - equalPos = assignment.find("=") - if equalPos != -1: - variable = assignment[:equalPos].strip() - value = assignment[equalPos + 1:].strip() - if value.startswith("\"") and value.endswith("\""): - value = value[1:-1] - values[variable] = value - - return values.get("depot-path"), values.get("change") + m = re.search (r"^ *\[git-p4: (.*)\]$", line) + if not m: + continue + + assignments = m.group(1).split (':') + for a in assignments: + vals = a.split ('=') + key = vals[0].strip() + val = ('='.join (vals[1:])).strip() + if val.endswith ('\"') and val.startswith('"'): + val = val[1:-1] + + values[key] = val + + paths = values.get("depot-path").split(',') + return paths, values.get("change") def gitBranchExists(branch): proc = subprocess.Popen(["git", "rev-parse", branch], stderr=subprocess.PIPE, stdout=subprocess.PIPE); @@ -209,10 +211,11 @@ class P4RollBack(Command): line = line.strip() ref = refPrefix + line log = extractLogMessageFromGitCommit(ref) - depotPath, change = extractDepotPathAndChangeFromGitLog(log) + depotPaths, change = extractDepotPathsAndChangeFromGitLog(log) changed = False - if len(p4Cmd("changes -m 1 %s...@%s" % (depotPath, maxChange))) == 0: + if len(p4Cmd("changes -m 1 " + ' '.join (['%s...@%s' % (p, maxChange) + for p in depotPaths]))) == 0: print "Branch %s did not exist at change %s, deleting." % (ref, maxChange) system("git update-ref -d %s `git rev-parse %s`" % (ref, ref)) continue @@ -223,7 +226,7 @@ class P4RollBack(Command): print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange) system("git update-ref %s \"%s^\"" % (ref, ref)) log = extractLogMessageFromGitCommit(ref) - depotPath, change = extractDepotPathAndChangeFromGitLog(log) + depotPaths, change = extractDepotPathsAndChangeFromGitLog(log) if changed: print "%s rewound to %s" % (ref, change) @@ -472,9 +475,9 @@ class P4Submit(Command): depotPath = "" if gitBranchExists("p4"): - [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) + [depotPaths, dummy] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) if len(depotPath) == 0 and gitBranchExists("origin"): - [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) + [depotPaths, dummy] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) if len(depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" @@ -568,7 +571,7 @@ class P4Sync(Command): optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"), optparse.make_option("--max-changes", dest="maxChanges"), - optparse.make_option("--keep-path", dest="keepRepoPath") + optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true') ] self.description = """Imports from Perforce into a git repository.\n example: @@ -591,8 +594,8 @@ class P4Sync(Command): self.importIntoRemotes = True self.maxChanges = "" self.isWindows = (platform.system() == "Windows") - self.depotPath = None self.keepRepoPath = False + self.depotPaths = None if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False @@ -605,9 +608,10 @@ class P4Sync(Command): fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] - if not path.startswith(self.depotPath): - # if not self.silent: - # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.depotPath, change) + + found = [p for p in self.depotPaths + if path.startswith (p)] + if not found: fnum = fnum + 1 continue @@ -620,20 +624,24 @@ class P4Sync(Command): fnum = fnum + 1 return files - def stripRepoPath(self, path, prefix): + def stripRepoPath(self, path, prefixes): if self.keepRepoPath: - prefix = re.sub("^(//[^/]+/).*", r'\1', prefix) + prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])] + + for p in prefixes: + if path.startswith(p): + path = path[len(p):] - return path[len(prefix):] + return path def splitFilesIntoBranches(self, commit): branches = {} fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] - if not path.startswith(self.depotPath): - # if not self.silent: - # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.depotPath, change) + found = [p for p in self.depotPaths + if path.startswith (p)] + if not found: fnum = fnum + 1 continue @@ -644,7 +652,7 @@ class P4Sync(Command): file["type"] = commit["type%s" % fnum] fnum = fnum + 1 - relPath = self.stripRepoPath(path, self.depotPath) + relPath = self.stripRepoPath(path, self.depotPaths) for branch in self.knownBranches.keys(): @@ -656,7 +664,7 @@ class P4Sync(Command): return branches - def commit(self, details, files, branch, branchPrefix, parent = ""): + def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] author = details["user"] @@ -678,7 +686,8 @@ class P4Sync(Command): self.gitStream.write("data < 0: @@ -688,12 +697,13 @@ class P4Sync(Command): for file in files: path = file["path"] - if not path.startswith(branchPrefix): - # print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) + + + if not [p for p in branchPrefixes if path.startswith(p)]: continue rev = file["rev"] depotPath = path + "#" + rev - relPath = self.stripRepoPath(path, branchPrefix) + relPath = self.stripRepoPath(path, branchPrefixes) action = file["action"] if file["type"] == "apple": @@ -728,7 +738,8 @@ class P4Sync(Command): if self.verbose: print "Change %s is labelled %s" % (change, labelDetails) - files = p4CmdList("files %s...@%s" % (branchPrefix, change)) + files = p4CmdList("files " + ' '.join (["%s...@%s" % (p, change) + for p in branchPrefixes])) if len(files) == len(labelRevisions): @@ -795,9 +806,9 @@ class P4Sync(Command): def getLabels(self): self.labels = {} - l = p4CmdList("labels %s..." % self.depotPath) + l = p4CmdList("labels %s..." % ' '.join (self.depotPaths)) if len(l) > 0 and not self.silent: - print "Finding files belonging to labels in %s" % self.depotPath + print "Finding files belonging to labels in %s" % `self.depotPath` for output in l: label = output["label"] @@ -805,7 +816,9 @@ class P4Sync(Command): newestChange = 0 if self.verbose: print "Querying files for label %s" % label - for file in p4CmdList("files %s...@%s" % (self.depotPath, label)): + for file in p4CmdList("files " + + ' '.join (["%s...@%s" % (p, label) + for p in self.depotPaths])): revisions[file["depotFile"]] = file["rev"] change = int(file["change"]) if change > newestChange: @@ -817,6 +830,8 @@ class P4Sync(Command): print "Label changes: %s" % self.labels.keys() def getBranchMapping(self): + + ## FIXME - what's a P4 projectName ? self.projectName = self.depotPath[self.depotPath.strip().rfind("/") + 1:] for info in p4CmdList("branches"): @@ -872,8 +887,8 @@ class P4Sync(Command): remoteHead = self.refPrefix + headName originHead = "origin/" + headName - [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) - if len(originPreviousDepotPath) == 0 or len(originP4Change) == 0: + [originPreviousDepotPaths, originP4Change] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) + if len(originPreviousDepotPaths) == 0 or len(originP4Change) == 0: continue update = False @@ -882,25 +897,26 @@ class P4Sync(Command): print "creating %s" % remoteHead update = True else: - [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead)) + [p4PreviousDepotPaths, p4Change] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead)) if len(p4Change) > 0: - if originPreviousDepotPath == p4PreviousDepotPath: + if originPreviousDepotPaths == p4PreviousDepotPaths: originP4Change = int(originP4Change) p4Change = int(p4Change) if originP4Change > p4Change: print "%s (%s) is newer than %s (%s). Updating p4 branch from origin." % (originHead, originP4Change, remoteHead, p4Change) update = True else: - print "Ignoring: %s was imported from %s while %s was imported from %s" % (originHead, originPreviousDepotPath, remoteHead, p4PreviousDepotPath) + print "Ignoring: %s was imported from %s while %s was imported from %s" % (originHead, originPreviousDepotPaths, remoteHead, p4PreviousDepotPaths) if update: system("git update-ref %s %s" % (remoteHead, originHead)) + def run(self, args): - self.depotPath = "" + self.depotPaths = [] self.changeRange = "" self.initialParent = "" - self.previousDepotPath = "" + self.previousDepotPaths = [] # map from branch depot path to parent branch self.knownBranches = {} @@ -926,7 +942,7 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) - if len(args) == 0: + if args == []: if self.hasOrigin: self.createOrUpdateBranchesFromOrigin() self.listExistingP4GitBranches() @@ -942,26 +958,31 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch) - (depotPath, change) = extractDepotPathAndChangeFromGitLog(logMsg) + (depotPaths, change) = extractDepotPathsAndChangeFromGitLog(logMsg) if self.verbose: - print "path %s change %s" % (depotPath, change) + print "path %s change %s" % (','.join(depotPaths), change) - if len(depotPath) > 0 and len(change) > 0: + if len(depotPaths) > 0 and len(change) > 0: change = int(change) + 1 p4Change = max(p4Change, change) - if len(self.previousDepotPath) == 0: - self.previousDepotPath = depotPath + if len(self.previousDepotPaths) == 0: + self.previousDepotPaths = depotPaths else: - i = 0 - l = min(len(self.previousDepotPath), len(depotPath)) - while i < l and self.previousDepotPath[i] == depotPath[i]: - i = i + 1 - self.previousDepotPath = self.previousDepotPath[:i] + ## FIXME + paths = [] + for (prev, cur) in zip(self.previousDepotPaths, depotPaths): + for i in range(0, max(len(cur), len(prev))): + if cur[i] <> prev[i]: + break + + paths.append (cur[:i]) + + self.previousDepotPaths = paths if p4Change > 0: - self.depotPath = self.previousDepotPath + self.depotPaths = self.previousDepotPaths self.changeRange = "@%s,#head" % p4Change self.initialParent = parseRevision(self.branch) if not self.silent and not self.detectBranches: @@ -970,43 +991,47 @@ class P4Sync(Command): if not self.branch.startswith("refs/"): self.branch = "refs/heads/" + self.branch - if len(self.depotPath) != 0: - self.depotPath = self.depotPath.strip() - - if len(args) == 0 and len(self.depotPath) != 0: + if len(args) == 0 and self.depotPaths: if not self.silent: - print "Depot path: %s" % self.depotPath - elif len(args) != 1: - return False + print "Depot paths: %s" % ' '.join(self.depotPaths) else: - if len(self.depotPath) != 0 and self.depotPath != args[0]: + if self.depotPaths and self.depotPaths != args: print ("previous import used depot path %s and now %s was specified. " - "This doesn't work!" % (self.depotPath, args[0])) + "This doesn't work!" % (' '.join (self.depotPaths), + ' '.join (args))) sys.exit(1) - self.depotPath = args[0] + + self.depotPaths = args self.revision = "" self.users = {} - if self.depotPath.find("@") != -1: - atIdx = self.depotPath.index("@") - self.changeRange = self.depotPath[atIdx:] - if self.changeRange == "@all": - self.changeRange = "" - elif self.changeRange.find(",") == -1: - self.revision = self.changeRange - self.changeRange = "" - self.depotPath = self.depotPath[0:atIdx] - elif self.depotPath.find("#") != -1: - hashIdx = self.depotPath.index("#") - self.revision = self.depotPath[hashIdx:] - self.depotPath = self.depotPath[0:hashIdx] - elif len(self.previousDepotPath) == 0: - self.revision = "#head" - - self.depotPath = re.sub ("\.\.\.$", "", self.depotPath) - if not self.depotPath.endswith("/"): - self.depotPath += "/" + newPaths = [] + for p in self.depotPaths: + if p.find("@") != -1: + atIdx = p.index("@") + self.changeRange = p[atIdx:] + if self.changeRange == "@all": + self.changeRange = "" + elif self.changeRange.find(",") == -1: + self.revision = self.changeRange + self.changeRange = "" + p = p[0:atIdx] + elif p.find("#") != -1: + hashIdx = p.index("#") + self.revision = p[hashIdx:] + p = p[0:hashIdx] + elif self.previousDepotPaths == []: + self.revision = "#head" + + p = re.sub ("\.\.\.$", "", p) + if not p.endswith("/"): + p += "/" + + newPaths.append(p) + + self.depotPaths = newPaths + self.loadUserMapFromCache() self.labels = {} @@ -1020,28 +1045,34 @@ class P4Sync(Command): print "initial parents: %s" % self.initialParents for b in self.p4BranchesInGit: if b != "master": + + ## FIXME b = b[len(self.projectName):] self.createdBranches.add(b) self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) importProcess = subprocess.Popen(["git", "fast-import"], - stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE); self.gitOutput = importProcess.stdout self.gitStream = importProcess.stdin self.gitError = importProcess.stderr if len(self.revision) > 0: - print "Doing initial import of %s from revision %s" % (self.depotPath, self.revision) + print "Doing initial import of %s from revision %s" % (' '.join(self.depotPaths), self.revision) details = { "user" : "git perforce import user", "time" : int(time.time()) } details["desc"] = ("Initial import of %s from the state at revision %s" - % (self.depotPath, self.revision)) + % (' '.join(self.depotPaths), self.revision)) details["change"] = self.revision newestRevision = 0 fileCnt = 0 - for info in p4CmdList("files %s...%s" % (self.depotPath, self.revision)): + for info in p4CmdList("files " + + ' '.join(["%s...%s" + % (p, self.revision) + for p in self.depotPaths])): change = int(info["change"]) if change > newestRevision: newestRevision = change @@ -1059,7 +1090,7 @@ class P4Sync(Command): details["change"] = newestRevision try: - self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPath) + self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths) except IOError: print "IO error with git fast-import. Is your git version recent enough?" print self.gitError.read() @@ -1079,8 +1110,11 @@ class P4Sync(Command): changes.sort() else: if self.verbose: - print "Getting p4 changes for %s...%s" % (self.depotPath, self.changeRange) - output = read_pipe_lines("p4 changes %s...%s" % (self.depotPath, self.changeRange)) + print "Getting p4 changes for %s...%s" % (`self.depotPaths`, + self.changeRange) + assert self.depotPaths + output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange) + for p in self.depotPaths])) for line in output: changeNum = line.split(" ")[1] @@ -1111,7 +1145,8 @@ class P4Sync(Command): if self.detectBranches: branches = self.splitFilesIntoBranches(description) for branch in branches.keys(): - branchPrefix = self.depotPath + branch + "/" + ## HACK --hwn + branchPrefix = self.depotPaths[0] + branch + "/" parent = "" @@ -1134,11 +1169,14 @@ class P4Sync(Command): if branch == "main": branch = "master" else: + + ## FIXME branch = self.projectName + branch if parent == "main": parent = "master" elif len(parent) > 0: + ## FIXME parent = self.projectName + parent branch = self.refPrefix + branch @@ -1155,7 +1193,8 @@ class P4Sync(Command): self.commit(description, filesForCommit, branch, branchPrefix, parent) else: files = self.extractFilesFromCommit(description) - self.commit(description, files, self.branch, self.depotPath, self.initialParent) + self.commit(description, files, self.branch, self.depotPaths, + self.initialParent) self.initialParent = "" except IOError: print self.gitError.read() @@ -1206,30 +1245,35 @@ class P4Clone(P4Sync): if len(args) < 1: return False - depotPath = args[0] destination = "" - if len(args) == 2: + if self.keepRepoPath: + destination = args[-1] + args = args[:-1] + elif len(args) == 2: destination = args[1] elif len(args) > 2: return False - if not depotPath.startswith("//"): - return False - - depotDir = re.sub("(@[^@]*)$", "", depotPath) - depotDir = re.sub("(#[^#]*)$", "", depotDir) - depotDir = re.sub(r"\.\.\.$,", "", depotDir) - depotDir = re.sub(r"/$", "", depotDir) + depotPaths = args + for p in depotPaths: + if not p.startswith("//"): + return False if not destination: + depotPath = args[0] + depotDir = re.sub("(@[^@]*)$", "", depotPath) + depotDir = re.sub("(#[^#]*)$", "", depotDir) + depotDir = re.sub(r"\.\.\.$,", "", depotDir) + depotDir = re.sub(r"/$", "", depotDir) + destination = os.path.split(depotDir)[1] - print "Importing from %s into %s" % (depotPath, destination) + print "Importing from %s into %s" % (`depotPaths`, destination) os.makedirs(destination) os.chdir(destination) system("git init") gitdir = os.getcwd() + "/.git" - if not P4Sync.run(self, [depotPath]): + if not P4Sync.run(self, depotPaths): return False if self.branch != "master": if gitBranchExists("refs/remotes/p4/master"): -- cgit v1.2.3 From 9226c03c3279d66e2dea21a7a3e9f187e5ee9b9a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 28 May 2007 19:23:19 +0200 Subject: In *_pipe print the command that failed if it fails. Fixed old calls to mypopen. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index efa2fce29e..0f1285b39b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -24,7 +24,7 @@ def write_pipe(c, str): pipe = os.popen(c, 'w') val = pipe.write(str) if pipe.close(): - sys.stderr.write('Command failed') + sys.stderr.write('Command failed: %s' % c) sys.exit(1) return val @@ -36,7 +36,7 @@ def read_pipe(c): pipe = os.popen(c, 'rb') val = pipe.read() if pipe.close(): - sys.stderr.write('Command failed') + sys.stderr.write('Command failed: %s' % c) sys.exit(1) return val @@ -49,7 +49,7 @@ def read_pipe_lines(c): pipe = os.popen(c, 'rb') val = pipe.readlines() if pipe.close(): - sys.stderr.write('Command failed') + sys.stderr.write('Command failed: %s' % c) sys.exit(1) return val @@ -157,7 +157,7 @@ def gitBranchExists(branch): return proc.wait() == 0; def gitConfig(key): - return mypopen("git config %s" % key).read()[:-1] + return os.popen("git config %s" % key, "rb").read()[:-1] class Command: def __init__(self): @@ -861,7 +861,7 @@ class P4Sync(Command): if not self.silent: print "Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix - for line in mypopen("git rev-parse --symbolic --remotes"): + for line in read_pipe_lines("git rev-parse --symbolic --remotes"): if (not line.startswith("origin/")) or line.endswith("HEAD\n"): continue -- cgit v1.2.3 From cfeb59be256bf9cd2853ed04d4af056b2f0eff31 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 28 May 2007 19:24:57 +0200 Subject: Fix typo in listExistingP4Branches that broke sync. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0f1285b39b..794286ee8e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -846,8 +846,8 @@ class P4Sync(Command): cmdline += " --branches" for line in read_pipe_lines(cmdline): - lie = line.strip() - if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"): + line = line.strip() + if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD"): continue if self.importIntoRemotes: -- cgit v1.2.3 From 2e4aef58932cdb345893103d77823a9dd6a146a6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 26 May 2007 23:09:27 -0400 Subject: Allow contrib new-workdir to link into bare repositories On one particular system I like to keep a cluster of bare Git repositories and spawn new-workdirs off of them. Since the bare repositories don't have working directories associated with them they don't have a .git/ subdirectory that hosts the repository we are linking to. Using a bare repository as the backing repository for a workdir created by this script does require that the user delete core.bare from the repository's configuration file, so that Git auto-senses the bareness of a repository based on pathname information, and not based on the config file. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 9877b98508..f2a3615bbc 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -20,17 +20,19 @@ new_workdir=$2 branch=$3 # want to make sure that what is pointed to has a .git directory ... -test -d "$orig_git/.git" || die "\"$orig_git\" is not a git repository!" +git_dir=$(cd "$orig_git" 2>/dev/null && + git rev-parse --git-dir 2>/dev/null) || + die "\"$orig_git\" is not a git repository!" # don't link to a workdir -if test -L "$orig_git/.git/config" +if test -L "$git_dir/config" then die "\"$orig_git\" is a working directory only, please specify" \ "a complete repository." fi # make sure the the links use full paths -orig_git=$(cd "$orig_git"; pwd) +git_dir=$(cd "$git_dir"; pwd) # create the workdir mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!" @@ -45,13 +47,13 @@ do mkdir -p "$(dirname "$new_workdir/.git/$x")" ;; esac - ln -s "$orig_git/.git/$x" "$new_workdir/.git/$x" + ln -s "$git_dir/$x" "$new_workdir/.git/$x" done # now setup the workdir cd "$new_workdir" # copy the HEAD from the original repository as a default branch -cp "$orig_git/.git/HEAD" .git/HEAD +cp "$git_dir/HEAD" .git/HEAD # checkout the branch (either the same as HEAD from the original repository, or # the one that was asked for) git checkout -f $branch -- cgit v1.2.3 From bb6e09b27afeaae780dabfda7a07d59fc7efc4cf Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: Diverse cleanups - print commands with \n - extractDepotPathsAndChangeFromGitLog -> extractSettings, returning dict. - store keepRepoPath in [git-p4: ] line - create a main() function, so git-p4 can be pychecked - use --destination for clone destination. This simplifies logic for --keep-path Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 224 +++++++++++++++++++++++++++------------------ 1 file changed, 134 insertions(+), 90 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ad145e8595..b280e97742 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -56,7 +56,7 @@ def read_pipe_lines(c): def system(cmd): if verbose: - sys.stderr.write("executing %s" % cmd) + sys.stderr.write("executing %s\n" % cmd) if os.system(cmd) != 0: die("command failed: %s" % cmd) @@ -112,7 +112,8 @@ def currentGitBranch(): return read_pipe("git name-rev HEAD").split(" ")[1].strip() def isValidGitDir(path): - if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): + if (os.path.exists(path + "/HEAD") + and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")): return True; return False @@ -133,7 +134,7 @@ def extractLogMessageFromGitCommit(commit): logMessage += log return logMessage -def extractDepotPathsAndChangeFromGitLog(log): +def extractSettingsGitLog(log): values = {} for line in log.split("\n"): line = line.strip() @@ -151,11 +152,12 @@ def extractDepotPathsAndChangeFromGitLog(log): values[key] = val - paths = values.get("depot-path").split(',') - return paths, values.get("change") + values['depot-paths'] = values.get("depot-paths").split(',') + return values def gitBranchExists(branch): - proc = subprocess.Popen(["git", "rev-parse", branch], stderr=subprocess.PIPE, stdout=subprocess.PIPE); + proc = subprocess.Popen(["git", "rev-parse", branch], + stderr=subprocess.PIPE, stdout=subprocess.PIPE); return proc.wait() == 0; def gitConfig(key): @@ -211,7 +213,11 @@ class P4RollBack(Command): line = line.strip() ref = refPrefix + line log = extractLogMessageFromGitCommit(ref) - depotPaths, change = extractDepotPathsAndChangeFromGitLog(log) + settings = extractSettingsGitLog(log) + + depotPaths = settings['depot-paths'] + change = settings['change'] + changed = False if len(p4Cmd("changes -m 1 " + ' '.join (['%s...@%s' % (p, maxChange) @@ -220,13 +226,17 @@ class P4RollBack(Command): system("git update-ref -d %s `git rev-parse %s`" % (ref, ref)) continue - while len(change) > 0 and int(change) > maxChange: + while change and int(change) > maxChange: changed = True if self.verbose: print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange) system("git update-ref %s \"%s^\"" % (ref, ref)) log = extractLogMessageFromGitCommit(ref) - depotPaths, change = extractDepotPathsAndChangeFromGitLog(log) + settings = extractSettingsGitLog(log) + + + depotPaths = settings['depot-paths'] + change = settings['change'] if changed: print "%s rewound to %s" % (ref, change) @@ -474,10 +484,12 @@ class P4Submit(Command): return False depotPath = "" + settings = None if gitBranchExists("p4"): - [depotPaths, dummy] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) + settings = extractSettingsGitLog(extractLogMessageFromGitCommit("p4")) if len(depotPath) == 0 and gitBranchExists("origin"): - [depotPaths, dummy] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) + settings = extractSettingsGitLog(extractLogMessageFromGitCommit("origin")) + depotPaths = settings['depot-paths'] if len(depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" @@ -686,8 +698,11 @@ class P4Sync(Command): self.gitStream.write("data < 0: @@ -883,12 +898,13 @@ class P4Sync(Command): if (not line.startswith("origin/")) or line.endswith("HEAD\n"): continue - headName = line[len("origin/")] + headName = line[len("origin/"):] remoteHead = self.refPrefix + headName originHead = "origin/" + headName - [originPreviousDepotPaths, originP4Change] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) - if len(originPreviousDepotPaths) == 0 or len(originP4Change) == 0: + original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead)) + if (not original.has_key('depot-paths') + or not original.has_key('change')): continue update = False @@ -897,20 +913,36 @@ class P4Sync(Command): print "creating %s" % remoteHead update = True else: - [p4PreviousDepotPaths, p4Change] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead)) - if len(p4Change) > 0: - if originPreviousDepotPaths == p4PreviousDepotPaths: - originP4Change = int(originP4Change) - p4Change = int(p4Change) + settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead)) + if settings.has_key('change') > 0: + if settings['depot-paths'] == original['depot-paths']: + originP4Change = int(original['change']) + p4Change = int(settings['change']) if originP4Change > p4Change: - print "%s (%s) is newer than %s (%s). Updating p4 branch from origin." % (originHead, originP4Change, remoteHead, p4Change) + print ("%s (%s) is newer than %s (%s). " + "Updating p4 branch from origin." + % (originHead, originP4Change, + remoteHead, p4Change)) update = True else: - print "Ignoring: %s was imported from %s while %s was imported from %s" % (originHead, originPreviousDepotPaths, remoteHead, p4PreviousDepotPaths) + print ("Ignoring: %s was imported from %s while " + "%s was imported from %s" + % (originHead, ','.join(original['depot-paths']), + remoteHead, ','.join(settings['depot-paths']))) if update: system("git update-ref %s %s" % (remoteHead, originHead)) + def updateOptionDict(self, d): + option_keys = {} + if self.keepRepoPath: + option_keys['keepRepoPath'] = 1 + + d["options"] = ' '.join(sorted(option_keys.keys())) + + def readOptions(self, d): + self.keepRepoPath = (d.has_key('options') + and ('keepRepoPath' in d['options'])) def run(self, args): self.depotPaths = [] @@ -942,7 +974,8 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) - if args == []: + ### FIXME + if 1: if self.hasOrigin: self.createOrUpdateBranchesFromOrigin() self.listExistingP4GitBranches() @@ -958,19 +991,22 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch) - (depotPaths, change) = extractDepotPathsAndChangeFromGitLog(logMsg) + + settings = extractSettingsGitLog(logMsg) if self.verbose: print "path %s change %s" % (','.join(depotPaths), change) - if len(depotPaths) > 0 and len(change) > 0: - change = int(change) + 1 + self.readOptions(settings) + if (settings.has_key('depot-paths') + and settings.has_key ('change')): + change = int(settings['change']) + 1 p4Change = max(p4Change, change) - if len(self.previousDepotPaths) == 0: + depotPaths = sorted(settings['depot-paths']) + if self.previousDepotPaths == []: self.previousDepotPaths = depotPaths else: - ## FIXME paths = [] for (prev, cur) in zip(self.previousDepotPaths, depotPaths): for i in range(0, max(len(cur), len(prev))): @@ -982,7 +1018,7 @@ class P4Sync(Command): self.previousDepotPaths = paths if p4Change > 0: - self.depotPaths = self.previousDepotPaths + self.depotPaths = sorted(self.previousDepotPaths) self.changeRange = "@%s,#head" % p4Change self.initialParent = parseRevision(self.branch) if not self.silent and not self.detectBranches: @@ -1001,7 +1037,7 @@ class P4Sync(Command): ' '.join (args))) sys.exit(1) - self.depotPaths = args + self.depotPaths = sorted(args) self.revision = "" self.users = {} @@ -1088,7 +1124,7 @@ class P4Sync(Command): fileCnt = fileCnt + 1 details["change"] = newestRevision - + self.updateOptionDict(details) try: self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths) except IOError: @@ -1135,6 +1171,7 @@ class P4Sync(Command): cnt = 1 for change in changes: description = p4Cmd("describe %s" % change) + self.updateOptionDict(description) if not self.silent: sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) @@ -1237,7 +1274,12 @@ class P4Clone(P4Sync): def __init__(self): P4Sync.__init__(self) self.description = "Creates a new git repository and imports from Perforce into it" - self.usage = "usage: %prog [options] //depot/path[@revRange] [directory]" + self.usage = "usage: %prog [options] //depot/path[@revRange]" + self.options.append( + optparse.make_option("--destination", dest="cloneDestination", + action='store', default=None, + help="where to leave result of the clone")) + self.cloneDestination = None self.needsGit = False def run(self, args): @@ -1245,32 +1287,28 @@ class P4Clone(P4Sync): if len(args) < 1: return False - destination = "" - if self.keepRepoPath: - destination = args[-1] - args = args[:-1] - elif len(args) == 2: - destination = args[1] - elif len(args) > 2: - return False + + if self.keepRepoPath and not self.cloneDestination: + sys.stderr.write("Must specify destination for --keep-path\n") + sys.exit(1) depotPaths = args for p in depotPaths: if not p.startswith("//"): return False - if not destination: + if not self.cloneDestination: depotPath = args[0] depotDir = re.sub("(@[^@]*)$", "", depotPath) depotDir = re.sub("(#[^#]*)$", "", depotDir) depotDir = re.sub(r"\.\.\.$,", "", depotDir) depotDir = re.sub(r"/$", "", depotDir) - destination = os.path.split(depotDir)[1] + self.cloneDestination = os.path.split(depotDir)[1] - print "Importing from %s into %s" % (`depotPaths`, destination) - os.makedirs(destination) - os.chdir(destination) + print "Importing from %s into %s" % (`depotPaths`, self.cloneDestination) + os.makedirs(self.cloneDestination) + os.chdir(self.cloneDestination) system("git init") gitdir = os.getcwd() + "/.git" if not P4Sync.run(self, depotPaths): @@ -1310,54 +1348,60 @@ commands = { "rollback" : P4RollBack() } -if len(sys.argv[1:]) == 0: - printUsage(commands.keys()) - sys.exit(2) - -cmd = "" -cmdName = sys.argv[1] -try: - cmd = commands[cmdName] -except KeyError: - print "unknown command %s" % cmdName - print "" - printUsage(commands.keys()) - sys.exit(2) -options = cmd.options -cmd.gitdir = gitdir +def main(): + if len(sys.argv[1:]) == 0: + printUsage(commands.keys()) + sys.exit(2) -args = sys.argv[2:] - -if len(options) > 0: - options.append(optparse.make_option("--git-dir", dest="gitdir")) + cmd = "" + cmdName = sys.argv[1] + try: + cmd = commands[cmdName] + except KeyError: + print "unknown command %s" % cmdName + print "" + printUsage(commands.keys()) + sys.exit(2) + + options = cmd.options + cmd.gitdir = gitdir + + args = sys.argv[2:] + + if len(options) > 0: + options.append(optparse.make_option("--git-dir", dest="gitdir")) + + parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), + options, + description = cmd.description, + formatter = HelpFormatter()) + + (cmd, args) = parser.parse_args(sys.argv[2:], cmd); + global verbose + verbose = cmd.verbose + if cmd.needsGit: + gitdir = cmd.gitdir + if len(gitdir) == 0: + gitdir = ".git" + if not isValidGitDir(gitdir): + gitdir = read_pipe("git rev-parse --git-dir").strip() + if os.path.exists(gitdir): + cdup = read_pipe("git rev-parse --show-cdup").strip() + if len(cdup) > 0: + os.chdir(cdup); - parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), - options, - description = cmd.description, - formatter = HelpFormatter()) + if not isValidGitDir(gitdir): + if isValidGitDir(gitdir + "/.git"): + gitdir += "/.git" + else: + die("fatal: cannot locate git repository at %s" % gitdir) - (cmd, args) = parser.parse_args(sys.argv[2:], cmd); + os.environ["GIT_DIR"] = gitdir -verbose = cmd.verbose -if cmd.needsGit: - gitdir = cmd.gitdir - if len(gitdir) == 0: - gitdir = ".git" - if not isValidGitDir(gitdir): - gitdir = read_pipe("git rev-parse --git-dir").strip() - if os.path.exists(gitdir): - cdup = read_pipe("git rev-parse --show-cdup").strip() - if len(cdup) > 0: - os.chdir(cdup); - - if not isValidGitDir(gitdir): - if isValidGitDir(gitdir + "/.git"): - gitdir += "/.git" - else: - die("fatal: cannot locate git repository at %s" % gitdir) + if not cmd.run(args): + parser.print_help() - os.environ["GIT_DIR"] = gitdir -if not cmd.run(args): - parser.print_help() +if __name__ == '__main__': + main() -- cgit v1.2.3 From b86f73782eafd1c61bb706ec5ca3d1ec548d82f5 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: remove global .gitdir Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 53 +++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 28ea53dea4..6501387657 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -14,7 +14,6 @@ import re from sets import Set; -gitdir = os.environ.get("GIT_DIR", "") verbose = False def write_pipe(c, str): @@ -469,9 +468,7 @@ class P4Submit(Command): % (fileName, fileName)) def run(self, args): - global gitdir # make gitdir absolute so we can cd out into the perforce checkout - gitdir = os.path.abspath(gitdir) os.environ["GIT_DIR"] = gitdir if len(args) == 0: @@ -510,7 +507,7 @@ class P4Submit(Command): print "No changes in working directory to submit." return True patch = read_pipe("git diff -p --binary --diff-filter=ACMRTUXB HEAD") - self.diffFile = gitdir + "/p4-git-diff" + self.diffFile = self.gitdir + "/p4-git-diff" f = open(self.diffFile, "wb") f.write(patch) f.close(); @@ -535,7 +532,7 @@ class P4Submit(Command): self.logSubstitutions[tokens[0]] = tokens[1] self.check() - self.configFile = gitdir + "/p4-git-sync.cfg" + self.configFile = self.gitdir + "/p4-git-sync.cfg" self.config = shelve.open(self.configFile, writeback=True) if self.firstTime: @@ -799,7 +796,7 @@ class P4Sync(Command): continue self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" - cache = open(gitdir + "/p4-usercache.txt", "wb") + cache = open(self.gitdir + "/p4-usercache.txt", "wb") for user in self.users.keys(): cache.write("%s\t%s\n" % (user, self.users[user])) cache.close(); @@ -809,7 +806,7 @@ class P4Sync(Command): self.users = {} self.userMapFromPerforceServer = False try: - cache = open(gitdir + "/p4-usercache.txt", "rb") + cache = open(self.gitdir + "/p4-usercache.txt", "rb") lines = cache.readlines() cache.close() for line in lines: @@ -1283,8 +1280,6 @@ class P4Clone(P4Sync): self.needsGit = False def run(self, args): - global gitdir - if len(args) < 1: return False @@ -1310,7 +1305,7 @@ class P4Clone(P4Sync): os.makedirs(self.cloneDestination) os.chdir(self.cloneDestination) system("git init") - gitdir = os.getcwd() + "/.git" + self.gitdir = os.getcwd() + "/.git" if not P4Sync.run(self, depotPaths): return False if self.branch != "master": @@ -1340,12 +1335,12 @@ def printUsage(commands): print "" commands = { - "debug" : P4Debug(), - "submit" : P4Submit(), - "sync" : P4Sync(), - "rebase" : P4Rebase(), - "clone" : P4Clone(), - "rollback" : P4RollBack() + "debug" : P4Debug, + "submit" : P4Submit, + "sync" : P4Sync, + "rebase" : P4Rebase, + "clone" : P4Clone, + "rollback" : P4RollBack } @@ -1357,7 +1352,8 @@ def main(): cmd = "" cmdName = sys.argv[1] try: - cmd = commands[cmdName] + klass = commands[cmdName] + cmd = klass() except KeyError: print "unknown command %s" % cmdName print "" @@ -1365,7 +1361,7 @@ def main(): sys.exit(2) options = cmd.options - cmd.gitdir = gitdir + cmd.gitdir = os.environ.get("GIT_DIR", None) args = sys.argv[2:] @@ -1381,23 +1377,22 @@ def main(): global verbose verbose = cmd.verbose if cmd.needsGit: - gitdir = cmd.gitdir - if len(gitdir) == 0: - gitdir = ".git" - if not isValidGitDir(gitdir): - gitdir = read_pipe("git rev-parse --git-dir").strip() - if os.path.exists(gitdir): + if cmd.gitdir == None: + cmd.gitdir = os.path.abspath(".git") + if not isValidGitDir(cmd.gitdir): + cmd.gitdir = read_pipe("git rev-parse --git-dir").strip() + if os.path.exists(cmd.gitdir): cdup = read_pipe("git rev-parse --show-cdup").strip() if len(cdup) > 0: os.chdir(cdup); - if not isValidGitDir(gitdir): - if isValidGitDir(gitdir + "/.git"): - gitdir += "/.git" + if not isValidGitDir(cmd.gitdir): + if isValidGitDir(cmd.gitdir + "/.git"): + cmd.gitdir += "/.git" else: - die("fatal: cannot locate git repository at %s" % gitdir) + die("fatal: cannot locate git repository at %s" % cmd.gitdir) - os.environ["GIT_DIR"] = gitdir + os.environ["GIT_DIR"] = cmd.gitdir if not cmd.run(args): parser.print_help() -- cgit v1.2.3 From 6a49f8e2e04317175060576d85a5d2062ebb43a4 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: Read p4 files in one batch. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 89 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 26 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6501387657..8a3c53eb8c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -61,6 +61,8 @@ def system(cmd): def p4CmdList(cmd): cmd = "p4 -G %s" % cmd + if verbose: + sys.stderr.write("Opening pipe: %s\n" % cmd) pipe = os.popen(cmd, "rb") result = [] @@ -609,9 +611,6 @@ class P4Sync(Command): if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False - def p4File(self, depotPath): - return read_pipe("p4 print -q \"%s\"" % depotPath) - def extractFilesFromCommit(self, commit): files = [] fnum = 0 @@ -673,6 +672,39 @@ class P4Sync(Command): return branches + ## Should move this out, doesn't use SELF. + def readP4Files(self, files): + specs = [(f['path'] + "#" + f['rev'], f) for f in files] + + data = read_pipe('p4 print %s' % ' '.join(['"%s"' % spec + for (spec, info) in specs])) + + idx = 0 + for j in range(0, len(specs)): + filespec, info = specs[j] + + assert idx < len(data) + if data[idx:idx + len(filespec)] != filespec: + assert False + idx = data.find ('\n', idx) + assert idx > 0 + idx += 1 + + start = idx + + end = -1 + if j < len(specs)-1: + next_spec, next_info = specs[j+1] + end = data.find(next_spec, start) + + assert end >= 0 + else: + end = len(specs) + + + info['data'] = data[start:end] + idx = end + def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] author = details["user"] @@ -681,7 +713,7 @@ class P4Sync(Command): print "commit into %s" % branch self.gitStream.write("commit %s\n" % branch) - # gitStream.write("mark :%s\n" % details["change"]) +# gitStream.write("mark :%s\n" % details["change"]) self.committedChanges.add(int(details["change"])) committer = "" if author not in self.users: @@ -707,29 +739,30 @@ class P4Sync(Command): print "parent %s" % parent self.gitStream.write("from %s\n" % parent) - for file in files: - path = file["path"] + new_files = [] + for f in files: + if [p for p in branchPrefixes if f['path'].startswith(p)]: + new_files.append (f) + else: + sys.stderr.write("Ignoring file outside of prefix: %s\n" % path) + files = new_files - if not [p for p in branchPrefixes if path.startswith(p)]: - continue - rev = file["rev"] - depotPath = path + "#" + rev - relPath = self.stripRepoPath(path, branchPrefixes) - action = file["action"] - + self.readP4Files(files) + for file in files: if file["type"] == "apple": - print "\nfile %s is a strange apple file that forks. Ignoring!" % path + print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path'] continue - if action == "delete": + relPath = self.stripRepoPath(file['path'], branchPrefixes) + if file["action"] == "delete": self.gitStream.write("D %s\n" % relPath) else: mode = 644 if file["type"].startswith("x"): mode = 755 - data = self.p4File(depotPath) + data = file['data'] if self.isWindows and file["type"].endswith("text"): data = data.replace("\r\n", "\n") @@ -971,8 +1004,9 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) - ### FIXME - if 1: + # TODO: should always look at previous commits, + # merge with previous imports, if possible. + if args == []: if self.hasOrigin: self.createOrUpdateBranchesFromOrigin() self.listExistingP4GitBranches() @@ -1046,7 +1080,7 @@ class P4Sync(Command): self.changeRange = p[atIdx:] if self.changeRange == "@all": self.changeRange = "" - elif self.changeRange.find(",") == -1: + elif ',' not in self.changeRange: self.revision = self.changeRange self.changeRange = "" p = p[0:atIdx] @@ -1279,6 +1313,15 @@ class P4Clone(P4Sync): self.cloneDestination = None self.needsGit = False + def defaultDestination(self, args): + ## TODO: use common prefix of args? + depotPath = args[0] + depotDir = re.sub("(@[^@]*)$", "", depotPath) + depotDir = re.sub("(#[^#]*)$", "", depotDir) + depotDir = re.sub(r"\.\.\.$,", "", depotDir) + depotDir = re.sub(r"/$", "", depotDir) + return os.path.split(depotDir)[1] + def run(self, args): if len(args) < 1: return False @@ -1293,13 +1336,7 @@ class P4Clone(P4Sync): return False if not self.cloneDestination: - depotPath = args[0] - depotDir = re.sub("(@[^@]*)$", "", depotPath) - depotDir = re.sub("(#[^#]*)$", "", depotDir) - depotDir = re.sub(r"\.\.\.$,", "", depotDir) - depotDir = re.sub(r"/$", "", depotDir) - - self.cloneDestination = os.path.split(depotDir)[1] + self.cloneDestination = self.defaultDestination() print "Importing from %s into %s" % (`depotPaths`, self.cloneDestination) os.makedirs(self.cloneDestination) -- cgit v1.2.3 From 9320da8dd492c13f1d32b7fde8c9e60bdbd10217 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: Thinko, fix buglet. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 8a3c53eb8c..f1f562fae4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -699,11 +699,11 @@ class P4Sync(Command): assert end >= 0 else: - end = len(specs) - + end = len(data) info['data'] = data[start:end] idx = end + assert idx == len(data) def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] -- cgit v1.2.3 From 183b8ef89be041cd50803427cfc46fe1afed17bb Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: store p4 user cache in home directory. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f1f562fae4..bd1afb2964 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -819,6 +819,9 @@ class P4Sync(Command): print ("Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change)) + def getUserCacheFilename(self): + return os.environ["HOME"] + "/.gitp4-usercache.txt") + def getUserMapFromPerforceServer(self): if self.userMapFromPerforceServer: return @@ -829,17 +832,19 @@ class P4Sync(Command): continue self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" - cache = open(self.gitdir + "/p4-usercache.txt", "wb") - for user in self.users.keys(): - cache.write("%s\t%s\n" % (user, self.users[user])) - cache.close(); + + s = '' + for (key, val) in self.users.items(): + s += "%s\t%s\n" % (key, val) + + open(self.getUserCacheFilename(), "wb").write(s) self.userMapFromPerforceServer = True def loadUserMapFromCache(self): self.users = {} self.userMapFromPerforceServer = False try: - cache = open(self.gitdir + "/p4-usercache.txt", "rb") + cache = open(self.getUserCacheFilename(), "rb") lines = cache.readlines() cache.close() for line in lines: -- cgit v1.2.3 From a3287be5bc40c1aa036eb5422db5a6a087d1736e Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: thinko. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bd1afb2964..01efd92809 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -820,7 +820,7 @@ class P4Sync(Command): % (labelDetails["label"], change)) def getUserCacheFilename(self): - return os.environ["HOME"] + "/.gitp4-usercache.txt") + return os.environ["HOME"] + "/.gitp4-usercache.txt" def getUserMapFromPerforceServer(self): if self.userMapFromPerforceServer: -- cgit v1.2.3 From 96e07dd23c570fe5e65e619c0f1e3c87be2a8352 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: read files before creating the commit. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 01efd92809..d1989e6134 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -697,6 +697,9 @@ class P4Sync(Command): next_spec, next_info = specs[j+1] end = data.find(next_spec, start) + if end < 0: + print spec, next_spec + assert end >= 0 else: end = len(data) @@ -712,6 +715,20 @@ class P4Sync(Command): if self.verbose: print "commit into %s" % branch + # start with reading files; if that fails, we should not + # create a commit. + new_files = [] + for f in files: + if [p for p in branchPrefixes if f['path'].startswith(p)]: + new_files.append (f) + else: + sys.stderr.write("Ignoring file outside of prefix: %s\n" % path) + files = new_files + self.readP4Files(files) + + + + self.gitStream.write("commit %s\n" % branch) # gitStream.write("mark :%s\n" % details["change"]) self.committedChanges.add(int(details["change"])) @@ -739,16 +756,6 @@ class P4Sync(Command): print "parent %s" % parent self.gitStream.write("from %s\n" % parent) - - new_files = [] - for f in files: - if [p for p in branchPrefixes if f['path'].startswith(p)]: - new_files.append (f) - else: - sys.stderr.write("Ignoring file outside of prefix: %s\n" % path) - files = new_files - - self.readP4Files(files) for file in files: if file["type"] == "apple": print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path'] @@ -1030,9 +1037,6 @@ class P4Sync(Command): settings = extractSettingsGitLog(logMsg) - if self.verbose: - print "path %s change %s" % (','.join(depotPaths), change) - self.readOptions(settings) if (settings.has_key('depot-paths') and settings.has_key ('change')): @@ -1145,6 +1149,9 @@ class P4Sync(Command): + ' '.join(["%s...%s" % (p, self.revision) for p in self.depotPaths])): + + if not info.has_key("change"): + print info change = int(info["change"]) if change > newestRevision: newestRevision = change @@ -1154,7 +1161,7 @@ class P4Sync(Command): #fileCnt = fileCnt + 1 continue - for prop in [ "depotFile", "rev", "action", "type" ]: + for prop in ["depotFile", "rev", "action", "type" ]: details["%s%s" % (prop, fileCnt)] = info[prop] fileCnt = fileCnt + 1 -- cgit v1.2.3 From 982bb8a30376d0024a1794426e6e2291a7a21294 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: don't p4 print deleted files. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d1989e6134..63d7a4c995 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -674,17 +674,18 @@ class P4Sync(Command): ## Should move this out, doesn't use SELF. def readP4Files(self, files): - specs = [(f['path'] + "#" + f['rev'], f) for f in files] + specs = [(f['path'] + "#" + f['rev'], f) for f in files + if f['action'] != 'delete'] - data = read_pipe('p4 print %s' % ' '.join(['"%s"' % spec - for (spec, info) in specs])) + data = read_pipe('p4 print %s' % ' '.join(['"%s"' % path + for (path, info) in specs])) idx = 0 for j in range(0, len(specs)): - filespec, info = specs[j] + (pathrev, info) = specs[j] assert idx < len(data) - if data[idx:idx + len(filespec)] != filespec: + if data[idx:idx + len(pathrev)] != pathrev: assert False idx = data.find ('\n', idx) assert idx > 0 @@ -694,11 +695,15 @@ class P4Sync(Command): end = -1 if j < len(specs)-1: - next_spec, next_info = specs[j+1] - end = data.find(next_spec, start) + (next_pathrev, next_info) = specs[j+1] + end = data.find(next_pathrev, start) if end < 0: - print spec, next_spec + print 'j' + print 'PATHREV', pathrev, specs[j] + print 'nextpathrev', next_pathrev, specs[j+1] + print 'start', start, len(data) + print 'end', end assert end >= 0 else: -- cgit v1.2.3 From f2eda79f6967363f9377ef3b137a35d0c86aca2c Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: only run p4 print if necessary Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 63d7a4c995..76bbe3fdbf 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -677,6 +677,9 @@ class P4Sync(Command): specs = [(f['path'] + "#" + f['rev'], f) for f in files if f['action'] != 'delete'] + if not specs: + return + data = read_pipe('p4 print %s' % ' '.join(['"%s"' % path for (path, info) in specs])) -- cgit v1.2.3 From d2c6dd30eff648aef30d003ef327ab9415f86db0 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: use p4CmdList() to get file contents in Python dicts. This is more robust. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 56 +++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 35 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 76bbe3fdbf..1d799708f6 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -580,7 +580,8 @@ class P4Sync(Command): optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), optparse.make_option("--verbose", dest="verbose", action="store_true"), - optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"), + optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false", + help="Import into refs/heads/ , not refs/remotes"), optparse.make_option("--max-changes", dest="maxChanges"), optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true') ] @@ -680,41 +681,22 @@ class P4Sync(Command): if not specs: return - data = read_pipe('p4 print %s' % ' '.join(['"%s"' % path - for (path, info) in specs])) + filedata = p4CmdList('print %s' % ' '.join(['"%s"' % path + for (path, info) in specs])) - idx = 0 - for j in range(0, len(specs)): - (pathrev, info) = specs[j] + j = 0; + contents = {} + while filedata[j:]: + stat = filedata[j] + text = filedata[j+1] + j += 2 - assert idx < len(data) - if data[idx:idx + len(pathrev)] != pathrev: - assert False - idx = data.find ('\n', idx) - assert idx > 0 - idx += 1 + assert stat['code'] == 'stat' and text['code'] == 'text' + contents[stat['depotFile']] = text['data'] - start = idx - - end = -1 - if j < len(specs)-1: - (next_pathrev, next_info) = specs[j+1] - end = data.find(next_pathrev, start) - - if end < 0: - print 'j' - print 'PATHREV', pathrev, specs[j] - print 'nextpathrev', next_pathrev, specs[j+1] - print 'start', start, len(data) - print 'end', end - - assert end >= 0 - else: - end = len(data) - - info['data'] = data[start:end] - idx = end - assert idx == len(data) + for f in files: + assert not f.has_key('data') + f['data'] = contents[f['path']] def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] @@ -1158,8 +1140,12 @@ class P4Sync(Command): % (p, self.revision) for p in self.depotPaths])): - if not info.has_key("change"): - print info + if info['code'] == 'error': + sys.stderr.write("p4 returned an error: %s\n" + % info['data']) + sys.exit(1) + + change = int(info["change"]) if change > newestRevision: newestRevision = change -- cgit v1.2.3 From 86dff6b6762149d5f3d4b44bb57f58a8399a33ee Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: Cleanups & import into p4/master for local import - import into master/local if --import-local is set - use Die() for exiting - if --verbose is set, raise Exception() - use joined strings iso. `list` for progress printing Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 54 +++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1d799708f6..cc0b7013da 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -16,40 +16,44 @@ from sets import Set; verbose = False +def die(msg): + if verbose: + raise Exception(msg) + else: + sys.stderr.write(msg + "\n") + sys.exit(1) + def write_pipe(c, str): if verbose: - sys.stderr.write('writing pipe: %s\n' % c) + sys.stderr.write('Writing pipe: %s\n' % c) pipe = os.popen(c, 'w') val = pipe.write(str) if pipe.close(): - sys.stderr.write('Command failed: %s' % c) - sys.exit(1) + die('Command failed: %s' % c) return val def read_pipe(c, ignore_error=False): if verbose: - sys.stderr.write('reading pipe: %s\n' % c) + sys.stderr.write('Reading pipe: %s\n' % c) pipe = os.popen(c, 'rb') val = pipe.read() if pipe.close() and not ignore_error: - sys.stderr.write('Command failed: %s\n' % c) - sys.exit(1) + die('Command failed: %s' % c) return val def read_pipe_lines(c): if verbose: - sys.stderr.write('reading pipe: %s\n' % c) + sys.stderr.write('Reading pipe: %s\n' % c) ## todo: check return status pipe = os.popen(c, 'rb') val = pipe.readlines() if pipe.close(): - sys.stderr.write('Command failed: %s\n' % c) - sys.exit(1) + die('Command failed: %s' % c) return val @@ -105,10 +109,6 @@ def p4Where(depotPath): clientPath = clientPath[:-3] return clientPath -def die(msg): - sys.stderr.write(msg + "\n") - sys.exit(1) - def currentGitBranch(): return read_pipe("git name-rev HEAD").split(" ")[1].strip() @@ -583,7 +583,8 @@ class P4Sync(Command): optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false", help="Import into refs/heads/ , not refs/remotes"), optparse.make_option("--max-changes", dest="maxChanges"), - optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true') + optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true', + help="Keep entire BRANCH/DIR/SUBDIR prefix during import") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -876,10 +877,14 @@ class P4Sync(Command): if self.verbose: print "Label changes: %s" % self.labels.keys() + def guessProjectName(self): + for p in self.depotPaths: + return p [p.strip().rfind("/") + 1:] + def getBranchMapping(self): ## FIXME - what's a P4 projectName ? - self.projectName = self.depotPath[self.depotPath.strip().rfind("/") + 1:] + self.projectName = self.guessProjectName() for info in p4CmdList("branches"): details = p4Cmd("branch -o %s" % info["branch"]) @@ -911,9 +916,11 @@ class P4Sync(Command): for line in read_pipe_lines(cmdline): line = line.strip() - if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD"): - continue + ## only import to p4/ + if not line.startswith('p4/'): + continue + branch = line if self.importIntoRemotes: # strip off p4 branch = re.sub ("^p4/", "", line) @@ -923,7 +930,8 @@ class P4Sync(Command): def createOrUpdateBranchesFromOrigin(self): if not self.silent: - print "Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix + print ("Creating/updating branch(es) in %s based on origin branch(es)" + % self.refPrefix) for line in read_pipe_lines("git rev-parse --symbolic --remotes"): line = line.strip() @@ -998,7 +1006,7 @@ class P4Sync(Command): system("git fetch origin") if len(self.branch) == 0: - self.branch = self.refPrefix + "master" + self.branch = self.refPrefix + "p4/master" if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: system("git update-ref %s refs/heads/p4" % self.branch) system("git branch -D p4"); @@ -1023,6 +1031,7 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: + print self.p4BranchesInGit logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch) settings = extractSettingsGitLog(logMsg) @@ -1125,7 +1134,7 @@ class P4Sync(Command): self.gitStream = importProcess.stdin self.gitError = importProcess.stderr - if len(self.revision) > 0: + if self.revision: print "Doing initial import of %s from revision %s" % (' '.join(self.depotPaths), self.revision) details = { "user" : "git perforce import user", "time" : int(time.time()) } @@ -1183,7 +1192,7 @@ class P4Sync(Command): changes.sort() else: if self.verbose: - print "Getting p4 changes for %s...%s" % (`self.depotPaths`, + print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths), self.changeRange) assert self.depotPaths output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange) @@ -1344,7 +1353,7 @@ class P4Clone(P4Sync): if not self.cloneDestination: self.cloneDestination = self.defaultDestination() - print "Importing from %s into %s" % (`depotPaths`, self.cloneDestination) + print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination) os.makedirs(self.cloneDestination) os.chdir(self.cloneDestination) system("git init") @@ -1357,6 +1366,7 @@ class P4Clone(P4Sync): system("git checkout -f") else: print "Could not detect main branch. No checkout/master branch created." + return True class HelpFormatter(optparse.IndentedHelpFormatter): -- cgit v1.2.3 From b17f88b5445210c2223b0642917c43581c767761 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: remove debug print Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index cc0b7013da..5dae1f19ae 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1031,7 +1031,6 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: - print self.p4BranchesInGit logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch) settings = extractSettingsGitLog(logMsg) -- cgit v1.2.3 From b1ce94472684957cd5aba759f495a889f154a9a2 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: thinko: really ignore deleted files. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 5dae1f19ae..294c2ecfca 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -173,13 +173,18 @@ class P4Debug(Command): def __init__(self): Command.__init__(self) self.options = [ - optparse.make_option("--verbose", dest="verbose", action="store_true"), + optparse.make_option("--verbose", dest="verbose", action="store_true", + default=False), ] self.description = "A tool to debug the output of p4 -G." self.needsGit = False + self.verbose = False def run(self, args): + j = 0 for output in p4CmdList(" ".join(args)): + print 'Element: %d' % j + j += 1 print output return True @@ -676,24 +681,27 @@ class P4Sync(Command): ## Should move this out, doesn't use SELF. def readP4Files(self, files): - specs = [(f['path'] + "#" + f['rev'], f) for f in files + files = [f for f in files if f['action'] != 'delete'] - if not specs: + if not files: return - filedata = p4CmdList('print %s' % ' '.join(['"%s"' % path - for (path, info) in specs])) + filedata = p4CmdList('print %s' % ' '.join(['"%s#%s"' % (f['path'], + f['rev']) + for f in files])) j = 0; contents = {} - while filedata[j:]: + while j < len(filedata): stat = filedata[j] - text = filedata[j+1] - j += 2 + j += 1 + text = '' + while j < len(filedata) and filedata[j]['code'] == 'text': + text += filedata[j]['data'] + j += 1 - assert stat['code'] == 'stat' and text['code'] == 'text' - contents[stat['depotFile']] = text['data'] + contents[stat['depotFile']] = text for f in files: assert not f.has_key('data') -- cgit v1.2.3 From 7530a40ce2006082580865f4db6d32b956ca8dc0 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: look for 'text' and 'binary' files. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 294c2ecfca..e955ad4f44 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -697,7 +697,8 @@ class P4Sync(Command): stat = filedata[j] j += 1 text = '' - while j < len(filedata) and filedata[j]['code'] == 'text': + while j < len(filedata) and filedata[j]['code'] in ('text', + 'binary'): text += filedata[j]['data'] j += 1 @@ -773,7 +774,7 @@ class P4Sync(Command): if self.isWindows and file["type"].endswith("text"): data = data.replace("\r\n", "\n") - self.gitStream.write("M %s inline %s\n" % (mode, relPath)) + self.gitStream.write("M %d inline %s\n" % (mode, relPath)) self.gitStream.write("data %s\n" % len(data)) self.gitStream.write(data) self.gitStream.write("\n") -- cgit v1.2.3 From a6080a0a44d5ead84db3dabbbc80e82df838533d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 7 Jun 2007 00:04:01 -0700 Subject: War on whitespace This uses "git-apply --whitespace=strip" to fix whitespace errors that have crept in to our source files over time. There are a few files that need to have trailing whitespaces (most notably, test vectors). The results still passes the test, and build result in Documentation/ area is unchanged. Signed-off-by: Junio C Hamano --- contrib/README | 1 - contrib/blameview/README | 1 - contrib/gitview/gitview | 2 -- contrib/hooks/post-receive-email | 2 +- contrib/remotes2config.sh | 2 -- 5 files changed, 1 insertion(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/README b/contrib/README index e1c0a01ff3..05f291c1f1 100644 --- a/contrib/README +++ b/contrib/README @@ -41,4 +41,3 @@ submit a patch to create a subdirectory of contrib/ and put your stuff there. -jc - diff --git a/contrib/blameview/README b/contrib/blameview/README index 50a6f67fd6..fada5ce909 100644 --- a/contrib/blameview/README +++ b/contrib/blameview/README @@ -7,4 +7,3 @@ To: Linus Torvalds Cc: git@vger.kernel.org Date: Sat, 27 Jan 2007 18:52:38 -0500 Message-ID: <20070127235238.GA28706@coredump.intra.peff.net> - diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 2d80e2bad2..3dc1ef50c7 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -1277,5 +1277,3 @@ if __name__ == "__main__": view = GitView( without_diff != 1) view.run(sys.argv[without_diff:]) - - diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index d1bef9125b..c589a39a0c 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -199,7 +199,7 @@ generate_email_footer() hooks/post-receive - -- + -- $projectdesc EOF } diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh index dc09eae972..0c8b954490 100644 --- a/contrib/remotes2config.sh +++ b/contrib/remotes2config.sh @@ -31,5 +31,3 @@ if [ -d "$GIT_DIR"/remotes ]; then esac done fi - - -- cgit v1.2.3 From 845b42cb6c1c1e80c4283234ea2162cae809bd50 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 09:19:34 +0200 Subject: Fix support for "depot-path" in older git-p4 imports Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e955ad4f44..e576f2dd9e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -153,7 +153,10 @@ def extractSettingsGitLog(log): values[key] = val - values['depot-paths'] = values.get("depot-paths").split(',') + paths = values.get("depot-paths") + if not paths: + paths = values.get("depot-path") + values['depot-paths'] = paths.split(',') return values def gitBranchExists(branch): -- cgit v1.2.3 From 583e170706f5d69770d0de220286f8f0f73667f3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 09:37:13 +0200 Subject: Fix common path "calculation" from logs of multiple branches. Need to use min instead of max for prev/cur to avoid out-of-bounds string access. Also treat "i" as index of the last match instead of a length because in case of a complete match of the two strings i was off by one. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e576f2dd9e..ba34f0a61f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1059,11 +1059,12 @@ class P4Sync(Command): else: paths = [] for (prev, cur) in zip(self.previousDepotPaths, depotPaths): - for i in range(0, max(len(cur), len(prev))): + for i in range(0, min(len(cur), len(prev))): if cur[i] <> prev[i]: + i = i - 1 break - paths.append (cur[:i]) + paths.append (cur[:i + 1]) self.previousDepotPaths = paths -- cgit v1.2.3 From 330f53b8d679ece8363a73ed6f6e211d99cd5fdf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 09:39:51 +0200 Subject: Don't attempt to set the initialParent on multi-branch imports (useless). At some point the code paths should be unified, but for now I need a working git-p4 :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ba34f0a61f..ff737d762c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1071,7 +1071,8 @@ class P4Sync(Command): if p4Change > 0: self.depotPaths = sorted(self.previousDepotPaths) self.changeRange = "@%s,#head" % p4Change - self.initialParent = parseRevision(self.branch) + if not self.detectBranches: + self.initialParent = parseRevision(self.branch) if not self.silent and not self.detectBranches: print "Performing incremental import into %s git branch" % self.branch -- cgit v1.2.3 From 6509e19cd1f6b5620d339a2be35b8a160ab30794 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 09:41:53 +0200 Subject: Hack to make the multi-branch import work again with self.depotPaths now that self.depotPath is gone Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ff737d762c..ececc44518 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -909,9 +909,10 @@ class P4Sync(Command): continue source = paths[0] destination = paths[1] - if source.startswith(self.depotPath) and destination.startswith(self.depotPath): - source = source[len(self.depotPath):-4] - destination = destination[len(self.depotPath):-4] + ## HACK + if source.startswith(self.depotPaths[0]) and destination.startswith(self.depotPaths[0]): + source = source[len(self.depotPaths[0]):-4] + destination = destination[len(self.depotPaths[0]):-4] if destination not in self.knownBranches: self.knownBranches[destination] = source if source not in self.knownBranches: -- cgit v1.2.3 From 68c42153060cab9ea6c16256febcf736a2a710d9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 12:51:03 +0200 Subject: Fix git-p4 rebase Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ececc44518..50d92c0f42 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1320,6 +1320,7 @@ class P4Rebase(Command): self.options = [ ] self.description = ("Fetches the latest revision from perforce and " + "rebases the current work (branch) against it") + self.verbose = False def run(self, args): sync = P4Sync() -- cgit v1.2.3 From b0d10df77a1130bdf3b4f59fdc18312432b90823 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 13:09:14 +0200 Subject: Fix git-p4 submit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 50d92c0f42..8be0afe828 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -276,6 +276,7 @@ class P4Submit(Command): self.origin = "" self.directSubmit = False self.trustMeLikeAFool = False + self.verbose = False self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -478,9 +479,6 @@ class P4Submit(Command): % (fileName, fileName)) def run(self, args): - # make gitdir absolute so we can cd out into the perforce checkout - os.environ["GIT_DIR"] = gitdir - if len(args) == 0: self.master = currentGitBranch() if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master): -- cgit v1.2.3 From a52d5c7bc027248fca472a402882586a9eaf59bf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 13:10:20 +0200 Subject: Fix depot-path determination for git-p4 submit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 8be0afe828..8b00e350ec 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -494,7 +494,7 @@ class P4Submit(Command): settings = extractSettingsGitLog(extractLogMessageFromGitCommit("p4")) if len(depotPath) == 0 and gitBranchExists("origin"): settings = extractSettingsGitLog(extractLogMessageFromGitCommit("origin")) - depotPaths = settings['depot-paths'] + depotPath = settings['depot-paths'][0] if len(depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" -- cgit v1.2.3 From f7baba8b092bdbc31196de1095c7779b633e28b1 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Thu, 7 Jun 2007 14:07:01 +0200 Subject: Ensure that the commit message is Windows formated (CRLF) before invoking the editor. (The default editor on Windows (Notepad) doesn't handle Unix line endings) Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 8b00e350ec..fc4e7d26f0 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -277,6 +277,7 @@ class P4Submit(Command): self.directSubmit = False self.trustMeLikeAFool = False self.verbose = False + self.isWindows = (platform.system() == "Windows") self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -398,6 +399,8 @@ class P4Submit(Command): if not self.directSubmit: logMessage = extractLogMessageFromGitCommit(id) logMessage = logMessage.replace("\n", "\n\t") + if self.isWindows: + logMessage = logMessage.replace("\n", "\r\n") logMessage = logMessage.strip() template = read_pipe("p4 change -o") @@ -444,6 +447,8 @@ class P4Submit(Command): tmpFile.close() os.remove(fileName) submitTemplate = message[:message.index(separatorLine)] + if self.isWindows: + submitTemplate = submitTemplate.replace("\r\n", "\n") if response == "y" or response == "yes": if self.dryRun: -- cgit v1.2.3 From 98ad4faf95c1e98d6f1aaccda6d96a00a3cddeed Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Thu, 7 Jun 2007 15:08:33 +0200 Subject: Fix git-p4 clone (defaultDestination) Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index fc4e7d26f0..89581eacaa 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1369,7 +1369,7 @@ class P4Clone(P4Sync): return False if not self.cloneDestination: - self.cloneDestination = self.defaultDestination() + self.cloneDestination = self.defaultDestination(args) print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination) os.makedirs(self.cloneDestination) -- cgit v1.2.3 From db775559c2d866de895cc36532ddff6b1ebbeda9 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Thu, 7 Jun 2007 15:13:59 +0200 Subject: Fix single branch import into remotes Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 89581eacaa..ad023f203c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1014,7 +1014,7 @@ class P4Sync(Command): if self.importIntoRemotes: self.refPrefix = "refs/remotes/p4/" else: - self.refPrefix = "refs/heads/" + self.refPrefix = "refs/heads/p4/" if self.syncWithOrigin and self.hasOrigin: if not self.silent: @@ -1022,7 +1022,7 @@ class P4Sync(Command): system("git fetch origin") if len(self.branch) == 0: - self.branch = self.refPrefix + "p4/master" + self.branch = self.refPrefix + "master" if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: system("git update-ref %s refs/heads/p4" % self.branch) system("git branch -D p4"); -- cgit v1.2.3 From c4b33253c221d928f3edde71123a44765495b31a Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Thu, 7 Jun 2007 15:28:04 +0200 Subject: Exclude the HEAD symbolic ref from the list of known branches Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ad023f203c..965b391cda 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -934,7 +934,7 @@ class P4Sync(Command): line = line.strip() ## only import to p4/ - if not line.startswith('p4/'): + if not line.startswith('p4/') or line == "p4/HEAD": continue branch = line if self.importIntoRemotes: -- cgit v1.2.3 From 5e100b5cd7210f8054fd3464872c0366abc3c1dc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 21:12:25 +0200 Subject: Make clone behave like git clone by default again. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 965b391cda..3fe7ae77a8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1364,6 +1364,11 @@ class P4Clone(P4Sync): sys.exit(1) depotPaths = args + + if not self.cloneDestination and len(depotPaths) > 1: + self.cloneDestination = depotPaths[-1] + depotPaths = depotPaths[:-1] + for p in depotPaths: if not p.startswith("//"): return False -- cgit v1.2.3 From a3fdd57901bfe1014c4a48e13815d80f1f0e8577 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 22:54:32 +0200 Subject: Make git-p4 submit detect the correct reference (origin) branch when working with multi-branch imports. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3fe7ae77a8..efec0be32c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -156,7 +156,8 @@ def extractSettingsGitLog(log): paths = values.get("depot-paths") if not paths: paths = values.get("depot-path") - values['depot-paths'] = paths.split(',') + if paths: + values['depot-paths'] = paths.split(',') return values def gitBranchExists(branch): @@ -494,12 +495,27 @@ class P4Submit(Command): return False depotPath = "" - settings = None - if gitBranchExists("p4"): - settings = extractSettingsGitLog(extractLogMessageFromGitCommit("p4")) - if len(depotPath) == 0 and gitBranchExists("origin"): - settings = extractSettingsGitLog(extractLogMessageFromGitCommit("origin")) - depotPath = settings['depot-paths'][0] + parent = 0 + while parent < 65535: + commit = "HEAD~%s" % parent + log = extractLogMessageFromGitCommit(commit) + settings = extractSettingsGitLog(log) + if not settings.has_key("depot-paths"): + parent = parent + 1 + continue + + depotPath = settings['depot-paths'][0] + + if len(self.origin) == 0: + names = read_pipe_lines("git name-rev '--refs=refs/remotes/p4/*' '%s'" % commit) + if len(names) > 0: + # strip away the beginning of 'HEAD~42 refs/remotes/p4/foo' + self.origin = names[0].strip()[len(commit) + 1:] + + break + + if self.verbose: + print "Origin branch is " + self.origin if len(depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" @@ -530,12 +546,6 @@ class P4Submit(Command): if response == "y" or response == "yes": system("p4 sync ...") - if len(self.origin) == 0: - if gitBranchExists("p4"): - self.origin = "p4" - else: - self.origin = "origin" - if self.reset: self.firstTime = True @@ -969,7 +979,7 @@ class P4Sync(Command): print "creating %s" % remoteHead update = True else: - settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead)) + settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead)) if settings.has_key('change') > 0: if settings['depot-paths'] == original['depot-paths']: originP4Change = int(original['change']) -- cgit v1.2.3 From df450923a2a08c50976f2d241a1c4992cf03b3a7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Jun 2007 08:49:22 +0200 Subject: Only get the expensive branch mapping from the p4 server when not syncing with the help of an origin remote (which we instead then use to get new branches from). Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index efec0be32c..36fe69a795 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -907,10 +907,6 @@ class P4Sync(Command): return p [p.strip().rfind("/") + 1:] def getBranchMapping(self): - - ## FIXME - what's a P4 projectName ? - self.projectName = self.guessProjectName() - for info in p4CmdList("branches"): details = p4Cmd("branch -o %s" % info["branch"]) viewIdx = 0 @@ -1141,7 +1137,11 @@ class P4Sync(Command): self.getLabels(); if self.detectBranches: - self.getBranchMapping(); + ## FIXME - what's a P4 projectName ? + self.projectName = self.guessProjectName() + + if not self.hasOrigin: + self.getBranchMapping(); if self.verbose: print "p4-git branches: %s" % self.p4BranchesInGit print "initial parents: %s" % self.initialParents -- cgit v1.2.3 From 709b148a907e68bfb57808de8f65b186cc9a5e21 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 30 May 2007 14:47:08 +1000 Subject: gitview: Use new-style classes This changes the Commit class to use new-style class, which has been available since Python 2.2 (Dec 2001). This is a necessary step in order to use __slots__[] declaration, so that we can reduce the memory footprint in the next patch. Signed-off-by: Michael Ellerman Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 3dc1ef50c7..7e1d68d167 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -259,7 +259,7 @@ class CellRendererGraph(gtk.GenericCellRenderer): self.set_colour(ctx, colour, 0.0, 0.5) ctx.show_text(name) -class Commit: +class Commit(object): """ This represent a commit object obtained after parsing the git-rev-list output """ @@ -339,7 +339,7 @@ class Commit: fp.close() return diff -class AnnotateWindow: +class AnnotateWindow(object): """Annotate window. This object represents and manages a single window containing the annotate information of the file @@ -519,7 +519,7 @@ class AnnotateWindow: self.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready) -class DiffWindow: +class DiffWindow(object): """Diff window. This object represents and manages a single window containing the differences between two revisions on a branch. @@ -674,7 +674,7 @@ class DiffWindow: fp.close() dialog.destroy() -class GitView: +class GitView(object): """ This is the main class """ version = "0.9" -- cgit v1.2.3 From 225696af2ceaa2e06345954003eda742a76c4e0a Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 30 May 2007 14:47:09 +1000 Subject: gitview: Define __slots__ for Commit Define __slots__ for the Commit class. This reserves space in each Commit object for only the defined variables. On my system this reduces heap usage when viewing a kernel repo by 12% ~= 55868 KB. Signed-off-by: Michael Ellerman Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 7e1d68d167..098cb01353 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -263,6 +263,9 @@ class Commit(object): """ This represent a commit object obtained after parsing the git-rev-list output """ + __slots__ = ['children_sha1', 'message', 'author', 'date', 'committer', + 'commit_date', 'commit_sha1', 'parent_sha1'] + children_sha1 = {} def __init__(self, commit_lines): -- cgit v1.2.3 From 1b9a46849a45f2b0f58d6286957316f720a301b6 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: print error message when p4 print fails (eg. due to permission problems) Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e955ad4f44..35c24c93e3 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -702,6 +702,11 @@ class P4Sync(Command): text += filedata[j]['data'] j += 1 + + if not stat.has_key('depotFile'): + sys.stderr.write("p4 print fails with: %s\n" % repr(stat)) + continue + contents[stat['depotFile']] = text for f in files: -- cgit v1.2.3 From 5265bfcb06f420841e6304b278b552a4654a4ba0 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: also strip p4/ from local imports. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 28e37fa6ab..88ea12cb37 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -948,9 +948,9 @@ class P4Sync(Command): if not line.startswith('p4/') or line == "p4/HEAD": continue branch = line - if self.importIntoRemotes: - # strip off p4 - branch = re.sub ("^p4/", "", line) + + # strip off p4 + branch = re.sub ("^p4/", "", line) self.p4BranchesInGit.append(branch) self.initialParents[self.refPrefix + branch] = parseRevision(line) -- cgit v1.2.3 From 7aded26ce870ba2998e276604fc3c18dad6133bd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 10 Jun 2007 00:22:30 +0200 Subject: Fixed the check to make sure to exclude the HEAD symbolic refs when updating the remotes/p4 branches from origin. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 88ea12cb37..d03ba0b6ad 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -962,7 +962,7 @@ class P4Sync(Command): for line in read_pipe_lines("git rev-parse --symbolic --remotes"): line = line.strip() - if (not line.startswith("origin/")) or line.endswith("HEAD\n"): + if (not line.startswith("origin/")) or line.endswith("HEAD"): continue headName = line[len("origin/"):] -- cgit v1.2.3 From cae7b732d859b06a4f560271099891f15c5700f6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 10 Jun 2007 10:57:40 +0200 Subject: Fix updating/creating remotes/p4/* heads from origin/p4/* Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d03ba0b6ad..9d52eada42 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -960,14 +960,16 @@ class P4Sync(Command): print ("Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix) + originPrefix = "origin/p4/" + for line in read_pipe_lines("git rev-parse --symbolic --remotes"): line = line.strip() - if (not line.startswith("origin/")) or line.endswith("HEAD"): + if (not line.startswith(originPrefix)) or line.endswith("HEAD"): continue - headName = line[len("origin/"):] + headName = line[len(originPrefix):] remoteHead = self.refPrefix + headName - originHead = "origin/" + headName + originHead = line original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead)) if (not original.has_key('depot-paths') @@ -1020,7 +1022,7 @@ class P4Sync(Command): # map from branch depot path to parent branch self.knownBranches = {} self.initialParents = {} - self.hasOrigin = gitBranchExists("origin") + self.hasOrigin = gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") if self.importIntoRemotes: self.refPrefix = "refs/remotes/p4/" -- cgit v1.2.3 From 6e5295c4d3e4e79838d1dd7ab4a8b4965e1c7f96 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 08:50:57 +0200 Subject: Fix project name guessing Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9d52eada42..551573afc5 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -909,7 +909,12 @@ class P4Sync(Command): def guessProjectName(self): for p in self.depotPaths: - return p [p.strip().rfind("/") + 1:] + if p.endswith("/"): + p = p[:-1] + p = p[p.strip().rfind("/") + 1:] + if not p.endswith("/"): + p += "/" + return p def getBranchMapping(self): for info in p4CmdList("branches"): -- cgit v1.2.3 From 86fda6a327ce9355ae9eab69fc7a3ec33fb5d7d1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 08:54:45 +0200 Subject: Fix depot-paths encoding for multi-path imports (don't split up //depot/path/foo) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 551573afc5..815d8bbb5d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1312,7 +1312,7 @@ class P4Sync(Command): parent = self.initialParents[branch] del self.initialParents[branch] - self.commit(description, filesForCommit, branch, branchPrefix, parent) + self.commit(description, filesForCommit, branch, [branchPrefix], parent) else: files = self.extractFilesFromCommit(description) self.commit(description, files, self.branch, self.depotPaths, -- cgit v1.2.3 From a43ff00c7c2981426d06b3621bbf62476aa5ec0d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 09:59:27 +0200 Subject: Fix support for explicit disabling of syncing with the origin Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 815d8bbb5d..ff56181310 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1028,6 +1028,8 @@ class P4Sync(Command): self.knownBranches = {} self.initialParents = {} self.hasOrigin = gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") + if not self.syncWithOrigin: + self.hasOrigin = False if self.importIntoRemotes: self.refPrefix = "refs/remotes/p4/" -- cgit v1.2.3 From 6581de096e8323385b8ec7d467e91927a80ce3b9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 10:01:58 +0200 Subject: Write out the options tag in the log message of imports only if we actually have options Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ff56181310..e380c149b4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -765,12 +765,11 @@ class P4Sync(Command): self.gitStream.write("data < 0: + self.gitStream.write(": options = %s" % details['options']) + self.gitStream.write("]\nEOT\n\n") if len(parent) > 0: if self.verbose: -- cgit v1.2.3 From c3bf3f1301319faab7344e2d8b5ab10a3d648856 Mon Sep 17 00:00:00 2001 From: Kevin Green Date: Mon, 11 Jun 2007 16:48:07 -0400 Subject: git-p4: check for existence of repo dir before trying to create When using git-p4 in this manner: git-p4 clone //depot/path/project myproject If "myproject" already exists as a dir, but not a valid git repo, it fails to create the directory. Signed-off-by: Kevin Green --- contrib/fast-import/git-p4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e380c149b4..cababc7fc8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1400,7 +1400,8 @@ class P4Clone(P4Sync): self.cloneDestination = self.defaultDestination(args) print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination) - os.makedirs(self.cloneDestination) + if not os.path.exists(self.cloneDestination): + os.makedirs(self.cloneDestination) os.chdir(self.cloneDestination) system("git init") self.gitdir = os.getcwd() + "/.git" -- cgit v1.2.3 From a9d1a27af1f1c997eeef67aed54136ae83355bb9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 23:28:03 +0200 Subject: Provide some information for single branch imports where the commits go Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index cababc7fc8..6c199296d3 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1175,7 +1175,7 @@ class P4Sync(Command): self.gitError = importProcess.stderr if self.revision: - print "Doing initial import of %s from revision %s" % (' '.join(self.depotPaths), self.revision) + print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), self.revision, self.branch) details = { "user" : "git perforce import user", "time" : int(time.time()) } details["desc"] = ("Initial import of %s from the state at revision %s" @@ -1252,6 +1252,9 @@ class P4Sync(Command): print "No changes to import!" return True + if not self.silent and not self.detectBranches: + print "Import destination: %s" % self.branch + self.updatedBranches = set() cnt = 1 -- cgit v1.2.3 From 81b462a629c2feff9ab1dc43148643aad9571271 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 23:30:23 +0200 Subject: Mention remotes/p4/master also in the documentation. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index c315158d8d..b16a8384bc 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -36,8 +36,8 @@ If you want more control you can also use the git-p4 sync command directly: git-p4 sync //path/in/your/perforce/depot This will import the current head revision of the specified depot path into a -"p4" branch of your git repository. You can use the --branch=mybranch option -to use a different branch. +"remotes/p4/master" branch of your git repository. You can use the +--branch=mybranch option to use a different branch. If you want to import the entire history of a given depot path just use @@ -57,7 +57,7 @@ newer changes from the Perforce depot by just calling git-p4 sync -in your git repository. By default the "p4" branch is updated. +in your git repository. By default the "remotes/p4/master" branch is updated. It is recommended to run 'git repack -a -d -f' from time to time when using incremental imports to optimally combine the individual git packs that each -- cgit v1.2.3 From e6b711f00e4578eb4b2127af12f73a5fa438f948 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 23:40:25 +0200 Subject: git-p4 submit: Fix missing quotes around p4 commands to make them work with spaces in filenames Noticed by Alex Riesen Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6c199296d3..21f9ba7e07 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -391,10 +391,10 @@ class P4Submit(Command): system(applyPatchCmd) for f in filesToAdd: - system("p4 add %s" % f) + system("p4 add \"%s\"" % f) for f in filesToDelete: - system("p4 revert %s" % f) - system("p4 delete %s" % f) + system("p4 revert \"%s\"" % f) + system("p4 delete \"%s\"" % f) logMessage = "" if not self.directSubmit: -- cgit v1.2.3 From 27d2d8119bf985a0b59152737316f04f871e03f7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Jun 2007 14:31:59 +0200 Subject: Moved the code from git-p4 submit to figure out the upstream branch point into a separate helper method. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 21f9ba7e07..1c7db11529 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -168,6 +168,28 @@ def gitBranchExists(branch): def gitConfig(key): return read_pipe("git config %s" % key, ignore_error=True).strip() +def findUpstreamBranchPoint(): + settings = None + branchPoint = "" + parent = 0 + while parent < 65535: + commit = "HEAD~%s" % parent + log = extractLogMessageFromGitCommit(commit) + settings = extractSettingsGitLog(log) + if not settings.has_key("depot-paths"): + parent = parent + 1 + continue + + names = read_pipe_lines("git name-rev '--refs=refs/remotes/p4/*' '%s'" % commit) + if len(names) <= 0: + continue + + # strip away the beginning of 'HEAD~42 refs/remotes/p4/foo' + branchPoint = names[0].strip()[len(commit) + 1:] + break + + return [branchPoint, settings] + class Command: def __init__(self): self.usage = "usage: %prog [options]" @@ -494,25 +516,10 @@ class P4Submit(Command): else: return False - depotPath = "" - parent = 0 - while parent < 65535: - commit = "HEAD~%s" % parent - log = extractLogMessageFromGitCommit(commit) - settings = extractSettingsGitLog(log) - if not settings.has_key("depot-paths"): - parent = parent + 1 - continue - - depotPath = settings['depot-paths'][0] - - if len(self.origin) == 0: - names = read_pipe_lines("git name-rev '--refs=refs/remotes/p4/*' '%s'" % commit) - if len(names) > 0: - # strip away the beginning of 'HEAD~42 refs/remotes/p4/foo' - self.origin = names[0].strip()[len(commit) + 1:] - - break + [upstream, settings] = findUpstreamBranchPoint() + depotPath = settings['depot-paths'][0] + if len(self.origin) == 0: + self.origin = upstream if self.verbose: print "Origin branch is " + self.origin -- cgit v1.2.3 From d7e3868cdfdc73c3de15296ecf32138a8308c07e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Jun 2007 14:34:46 +0200 Subject: Fix git-p4 rebase to detect the correct upstream branch instead of unconditionally always rebasing on top of remotes/p4/master Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1c7db11529..1168704be4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1361,9 +1361,17 @@ class P4Rebase(Command): def run(self, args): sync = P4Sync() sync.run([]) - print "Rebasing the current branch" + + [upstream, settings] = findUpstreamBranchPoint() + if len(upstream) == 0: + die("Cannot find upstream branchpoint for rebase") + + # the branchpoint may be p4/foo~3, so strip off the parent + upstream = re.sub("~[0-9]+$", "", upstream) + + print "Rebasing the current branch onto %s" % upstream oldHead = read_pipe("git rev-parse HEAD").strip() - system("git rebase p4") + system("git rebase %s" % upstream) system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True -- cgit v1.2.3 From cbae7080a7fe0586255e85e1d14f1011260e8eee Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Tue, 12 Jun 2007 15:27:52 +0200 Subject: Only use double quotes on Windows Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1168704be4..b3f27fe90f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -180,7 +180,7 @@ def findUpstreamBranchPoint(): parent = parent + 1 continue - names = read_pipe_lines("git name-rev '--refs=refs/remotes/p4/*' '%s'" % commit) + names = read_pipe_lines("git name-rev \"--refs=refs/remotes/p4/*\" \"%s\"" % commit) if len(names) <= 0: continue -- cgit v1.2.3 From 30a844874db6ee25b6f54ea786f0adabd8e074d7 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 13 Jun 2007 14:16:15 +0530 Subject: gitview: Fix the blame interface. The async reading from the pipe was skipping some of the input lines. Fix the same by making sure that we add the partial content of the previous read to the newly read data. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 098cb01353..93ecfc1bb9 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -352,6 +352,7 @@ class AnnotateWindow(object): self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_border_width(0) self.window.set_title("Git repository browser annotation window") + self.prev_read = "" # Use two thirds of the screen by default screen = self.window.get_screen() @@ -401,7 +402,10 @@ class AnnotateWindow(object): def data_ready(self, source, condition): while (1): try : - buffer = source.read(8192) + # A simple readline doesn't work + # a readline bug ?? + buffer = source.read(100) + except: # resource temporary not available return True @@ -411,6 +415,19 @@ class AnnotateWindow(object): source.close() return False + if (self.prev_read != ""): + buffer = self.prev_read + buffer + self.prev_read = "" + + if (buffer[len(buffer) -1] != '\n'): + try: + newline_index = buffer.rindex("\n") + except ValueError: + newline_index = 0 + + self.prev_read = buffer[newline_index:(len(buffer))] + buffer = buffer[0:newline_index] + for buff in buffer.split("\n"): annotate_line = re.compile('^([0-9a-f]{40}) (.+) (.+) (.+)$') m = annotate_line.match(buff) -- cgit v1.2.3 From 1be846f6e48bae57c3d5cc346300eaf9550f6c8d Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 13 Jun 2007 14:16:16 +0530 Subject: gitview: run blame with -C -C pass -C -C option to git-blame so that blame browsing works when the data is copied over from other files. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 93ecfc1bb9..5931766620 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -533,7 +533,7 @@ class AnnotateWindow(object): self.add_file_data(filename, commit_sha1, line_num) - fp = os.popen("git blame --incremental -- " + filename + " " + commit_sha1) + fp = os.popen("git blame --incremental -C -C -- " + filename + " " + commit_sha1) flags = fcntl.fcntl(fp.fileno(), fcntl.F_GETFL) fcntl.fcntl(fp.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK) self.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready) -- cgit v1.2.3 From 3c699645f589612065b048ecde45a4ea293dc75f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 16 Jun 2007 13:09:21 +0200 Subject: Fix initial multi-branch import. The list of existing p4 branches in git wasn't initialized. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b3f27fe90f..e527734be5 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -637,6 +637,7 @@ class P4Sync(Command): self.isWindows = (platform.system() == "Windows") self.keepRepoPath = False self.depotPaths = None + self.p4BranchesInGit = [] if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False -- cgit v1.2.3 From da4a660161cfe9d04c0849d77fa460c6ffc6503c Mon Sep 17 00:00:00 2001 From: Benjamin Sergeant Date: Fri, 8 Jun 2007 11:13:55 -0700 Subject: git-p4 fails when cloning a p4 depo. A perforce command with all the files in the repo is generated to get all the file content. Here is a patch to break it into multiple successive perforce command who uses 4K of parameter max, and collect the output for later. It works, but not for big depos, because the whole perforce depo content is stored in memory in P4Sync.run(), and it looks like mine is bigger than 2 Gigs, so I had to kill the process. [Simon: I added the bit about using SC_ARG_MAX, as suggested by Han-Wen] Signed-off-by: Benjamin Sergeant Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e527734be5..d1f8d3b78d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -711,9 +711,23 @@ class P4Sync(Command): if not files: return - filedata = p4CmdList('print %s' % ' '.join(['"%s#%s"' % (f['path'], - f['rev']) - for f in files])) + # We cannot put all the files on the command line + # OS have limitations on the max lenght of arguments + # POSIX says it's 4096 bytes, default for Linux seems to be 130 K. + # and all OS from the table below seems to be higher than POSIX. + # See http://www.in-ulm.de/~mascheck/various/argmax/ + argmax = min(4000, os.sysconf('SC_ARG_MAX')) + chunk = '' + filedata = [] + for i in xrange(len(files)): + f = files[i] + chunk += '"%s#%s" ' % (f['path'], f['rev']) + if len(chunk) > argmax or i == len(files)-1: + data = p4CmdList('print %s' % chunk) + if "p4ExitCode" in data[0]: + die("Problems executing p4. Error: [%d]." % (data[0]['p4ExitCode'])); + filedata.extend(data) + chunk = '' j = 0; contents = {} -- cgit v1.2.3 From 6555b2ccfe63913b3e5c8b02e117f0f476307ca2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Jun 2007 11:25:34 +0200 Subject: Fix the branch mapping detection to be independent from the order of the "p4 branches" output. Collect "unknown" source branches separately and register them at the end. Also added a minor speed up to splitFilesIntoBranches by breaking out of the loop through all branches when it's safe. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d1f8d3b78d..3b6d8a09d1 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -700,6 +700,7 @@ class P4Sync(Command): if branch not in branches: branches[branch] = [] branches[branch].append(file) + break return branches @@ -938,6 +939,8 @@ class P4Sync(Command): return p def getBranchMapping(self): + lostAndFoundBranches = set() + for info in p4CmdList("branches"): details = p4Cmd("branch -o %s" % info["branch"]) viewIdx = 0 @@ -953,10 +956,17 @@ class P4Sync(Command): if source.startswith(self.depotPaths[0]) and destination.startswith(self.depotPaths[0]): source = source[len(self.depotPaths[0]):-4] destination = destination[len(self.depotPaths[0]):-4] - if destination not in self.knownBranches: - self.knownBranches[destination] = source + + self.knownBranches[destination] = source + + lostAndFoundBranches.discard(destination) + if source not in self.knownBranches: - self.knownBranches[source] = source + lostAndFoundBranches.add(source) + + + for branch in lostAndFoundBranches: + self.knownBranches[branch] = branch def listExistingP4GitBranches(self): self.p4BranchesInGit = [] -- cgit v1.2.3 From 1a2edf4e8dff05fea66daf82c675cfab673c1242 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Jun 2007 15:10:24 +0200 Subject: Warn about conflicting p4 branch mappings and use the first one found. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3b6d8a09d1..2040591383 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -957,6 +957,12 @@ class P4Sync(Command): source = source[len(self.depotPaths[0]):-4] destination = destination[len(self.depotPaths[0]):-4] + if destination in self.knownBranches: + if not self.silent: + print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination) + print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination) + continue + self.knownBranches[destination] = source lostAndFoundBranches.discard(destination) -- cgit v1.2.3 From 09d89de2e31ed4d62b6ff344e9ad9ef29550121b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 20 Jun 2007 23:10:28 +0200 Subject: Added git-p4 branches command that shows the mapping of perforce depot paths to imported git branches. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2040591383..16de15c4ea 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1465,6 +1465,31 @@ class P4Clone(P4Sync): return True +class P4Branches(Command): + def __init__(self): + Command.__init__(self) + self.options = [ ] + self.description = ("Shows the git branches that hold imports and their " + + "corresponding perforce depot paths") + self.verbose = False + + def run(self, args): + cmdline = "git rev-parse --symbolic " + cmdline += " --remotes" + + for line in read_pipe_lines(cmdline): + line = line.strip() + + if not line.startswith('p4/') or line == "p4/HEAD": + continue + branch = line + + log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch) + settings = extractSettingsGitLog(log) + + print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"]) + return True + class HelpFormatter(optparse.IndentedHelpFormatter): def __init__(self): optparse.IndentedHelpFormatter.__init__(self) @@ -1489,7 +1514,8 @@ commands = { "sync" : P4Sync, "rebase" : P4Rebase, "clone" : P4Clone, - "rollback" : P4RollBack + "rollback" : P4RollBack, + "branches" : P4Branches } -- cgit v1.2.3 From 9ceab36375dfd4088b265033e1de9225b3527cab Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 22 Jun 2007 00:01:57 +0200 Subject: Make it possible to specify the HEAD for the internal findUpstreamBranchPoint function. This isn't used right now in git-p4 but I use it in an external script that loads git-p4 as module. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 16de15c4ea..54a05eb99c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -168,12 +168,12 @@ def gitBranchExists(branch): def gitConfig(key): return read_pipe("git config %s" % key, ignore_error=True).strip() -def findUpstreamBranchPoint(): +def findUpstreamBranchPoint(head = "HEAD"): settings = None branchPoint = "" parent = 0 while parent < 65535: - commit = "HEAD~%s" % parent + commit = head + "~%s" % parent log = extractLogMessageFromGitCommit(commit) settings = extractSettingsGitLog(log) if not settings.has_key("depot-paths"): -- cgit v1.2.3 From 92d7c8e37b96244f2c55f5c66ec52c6325acca1d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 22 Jun 2007 18:44:04 -0400 Subject: Avoid src:dst syntax as default bash completion for git push Raimund Bauer just discovered that the default bash completion for a local branch name in a git-push line is not the best choice when the branch does not exist on the remote system. In the past we have always completed the local name 'test' as "test:test", indicating that the destination name is the same as the local name. But this fails when "test" does not yet exist on the remote system, as there is no "test" branch for it to match the name against. Fortunately git-push does the right thing when given just the local branch, as it assumes you want to use the same name in the destination repository. So we now offer "test" as the completion in a git-push line, and let git-push assume that is also the remote branch name. We also still support the remote branch completion after the :, but only if the user manually adds the colon before trying to get a completion. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9e72f0f7b1..c7c9963347 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -683,7 +683,7 @@ _git_push () __gitcomp "$(__git_refs "$remote")" "" "${cur#*:}" ;; *) - __gitcomp "$(__git_refs2)" + __gitcomp "$(__git_refs)" ;; esac ;; -- cgit v1.2.3 From 09381b458f5f1ac1a78df1aaeeb53aa79fcf2c4f Mon Sep 17 00:00:00 2001 From: Julian Phillips Date: Tue, 19 Jun 2007 12:44:43 +0100 Subject: new-workdir: handle rev-parse --git-dir not always giving full path rev-parse --git-dir outputs a full path - except for the single case of when the path would be $(pwd)/.git, in which case it outputs simply .git. Check for this special case and handle it. Signed-off-by: Julian Phillips Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index f2a3615bbc..709b2a3ac0 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -24,6 +24,11 @@ git_dir=$(cd "$orig_git" 2>/dev/null && git rev-parse --git-dir 2>/dev/null) || die "\"$orig_git\" is not a git repository!" +if test "$git_dir" == ".git" +then + git_dir="$orig_git/.git" +fi + # don't link to a workdir if test -L "$git_dir/config" then -- cgit v1.2.3 From 161fea832a8e8ea7d6cdc1bdc388f19825641a53 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 24 Jun 2007 19:42:16 -0400 Subject: Teach bash how to complete +refspec on git-push Using `git push origin +foo` to forcefully overwrite the remote branch named foo is a common idiom, especially since + is shorter than the long option --force and can be specified on a per-branch basis. We now complete `git push origin +foo` just like we do the standard `git push origin foo`. The leading + on a branch refspec does not alter the completion. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c7c9963347..f2b10fa5f6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -682,6 +682,9 @@ _git_push () esac __gitcomp "$(__git_refs "$remote")" "" "${cur#*:}" ;; + +*) + __gitcomp "$(__git_refs)" + "${cur#+}" + ;; *) __gitcomp "$(__git_refs)" ;; -- cgit v1.2.3 From b658d50325c938d87d4891cb62e2eefb0b58a62c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 25 Jun 2007 13:04:26 +0200 Subject: git-new-workdir: Fix shell warning about operator == used with test. Use = instead of == with test to test for equality. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 709b2a3ac0..3ff6bd166a 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -24,7 +24,7 @@ git_dir=$(cd "$orig_git" 2>/dev/null && git rev-parse --git-dir 2>/dev/null) || die "\"$orig_git\" is not a git repository!" -if test "$git_dir" == ".git" +if test "$git_dir" = ".git" then git_dir="$orig_git/.git" fi -- cgit v1.2.3 From 5be60078c935ed08ee8eb5a32680bdfb6bb5bdf3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 2 Jul 2007 22:52:14 -0700 Subject: Rewrite "git-frotz" to "git frotz" This uses the remove-dashes target to replace "git-frotz" to "git frotz". Signed-off-by: Junio C Hamano --- contrib/examples/git-gc.sh | 8 ++++---- contrib/examples/git-resolve.sh | 34 +++++++++++++++++----------------- contrib/remotes2config.sh | 4 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-gc.sh b/contrib/examples/git-gc.sh index 436d7caff5..2ae235b081 100755 --- a/contrib/examples/git-gc.sh +++ b/contrib/examples/git-gc.sh @@ -30,8 +30,8 @@ notbare|"") esac test "true" != "$pack_refs" || -git-pack-refs --prune && -git-reflog expire --all && +git pack-refs --prune && +git reflog expire --all && git-repack -a -d -l && -$no_prune git-prune && -git-rerere gc || exit +$no_prune git prune && +git rerere gc || exit diff --git a/contrib/examples/git-resolve.sh b/contrib/examples/git-resolve.sh index 36b90e3849..0ee1bd898e 100755 --- a/contrib/examples/git-resolve.sh +++ b/contrib/examples/git-resolve.sh @@ -17,8 +17,8 @@ dropheads() { "$GIT_DIR/LAST_MERGE" || exit 1 } -head=$(git-rev-parse --verify "$1"^0) && -merge=$(git-rev-parse --verify "$2"^0) && +head=$(git rev-parse --verify "$1"^0) && +merge=$(git rev-parse --verify "$2"^0) && merge_name="$2" && merge_msg="$3" || usage @@ -34,7 +34,7 @@ dropheads echo $head > "$GIT_DIR"/ORIG_HEAD echo $merge > "$GIT_DIR"/LAST_MERGE -common=$(git-merge-base $head $merge) +common=$(git merge-base $head $merge) if [ -z "$common" ]; then die "Unable to find common commit between" $merge $head fi @@ -46,11 +46,11 @@ case "$common" in exit 0 ;; "$head") - echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $merge)" - git-read-tree -u -m $head $merge || exit 1 - git-update-ref -m "resolve $merge_name: Fast forward" \ + echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $merge)" + git read-tree -u -m $head $merge || exit 1 + git update-ref -m "resolve $merge_name: Fast forward" \ HEAD "$merge" "$head" - git-diff-tree -p $head $merge | git-apply --stat + git diff-tree -p $head $merge | git apply --stat dropheads exit 0 ;; @@ -62,7 +62,7 @@ git var GIT_COMMITTER_IDENT >/dev/null || exit # Find an optimum merge base if there are more than one candidates. LF=' ' -common=$(git-merge-base -a $head $merge) +common=$(git merge-base -a $head $merge) case "$common" in ?*"$LF"?*) echo "Trying to find the optimum merge base." @@ -72,10 +72,10 @@ case "$common" in for c in $common do rm -f $G - GIT_INDEX_FILE=$G git-read-tree -m $c $head $merge \ + GIT_INDEX_FILE=$G git read-tree -m $c $head $merge \ 2>/dev/null || continue # Count the paths that are unmerged. - cnt=`GIT_INDEX_FILE=$G git-ls-files --unmerged | wc -l` + cnt=`GIT_INDEX_FILE=$G git ls-files --unmerged | wc -l` if test $best_cnt -le 0 -o $cnt -le $best_cnt then best=$c @@ -92,9 +92,9 @@ case "$common" in esac echo "Trying to merge $merge into $head using $common." -git-update-index --refresh 2>/dev/null -git-read-tree -u -m $common $head $merge || exit 1 -result_tree=$(git-write-tree 2> /dev/null) +git update-index --refresh 2>/dev/null +git read-tree -u -m $common $head $merge || exit 1 +result_tree=$(git write-tree 2> /dev/null) if [ $? -ne 0 ]; then echo "Simple merge failed, trying Automatic merge" git-merge-index -o git-merge-one-file -a @@ -102,11 +102,11 @@ if [ $? -ne 0 ]; then echo $merge > "$GIT_DIR"/MERGE_HEAD die "Automatic merge failed, fix up by hand" fi - result_tree=$(git-write-tree) || exit 1 + result_tree=$(git write-tree) || exit 1 fi -result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree -p $head -p $merge) +result_commit=$(echo "$merge_msg" | git commit-tree $result_tree -p $head -p $merge) echo "Committed merge $result_commit" -git-update-ref -m "resolve $merge_name: In-index merge" \ +git update-ref -m "resolve $merge_name: In-index merge" \ HEAD "$result_commit" "$head" -git-diff-tree -p $head $result_commit | git-apply --stat +git diff-tree -p $head $result_commit | git apply --stat dropheads diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh index 0c8b954490..5838b3ab05 100644 --- a/contrib/remotes2config.sh +++ b/contrib/remotes2config.sh @@ -26,8 +26,8 @@ if [ -d "$GIT_DIR"/remotes ]; then mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old fi ;; *) - echo "git-config $key "$value" $regex" - git-config $key "$value" $regex || error=1 ;; + echo "git config $key "$value" $regex" + git config $key "$value" $regex || error=1 ;; esac done fi -- cgit v1.2.3 From b4372ef136b0a5a2c1dbd88a11dd72b478d0e0a5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 6 Jul 2007 13:05:59 +0100 Subject: Enable "git rerere" by the config variable rerere.enabled Earlier, "git rerere" was enabled by creating the directory .git/rr-cache. That is definitely not in line with most other features, which are enabled by a config variable. So, check the config variable "rerere.enabled". If it is set to "false" explicitely, do not activate rerere, even if .git/rr-cache exists. This should help when you want to disable rerere temporarily. If "rerere.enabled" is not set at all, fall back to detection of the directory .git/rr-cache. [jc: with minimum tweaks] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index f60017948f..457f95fc05 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -681,8 +681,7 @@ and returns the process output as a string." (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) (git-set-files-state files 'uptodate) - (when (file-directory-p ".git/rr-cache") - (git-run-command nil nil "rerere")) + (git-run-command nil nil "rerere") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) -- cgit v1.2.3 From 73f893605050c291dd8c4d8f3f50b8fec47dcf51 Mon Sep 17 00:00:00 2001 From: Brian Downing Date: Wed, 11 Jul 2007 22:02:25 -0500 Subject: Pack information tool This tool will print vaguely pretty information about a pack. It expects the output of "git-verify-pack -v" as input on stdin. $ git-verify-pack -v | packinfo.pl See the documentation in the script (contrib/stats/packinfo.pl) for more information. Signed-off-by: Brian Downing Signed-off-by: Junio C Hamano --- contrib/stats/packinfo.pl | 212 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100755 contrib/stats/packinfo.pl (limited to 'contrib') diff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl new file mode 100755 index 0000000000..792673a325 --- /dev/null +++ b/contrib/stats/packinfo.pl @@ -0,0 +1,212 @@ +#!/bin/perl +# +# This tool will print vaguely pretty information about a pack. It +# expects the output of "git-verify-pack -v" as input on stdin. +# +# $ git-verify-pack -v | packinfo.pl +# +# This prints some full-pack statistics; currently "all sizes", "all +# path sizes", "tree sizes", "tree path sizes", and "depths". +# +# * "all sizes" stats are across every object size in the file; +# full sizes for base objects, and delta size for deltas. +# * "all path sizes" stats are across all object's "path sizes". +# A path size is the sum of the size of the delta chain, including the +# base object. In other words, it's how many bytes need be read to +# reassemble the file from deltas. +# * "tree sizes" are object sizes grouped into delta trees. +# * "tree path sizes" are path sizes grouped into delta trees. +# * "depths" should be obvious. +# +# When run as: +# +# $ git-verify-pack -v | packinfo.pl -tree +# +# the trees of objects are output along with the stats. This looks +# like: +# +# 0 commit 031321c6... 803 803 +# +# 0 blob 03156f21... 1767 1767 +# 1 blob f52a9d7f... 10 1777 +# 2 blob a8cc5739... 51 1828 +# 3 blob 660e90b1... 15 1843 +# 4 blob 0cb8e3bb... 33 1876 +# 2 blob e48607f0... 311 2088 +# size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85 +# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26 +# +# The first number after the sha1 is the object size, the second +# number is the path size. The statistics are across all objects in +# the previous delta tree. Obviously they are omitted for trees of +# one object. +# +# When run as: +# +# $ git-verify-pack -v | packinfo.pl -tree -filenames +# +# it adds filenames to the tree. Getting this information is slow: +# +# 0 blob 03156f21... 1767 1767 Documentation/git-lost-found.txt @ tags/v1.2.0~142 +# 1 blob f52a9d7f... 10 1777 Documentation/git-lost-found.txt @ tags/v1.5.0-rc1~74 +# 2 blob a8cc5739... 51 1828 Documentation/git-lost+found.txt @ tags/v0.99.9h^0 +# 3 blob 660e90b1... 15 1843 Documentation/git-lost+found.txt @ master~3222^2~2 +# 4 blob 0cb8e3bb... 33 1876 Documentation/git-lost+found.txt @ master~3222^2~3 +# 2 blob e48607f0... 311 2088 Documentation/git-lost-found.txt @ tags/v1.5.2-rc3~4 +# size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85 +# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26 +# +# When run as: +# +# $ git-verify-pack -v | packinfo.pl -dump +# +# it prints out "sha1 size pathsize depth" for each sha1 in lexical +# order. +# +# 000079a2eaef17b7eae70e1f0f635557ea67b644 30 472 7 +# 00013cafe6980411aa6fdd940784917b5ff50f0a 44 1542 4 +# 000182eacf99cde27d5916aa415921924b82972c 499 499 0 +# ... +# +# This is handy for comparing two packs. Adding "-filenames" will add +# filenames, as per "-tree -filenames" above. + +use strict; +use Getopt::Long; + +my $filenames = 0; +my $tree = 0; +my $dump = 0; +GetOptions("tree" => \$tree, + "filenames" => \$filenames, + "dump" => \$dump); + +my %parents; +my %children; +my %sizes; +my @roots; +my %paths; +my %types; +my @commits; +my %names; +my %depths; +my @depths; + +while () { + my ($sha1, $type, $size, $offset, $depth, $parent) = split(/\s+/, $_); + next unless ($sha1 =~ /^[0-9a-f]{40}$/); + $depths{$sha1} = $depth || 0; + push(@depths, $depth || 0); + push(@commits, $sha1) if ($type eq 'commit'); + push(@roots, $sha1) unless $parent; + $parents{$sha1} = $parent; + $types{$sha1} = $type; + push(@{$children{$parent}}, $sha1); + $sizes{$sha1} = $size; +} + +if ($filenames && ($tree || $dump)) { + open(NAMES, "git-name-rev --all|"); + while () { + if (/^(\S+)\s+(.*)$/) { + my ($sha1, $name) = ($1, $2); + $names{$sha1} = $name; + } + } + close NAMES; + + for my $commit (@commits) { + my $name = $names{$commit}; + open(TREE, "git-ls-tree -t -r $commit|"); + print STDERR "Plumbing tree $name\n"; + while () { + if (/^(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) { + my ($mode, $type, $sha1, $path) = ($1, $2, $3, $4); + $paths{$sha1} = "$path @ $name"; + } + } + close TREE; + } +} + +sub stats { + my @data = sort {$a <=> $b} @_; + my $min = $data[0]; + my $max = $data[$#data]; + my $total = 0; + my $count = scalar @data; + for my $datum (@data) { + $total += $datum; + } + my $mean = $total / $count; + my $median = $data[int(@data / 2)]; + my $diff_sum = 0; + for my $datum (@data) { + $diff_sum += ($datum - $mean)**2; + } + my $std_dev = sqrt($diff_sum / $count); + return ($count, $total, $min, $max, $mean, $median, $std_dev); +} + +sub print_stats { + my $name = shift; + my ($count, $total, $min, $max, $mean, $median, $std_dev) = stats(@_); + printf("%s: count %s total %s min %s max %s mean %.2f median %s std_dev %.2f\n", + $name, $count, $total, $min, $max, $mean, $median, $std_dev); +} + +my @sizes; +my @path_sizes; +my @all_sizes; +my @all_path_sizes; +my %path_sizes; + +sub dig { + my ($sha1, $depth, $path_size) = @_; + $path_size += $sizes{$sha1}; + push(@sizes, $sizes{$sha1}); + push(@all_sizes, $sizes{$sha1}); + push(@path_sizes, $path_size); + push(@all_path_sizes, $path_size); + $path_sizes{$sha1} = $path_size; + if ($tree) { + printf("%3d%s %6s %s %8d %8d %s\n", + $depth, (" " x $depth), $types{$sha1}, + $sha1, $sizes{$sha1}, $path_size, $paths{$sha1}); + } + for my $child (@{$children{$sha1}}) { + dig($child, $depth + 1, $path_size); + } +} + +my @tree_sizes; +my @tree_path_sizes; + +for my $root (@roots) { + undef @sizes; + undef @path_sizes; + dig($root, 0, 0); + my ($aa, $sz_total) = stats(@sizes); + my ($bb, $psz_total) = stats(@path_sizes); + push(@tree_sizes, $sz_total); + push(@tree_path_sizes, $psz_total); + if ($tree) { + if (@sizes > 1) { + print_stats(" size", @sizes); + print_stats("path size", @path_sizes); + } + print "\n"; + } +} + +if ($dump) { + for my $sha1 (sort keys %sizes) { + print "$sha1 $sizes{$sha1} $path_sizes{$sha1} $depths{$sha1} $paths{$sha1}\n"; + } +} else { + print_stats(" all sizes", @all_sizes); + print_stats(" all path sizes", @all_path_sizes); + print_stats(" tree sizes", @tree_sizes); + print_stats("tree path sizes", @tree_path_sizes); + print_stats(" depths", @depths); +} -- cgit v1.2.3 From 750bd6ac350079cf1f76c24acb7686ad89a95102 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 12 Jul 2007 03:40:18 -0400 Subject: script to display a distribution of longest common hash prefixes This script was originally posted on the git mailing list by Randal L. Schwartz . Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- contrib/stats/git-common-hash | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100755 contrib/stats/git-common-hash (limited to 'contrib') diff --git a/contrib/stats/git-common-hash b/contrib/stats/git-common-hash new file mode 100755 index 0000000000..e27fd088be --- /dev/null +++ b/contrib/stats/git-common-hash @@ -0,0 +1,26 @@ +#!/bin/sh + +# This script displays the distribution of longest common hash prefixes. +# This can be used to determine the minimum prefix length to use +# for object names to be unique. + +git rev-list --objects --all | sort | perl -lne ' + substr($_, 40) = ""; + # uncomment next line for a distribution of bits instead of hex chars + # $_ = unpack("B*",pack("H*",$_)); + if (defined $p) { + ($p ^ $_) =~ /^(\0*)/; + $common = length $1; + if (defined $pcommon) { + $count[$pcommon > $common ? $pcommon : $common]++; + } else { + $count[$common]++; # first item + } + } + $p = $_; + $pcommon = $common; + END { + $count[$common]++; # last item + print "$_: $count[$_]" for 0..$#count; + } +' -- cgit v1.2.3 From b492bbd8362840f9ac1e5389cdfaa8dd73ca793b Mon Sep 17 00:00:00 2001 From: Brian Downing Date: Thu, 12 Jul 2007 09:16:11 -0500 Subject: Correct shebang line for contrib/stats/packinfo.pl "/bin/perl"? What was I thinking? Signed-off-by: Brian Downing Signed-off-by: Junio C Hamano --- contrib/stats/packinfo.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl index 792673a325..aab501ea08 100755 --- a/contrib/stats/packinfo.pl +++ b/contrib/stats/packinfo.pl @@ -1,4 +1,4 @@ -#!/bin/perl +#!/usr/bin/perl # # This tool will print vaguely pretty information about a pack. It # expects the output of "git-verify-pack -v" as input on stdin. -- cgit v1.2.3 From 248c648a0de945ec37af13e0a9b8671da525c323 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Thu, 12 Jul 2007 16:48:48 +0200 Subject: Add missing functions to contrib/emacs/vc-git.el This is necessary to make several editing functions work, like C-u C-x v = Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index e456ab9712..b8f6be5c0a 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -81,6 +81,71 @@ (match-string 2 str) str))) +(defun vc-git-symbolic-commit (commit) + "Translate COMMIT string into symbolic form. +Returns nil if not possible." + (and commit + (with-temp-buffer + (and + (zerop + (call-process "git" nil '(t nil) nil "name-rev" + "--name-only" "--tags" + commit)) + (goto-char (point-min)) + (= (forward-line 2) 1) + (bolp) + (buffer-substring-no-properties (point-min) (1- (point-max))))))) + +(defun vc-git-previous-version (file rev) + "git-specific version of `vc-previous-version'." + (let ((default-directory (file-name-directory (expand-file-name file))) + (file (file-name-nondirectory file))) + (vc-git-symbolic-commit + (with-temp-buffer + (and + (zerop + (call-process "git" nil '(t nil) nil "rev-list" + "-2" rev "--" file)) + (goto-char (point-max)) + (bolp) + (zerop (forward-line -1)) + (not (bobp)) + (buffer-substring-no-properties + (point) + (1- (point-max)))))))) + +(defun vc-git-next-version (file rev) + "git-specific version of `vc-next-version'." + (let* ((default-directory (file-name-directory + (expand-file-name file))) + (file (file-name-nondirectory file)) + (current-rev + (with-temp-buffer + (and + (zerop + (call-process "git" nil '(t nil) nil "rev-list" + "-1" rev "--" file)) + (goto-char (point-max)) + (bolp) + (zerop (forward-line -1)) + (bobp) + (buffer-substring-no-properties + (point) + (1- (point-max))))))) + (and current-rev + (vc-git-symbolic-commit + (with-temp-buffer + (and + (zerop + (call-process "git" nil '(t nil) nil "rev-list" + "HEAD" "--" file)) + (goto-char (point-min)) + (search-forward current-rev nil t) + (zerop (forward-line -1)) + (buffer-substring-no-properties + (point) + (progn (forward-line 1) (1- (point)))))))))) + (defun vc-git-revert (file &optional contents-done) "Revert FILE to the version stored in the git repository." (if contents-done -- cgit v1.2.3 From af6861b14488a91ab932c63852979eaf78b549d2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 14 Jul 2007 13:43:09 -0700 Subject: Add contrib/stats/mailmap.pl script This script reads the existing commit log and .mailmap file, and outputs author e-mail addresses that would map to more than one names (most likely due to difference in the way they are spelled, but some are due to ancient botched commits). Signed-off-by: Junio C Hamano --- contrib/stats/mailmap.pl | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 contrib/stats/mailmap.pl (limited to 'contrib') diff --git a/contrib/stats/mailmap.pl b/contrib/stats/mailmap.pl new file mode 100755 index 0000000000..4b852e2455 --- /dev/null +++ b/contrib/stats/mailmap.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl -w +my %mailmap = (); +open I, "<", ".mailmap"; +while () { + chomp; + next if /^#/; + if (my ($author, $mail) = /^(.*?)\s+<(.+)>$/) { + $mailmap{$mail} = $author; + } +} +close I; + +my %mail2author = (); +open I, "git log --pretty='format:%ae %an' |"; +while () { + chomp; + my ($mail, $author) = split(/\t/, $_); + next if exists $mailmap{$mail}; + $mail2author{$mail} ||= {}; + $mail2author{$mail}{$author} ||= 0; + $mail2author{$mail}{$author}++; +} +close I; + +while (my ($mail, $authorcount) = each %mail2author) { + # %$authorcount is ($author => $count); + # sort and show the names from the most frequent ones. + my @names = (map { $_->[0] } + sort { $b->[1] <=> $a->[1] } + map { [$_, $authorcount->{$_}] } + keys %$authorcount); + if (1 < @names) { + for (@names) { + print "$_ <$mail>\n"; + } + } +} + -- cgit v1.2.3 From 48b4c3d5ab1610c6dc0198fe94334d78e8a82e16 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Fri, 13 Jul 2007 14:39:05 +0200 Subject: Fix git-p4 on Windows to not use the Posix sysconf function. Add condition for Windows, since it doesn't support the os.sysconf module. We hardcode the commandline limit to 2K, as that should work on most Windows platforms. Signed-off-by: Marius Storm-Olsen Acked-by: Simon Hausmann Signed-off-by: Shawn O. Pearce --- contrib/fast-import/git-p4 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 54a05eb99c..d877150f41 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -717,7 +717,11 @@ class P4Sync(Command): # POSIX says it's 4096 bytes, default for Linux seems to be 130 K. # and all OS from the table below seems to be higher than POSIX. # See http://www.in-ulm.de/~mascheck/various/argmax/ - argmax = min(4000, os.sysconf('SC_ARG_MAX')) + if (self.isWindows): + argmax = 2000 + else: + argmax = min(4000, os.sysconf('SC_ARG_MAX')) + chunk = '' filedata = [] for i in xrange(len(files)): -- cgit v1.2.3 From 4cb08df553e270ab516a140caeeddd8e94f1e497 Mon Sep 17 00:00:00 2001 From: Emil Medve Date: Sat, 14 Jul 2007 12:51:44 -0500 Subject: Use $(RM) in Makefiles instead of 'rm -f' Signed-off-by: Emil Medve Signed-off-by: Junio C Hamano --- contrib/emacs/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile index 98aa0aae9b..5e94d6fcd3 100644 --- a/contrib/emacs/Makefile +++ b/contrib/emacs/Makefile @@ -7,6 +7,7 @@ INSTALL ?= install INSTALL_ELC = $(INSTALL) -m 644 prefix ?= $(HOME) emacsdir = $(prefix)/share/emacs/site-lisp +RM ?= rm -f all: $(ELC) @@ -17,4 +18,4 @@ install: all %.elc: %.el $(EMACS) -batch -f batch-byte-compile $< -clean:; rm -f $(ELC) +clean:; $(RM) $(ELC) -- cgit v1.2.3 From e3b4968f9caa04e83b2f27b64182187b13b86dab Mon Sep 17 00:00:00 2001 From: Sean Date: Sun, 15 Jul 2007 15:52:32 -0400 Subject: Demote git-p4import to contrib status. Move git-p4import.py and Documentation/git-p4import.txt into a contrib/p4import directory. Add a README there directing people to contrib/fast-import/git-p4 as a better alternative. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano --- contrib/p4import/README | 1 + contrib/p4import/git-p4import.py | 360 ++++++++++++++++++++++++++++++++++++++ contrib/p4import/git-p4import.txt | 167 ++++++++++++++++++ 3 files changed, 528 insertions(+) create mode 100644 contrib/p4import/README create mode 100644 contrib/p4import/git-p4import.py create mode 100644 contrib/p4import/git-p4import.txt (limited to 'contrib') diff --git a/contrib/p4import/README b/contrib/p4import/README new file mode 100644 index 0000000000..b9892b6793 --- /dev/null +++ b/contrib/p4import/README @@ -0,0 +1 @@ +Please see contrib/fast-import/git-p4 for a better Perforce importer. diff --git a/contrib/p4import/git-p4import.py b/contrib/p4import/git-p4import.py new file mode 100644 index 0000000000..0f3d97b67e --- /dev/null +++ b/contrib/p4import/git-p4import.py @@ -0,0 +1,360 @@ +#!/usr/bin/python +# +# This tool is copyright (c) 2006, Sean Estabrooks. +# It is released under the Gnu Public License, version 2. +# +# Import Perforce branches into Git repositories. +# Checking out the files is done by calling the standard p4 +# client which you must have properly configured yourself +# + +import marshal +import os +import sys +import time +import getopt + +from signal import signal, \ + SIGPIPE, SIGINT, SIG_DFL, \ + default_int_handler + +signal(SIGPIPE, SIG_DFL) +s = signal(SIGINT, SIG_DFL) +if s != default_int_handler: + signal(SIGINT, s) + +def die(msg, *args): + for a in args: + msg = "%s %s" % (msg, a) + print "git-p4import fatal error:", msg + sys.exit(1) + +def usage(): + print "USAGE: git-p4import [-q|-v] [--authors=] [-t ] [//p4repo/path ]" + sys.exit(1) + +verbosity = 1 +logfile = "/dev/null" +ignore_warnings = False +stitch = 0 +tagall = True + +def report(level, msg, *args): + global verbosity + global logfile + for a in args: + msg = "%s %s" % (msg, a) + fd = open(logfile, "a") + fd.writelines(msg) + fd.close() + if level <= verbosity: + print msg + +class p4_command: + def __init__(self, _repopath): + try: + global logfile + self.userlist = {} + if _repopath[-1] == '/': + self.repopath = _repopath[:-1] + else: + self.repopath = _repopath + if self.repopath[-4:] != "/...": + self.repopath= "%s/..." % self.repopath + f=os.popen('p4 -V 2>>%s'%logfile, 'rb') + a = f.readlines() + if f.close(): + raise + except: + die("Could not find the \"p4\" command") + + def p4(self, cmd, *args): + global logfile + cmd = "%s %s" % (cmd, ' '.join(args)) + report(2, "P4:", cmd) + f=os.popen('p4 -G %s 2>>%s' % (cmd,logfile), 'rb') + list = [] + while 1: + try: + list.append(marshal.load(f)) + except EOFError: + break + self.ret = f.close() + return list + + def sync(self, id, force=False, trick=False, test=False): + if force: + ret = self.p4("sync -f %s@%s"%(self.repopath, id))[0] + elif trick: + ret = self.p4("sync -k %s@%s"%(self.repopath, id))[0] + elif test: + ret = self.p4("sync -n %s@%s"%(self.repopath, id))[0] + else: + ret = self.p4("sync %s@%s"%(self.repopath, id))[0] + if ret['code'] == "error": + data = ret['data'].upper() + if data.find('VIEW') > 0: + die("Perforce reports %s is not in client view"% self.repopath) + elif data.find('UP-TO-DATE') < 0: + die("Could not sync files from perforce", self.repopath) + + def changes(self, since=0): + try: + list = [] + for rec in self.p4("changes %s@%s,#head" % (self.repopath, since+1)): + list.append(rec['change']) + list.reverse() + return list + except: + return [] + + def authors(self, filename): + f=open(filename) + for l in f.readlines(): + self.userlist[l[:l.find('=')].rstrip()] = \ + (l[l.find('=')+1:l.find('<')].rstrip(),l[l.find('<')+1:l.find('>')]) + f.close() + for f,e in self.userlist.items(): + report(2, f, ":", e[0], " <", e[1], ">") + + def _get_user(self, id): + if not self.userlist.has_key(id): + try: + user = self.p4("users", id)[0] + self.userlist[id] = (user['FullName'], user['Email']) + except: + self.userlist[id] = (id, "") + return self.userlist[id] + + def _format_date(self, ticks): + symbol='+' + name = time.tzname[0] + offset = time.timezone + if ticks[8]: + name = time.tzname[1] + offset = time.altzone + if offset < 0: + offset *= -1 + symbol = '-' + localo = "%s%02d%02d %s" % (symbol, offset / 3600, offset % 3600, name) + tickso = time.strftime("%a %b %d %H:%M:%S %Y", ticks) + return "%s %s" % (tickso, localo) + + def where(self): + try: + return self.p4("where %s" % self.repopath)[-1]['path'] + except: + return "" + + def describe(self, num): + desc = self.p4("describe -s", num)[0] + self.msg = desc['desc'] + self.author, self.email = self._get_user(desc['user']) + self.date = self._format_date(time.localtime(long(desc['time']))) + return self + +class git_command: + def __init__(self): + try: + self.version = self.git("--version")[0][12:].rstrip() + except: + die("Could not find the \"git\" command") + try: + self.gitdir = self.get_single("rev-parse --git-dir") + report(2, "gdir:", self.gitdir) + except: + die("Not a git repository... did you forget to \"git init\" ?") + try: + self.cdup = self.get_single("rev-parse --show-cdup") + if self.cdup != "": + os.chdir(self.cdup) + self.topdir = os.getcwd() + report(2, "topdir:", self.topdir) + except: + die("Could not find top git directory") + + def git(self, cmd): + global logfile + report(2, "GIT:", cmd) + f=os.popen('git %s 2>>%s' % (cmd,logfile), 'rb') + r=f.readlines() + self.ret = f.close() + return r + + def get_single(self, cmd): + return self.git(cmd)[0].rstrip() + + def current_branch(self): + try: + testit = self.git("rev-parse --verify HEAD")[0] + return self.git("symbolic-ref HEAD")[0][11:].rstrip() + except: + return None + + def get_config(self, variable): + try: + return self.git("config --get %s" % variable)[0].rstrip() + except: + return None + + def set_config(self, variable, value): + try: + self.git("config %s %s"%(variable, value) ) + except: + die("Could not set %s to " % variable, value) + + def make_tag(self, name, head): + self.git("tag -f %s %s"%(name,head)) + + def top_change(self, branch): + try: + a=self.get_single("name-rev --tags refs/heads/%s" % branch) + loc = a.find(' tags/') + 6 + if a[loc:loc+3] != "p4/": + raise + return int(a[loc+3:][:-2]) + except: + return 0 + + def update_index(self): + self.git("ls-files -m -d -o -z | git update-index --add --remove -z --stdin") + + def checkout(self, branch): + self.git("checkout %s" % branch) + + def repoint_head(self, branch): + self.git("symbolic-ref HEAD refs/heads/%s" % branch) + + def remove_files(self): + self.git("ls-files | xargs rm") + + def clean_directories(self): + self.git("clean -d") + + def fresh_branch(self, branch): + report(1, "Creating new branch", branch) + self.git("ls-files | xargs rm") + os.remove(".git/index") + self.repoint_head(branch) + self.git("clean -d") + + def basedir(self): + return self.topdir + + def commit(self, author, email, date, msg, id): + self.update_index() + fd=open(".msg", "w") + fd.writelines(msg) + fd.close() + try: + current = self.get_single("rev-parse --verify HEAD") + head = "-p HEAD" + except: + current = "" + head = "" + tree = self.get_single("write-tree") + for r,l in [('DATE',date),('NAME',author),('EMAIL',email)]: + os.environ['GIT_AUTHOR_%s'%r] = l + os.environ['GIT_COMMITTER_%s'%r] = l + commit = self.get_single("commit-tree %s %s < .msg" % (tree,head)) + os.remove(".msg") + self.make_tag("p4/%s"%id, commit) + self.git("update-ref HEAD %s %s" % (commit, current) ) + +try: + opts, args = getopt.getopt(sys.argv[1:], "qhvt:", + ["authors=","help","stitch=","timezone=","log=","ignore","notags"]) +except getopt.GetoptError: + usage() + +for o, a in opts: + if o == "-q": + verbosity = 0 + if o == "-v": + verbosity += 1 + if o in ("--log"): + logfile = a + if o in ("--notags"): + tagall = False + if o in ("-h", "--help"): + usage() + if o in ("--ignore"): + ignore_warnings = True + +git = git_command() +branch=git.current_branch() + +for o, a in opts: + if o in ("-t", "--timezone"): + git.set_config("perforce.timezone", a) + if o in ("--stitch"): + git.set_config("perforce.%s.path" % branch, a) + stitch = 1 + +if len(args) == 2: + branch = args[1] + git.checkout(branch) + if branch == git.current_branch(): + die("Branch %s already exists!" % branch) + report(1, "Setting perforce to ", args[0]) + git.set_config("perforce.%s.path" % branch, args[0]) +elif len(args) != 0: + die("You must specify the perforce //depot/path and git branch") + +p4path = git.get_config("perforce.%s.path" % branch) +if p4path == None: + die("Do not know Perforce //depot/path for git branch", branch) + +p4 = p4_command(p4path) + +for o, a in opts: + if o in ("-a", "--authors"): + p4.authors(a) + +localdir = git.basedir() +if p4.where()[:len(localdir)] != localdir: + report(1, "**WARNING** Appears p4 client is misconfigured") + report(1, " for sync from %s to %s" % (p4.repopath, localdir)) + if ignore_warnings != True: + die("Reconfigure or use \"--ignore\" on command line") + +if stitch == 0: + top = git.top_change(branch) +else: + top = 0 +changes = p4.changes(top) +count = len(changes) +if count == 0: + report(1, "Already up to date...") + sys.exit(0) + +ptz = git.get_config("perforce.timezone") +if ptz: + report(1, "Setting timezone to", ptz) + os.environ['TZ'] = ptz + time.tzset() + +if stitch == 1: + git.remove_files() + git.clean_directories() + p4.sync(changes[0], force=True) +elif top == 0 and branch != git.current_branch(): + p4.sync(changes[0], test=True) + report(1, "Creating new initial commit"); + git.fresh_branch(branch) + p4.sync(changes[0], force=True) +else: + p4.sync(changes[0], trick=True) + +report(1, "processing %s changes from p4 (%s) to git (%s)" % (count, p4.repopath, branch)) +for id in changes: + report(1, "Importing changeset", id) + change = p4.describe(id) + p4.sync(id) + if tagall : + git.commit(change.author, change.email, change.date, change.msg, id) + else: + git.commit(change.author, change.email, change.date, change.msg, "import") + if stitch == 1: + git.clean_directories() + stitch = 0 diff --git a/contrib/p4import/git-p4import.txt b/contrib/p4import/git-p4import.txt new file mode 100644 index 0000000000..9967587fe6 --- /dev/null +++ b/contrib/p4import/git-p4import.txt @@ -0,0 +1,167 @@ +git-p4import(1) +=============== + +NAME +---- +git-p4import - Import a Perforce repository into git + + +SYNOPSIS +-------- +[verse] +`git-p4import` [-q|-v] [--notags] [--authors ] [-t ] + +`git-p4import` --stitch +`git-p4import` + + +DESCRIPTION +----------- +Import a Perforce repository into an existing git repository. When +a and are specified a new branch with the +given name will be created and the initial import will begin. + +Once the initial import is complete you can do an incremental import +of new commits from the Perforce repository. You do this by checking +out the appropriate git branch and then running `git-p4import` without +any options. + +The standard p4 client is used to communicate with the Perforce +repository; it must be configured correctly in order for `git-p4import` +to operate (see below). + + +OPTIONS +------- +-q:: + Do not display any progress information. + +-v:: + Give extra progress information. + +\--authors:: + Specify an authors file containing a mapping of Perforce user + ids to full names and email addresses (see Notes below). + +\--notags:: + Do not create a tag for each imported commit. + +\--stitch:: + Import the contents of the given perforce branch into the + currently checked out git branch. + +\--log:: + Store debugging information in the specified file. + +-t:: + Specify that the remote repository is in the specified timezone. + Timezone must be in the format "US/Pacific" or "Europe/London" + etc. You only need to specify this once, it will be saved in + the git config file for the repository. + +:: + The Perforce path that will be imported into the specified branch. + +:: + The new branch that will be created to hold the Perforce imports. + + +P4 Client +--------- +You must make the `p4` client command available in your $PATH and +configure it to communicate with the target Perforce repository. +Typically this means you must set the "$P4PORT" and "$P4CLIENT" +environment variables. + +You must also configure a `p4` client "view" which maps the Perforce +branch into the top level of your git repository, for example: + +------------ +Client: myhost + +Root: /home/sean/import + +Options: noallwrite clobber nocompress unlocked modtime rmdir + +View: + //public/jam/... //myhost/jam/... +------------ + +With the above `p4` client setup, you could import the "jam" +perforce branch into a branch named "jammy", like so: + +------------ +$ mkdir -p /home/sean/import/jam +$ cd /home/sean/import/jam +$ git init +$ git p4import //public/jam jammy +------------ + + +Multiple Branches +----------------- +Note that by creating multiple "views" you can use `git-p4import` +to import additional branches into the same git repository. +However, the `p4` client has a limitation in that it silently +ignores all but the last "view" that maps into the same local +directory. So the following will *not* work: + +------------ +View: + //public/jam/... //myhost/jam/... + //public/other/... //myhost/jam/... + //public/guest/... //myhost/jam/... +------------ + +If you want more than one Perforce branch to be imported into the +same directory you must employ a workaround. A simple option is +to adjust your `p4` client before each import to only include a +single view. + +Another option is to create multiple symlinks locally which all +point to the same directory in your git repository and then use +one per "view" instead of listing the actual directory. + + +Tags +---- +A git tag of the form p4/xx is created for every change imported from +the Perforce repository where xx is the Perforce changeset number. +Therefore after the import you can use git to access any commit by its +Perforce number, e.g. git show p4/327. + +The tag associated with the HEAD commit is also how `git-p4import` +determines if there are new changes to incrementally import from the +Perforce repository. + +If you import from a repository with many thousands of changes +you will have an equal number of p4/xxxx git tags. Git tags can +be expensive in terms of disk space and repository operations. +If you don't need to perform further incremental imports, you +may delete the tags. + + +Notes +----- +You can interrupt the import (e.g. ctrl-c) at any time and restart it +without worry. + +Author information is automatically determined by querying the +Perforce "users" table using the id associated with each change. +However, if you want to manually supply these mappings you can do +so with the "--authors" option. It accepts a file containing a list +of mappings with each line containing one mapping in the format: + +------------ + perforce_id = Full Name +------------ + + +Author +------ +Written by Sean Estabrooks + + +GIT +--- +Part of the gitlink:git[7] suite -- cgit v1.2.3 From 99c01de402b543647a6500ceeaca7f62e343b144 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Sun, 15 Jul 2007 11:46:11 +0200 Subject: contrib/emacs/Makefile: Also install .el files. Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- contrib/emacs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile index 5e94d6fcd3..a48540a92b 100644 --- a/contrib/emacs/Makefile +++ b/contrib/emacs/Makefile @@ -13,7 +13,7 @@ all: $(ELC) install: all $(INSTALL) -d $(DESTDIR)$(emacsdir) - $(INSTALL_ELC) $(ELC) $(DESTDIR)$(emacsdir) + $(INSTALL_ELC) $(ELC:.elc=.el) $(ELC) $(DESTDIR)$(emacsdir) %.elc: %.el $(EMACS) -batch -f batch-byte-compile $< -- cgit v1.2.3 From 9f90c7335e223c26d19f3c01a6d89e6c0cd8b827 Mon Sep 17 00:00:00 2001 From: Scott Lamb Date: Sun, 15 Jul 2007 20:58:10 -0700 Subject: git-p4: use subprocess in p4CmdList This allows bidirectional piping - useful for "-x -" to avoid commandline arguments - and is a step toward bypassing the shell. Signed-off-by: Scott Lamb Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d877150f41..d93e656155 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -63,21 +63,34 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) -def p4CmdList(cmd): +def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: sys.stderr.write("Opening pipe: %s\n" % cmd) - pipe = os.popen(cmd, "rb") + + # Use a temporary file to avoid deadlocks without + # subprocess.communicate(), which would put another copy + # of stdout into memory. + stdin_file = None + if stdin is not None: + stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode) + stdin_file.write(stdin) + stdin_file.flush() + stdin_file.seek(0) + + p4 = subprocess.Popen(cmd, shell=True, + stdin=stdin_file, + stdout=subprocess.PIPE) result = [] try: while True: - entry = marshal.load(pipe) + entry = marshal.load(p4.stdout) result.append(entry) except EOFError: pass - exitCode = pipe.close() - if exitCode != None: + exitCode = p4.wait() + if exitCode != 0: entry = {} entry["p4ExitCode"] = exitCode result.append(entry) -- cgit v1.2.3 From 788001908c8960daa162c32baf3dc0f9ad1c16f8 Mon Sep 17 00:00:00 2001 From: Scott Lamb Date: Sun, 15 Jul 2007 20:58:11 -0700 Subject: git-p4: input to "p4 files" by stdin instead of arguments This approach, suggested by Alex Riesen, bypasses the need for xargs-style argument list handling. The handling in question looks broken in a corner case with SC_ARG_MAX=4096 and final argument over 96 characters. Signed-off-by: Scott Lamb Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d93e656155..54053e3f3d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -725,27 +725,13 @@ class P4Sync(Command): if not files: return - # We cannot put all the files on the command line - # OS have limitations on the max lenght of arguments - # POSIX says it's 4096 bytes, default for Linux seems to be 130 K. - # and all OS from the table below seems to be higher than POSIX. - # See http://www.in-ulm.de/~mascheck/various/argmax/ - if (self.isWindows): - argmax = 2000 - else: - argmax = min(4000, os.sysconf('SC_ARG_MAX')) - - chunk = '' - filedata = [] - for i in xrange(len(files)): - f = files[i] - chunk += '"%s#%s" ' % (f['path'], f['rev']) - if len(chunk) > argmax or i == len(files)-1: - data = p4CmdList('print %s' % chunk) - if "p4ExitCode" in data[0]: - die("Problems executing p4. Error: [%d]." % (data[0]['p4ExitCode'])); - filedata.extend(data) - chunk = '' + filedata = p4CmdList('-x - print', + stdin='\n'.join(['%s#%s' % (f['path'], f['rev']) + for f in files]), + stdin_mode='w+') + if "p4ExitCode" in filedata[0]: + die("Problems executing p4. Error: [%d]." + % (filedata[0]['p4ExitCode'])); j = 0; contents = {} -- cgit v1.2.3 From 062410bb9d6553e6bc4f8fa7f0cab1ed63b75628 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 18 Jul 2007 10:56:31 +0200 Subject: git-p4: Cleanup, make listExistingP4Branches a global function for later use. Signed-off-by: Simon Hausmann Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 54053e3f3d..d4a2f14311 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -181,6 +181,29 @@ def gitBranchExists(branch): def gitConfig(key): return read_pipe("git config %s" % key, ignore_error=True).strip() +def p4BranchesInGit(branchesAreInRemotes = True): + branches = {} + + cmdline = "git rev-parse --symbolic " + if branchesAreInRemotes: + cmdline += " --remotes" + else: + cmdline += " --branches" + + for line in read_pipe_lines(cmdline): + line = line.strip() + + ## only import to p4/ + if not line.startswith('p4/') or line == "p4/HEAD": + continue + branch = line + + # strip off p4 + branch = re.sub ("^p4/", "", line) + + branches[branch] = parseRevision(line) + return branches + def findUpstreamBranchPoint(head = "HEAD"): settings = None branchPoint = "" -- cgit v1.2.3 From 86506fe54c8e9e5f75a73956840497e3e5744f86 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 18 Jul 2007 12:40:12 +0200 Subject: git-p4: Fix upstream branch detection for submit/rebase with multiple branches. Don't use git name-rev to locate the upstream git-p4 branch for rebase and submit but instead locate the branch by comparing the depot paths. name-rev may produce results like wrongbranch~12 as it uses the first match. Signed-off-by: Simon Hausmann Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d4a2f14311..a65f53a47b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -205,26 +205,31 @@ def p4BranchesInGit(branchesAreInRemotes = True): return branches def findUpstreamBranchPoint(head = "HEAD"): + branches = p4BranchesInGit() + # map from depot-path to branch name + branchByDepotPath = {} + for branch in branches.keys(): + tip = branches[branch] + log = extractLogMessageFromGitCommit(tip) + settings = extractSettingsGitLog(log) + if settings.has_key("depot-paths"): + paths = ",".join(settings["depot-paths"]) + branchByDepotPath[paths] = "remotes/p4/" + branch + settings = None - branchPoint = "" parent = 0 while parent < 65535: commit = head + "~%s" % parent log = extractLogMessageFromGitCommit(commit) settings = extractSettingsGitLog(log) - if not settings.has_key("depot-paths"): - parent = parent + 1 - continue - - names = read_pipe_lines("git name-rev \"--refs=refs/remotes/p4/*\" \"%s\"" % commit) - if len(names) <= 0: - continue + if settings.has_key("depot-paths"): + paths = ",".join(settings["depot-paths"]) + if branchByDepotPath.has_key(paths): + return [branchByDepotPath[paths], settings] - # strip away the beginning of 'HEAD~42 refs/remotes/p4/foo' - branchPoint = names[0].strip()[len(commit) + 1:] - break + parent = parent + 1 - return [branchPoint, settings] + return ["", settings] class Command: def __init__(self): -- cgit v1.2.3 From 144ff46b196e49fd52b2ecf0aaa1db4c190393b9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 18 Jul 2007 17:27:50 +0200 Subject: git-p4: Cleanup, used common function for listing imported p4 branches Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a65f53a47b..e3404ca853 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1006,27 +1006,11 @@ class P4Sync(Command): self.knownBranches[branch] = branch def listExistingP4GitBranches(self): - self.p4BranchesInGit = [] - - cmdline = "git rev-parse --symbolic " - if self.importIntoRemotes: - cmdline += " --remotes" - else: - cmdline += " --branches" - - for line in read_pipe_lines(cmdline): - line = line.strip() - - ## only import to p4/ - if not line.startswith('p4/') or line == "p4/HEAD": - continue - branch = line - - # strip off p4 - branch = re.sub ("^p4/", "", line) - - self.p4BranchesInGit.append(branch) - self.initialParents[self.refPrefix + branch] = parseRevision(line) + # branches holds mapping from name to commit + branches = p4BranchesInGit(self.importIntoRemotes) + self.p4BranchesInGit = branches.keys() + for branch in branches.keys(): + self.initialParents[self.refPrefix + branch] = branches[branch] def createOrUpdateBranchesFromOrigin(self): if not self.silent: -- cgit v1.2.3 From 62e09ce998dd7f6b844deb650101c743a5c4ce50 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Fri, 20 Jul 2007 01:42:28 +0200 Subject: Make git tag a builtin. This replaces the script "git-tag.sh" with "builtin-tag.c". The existing test suite for "git tag" guarantees the compatibility with the features provided by the script version. There are some minor changes in the behaviour of "git tag" here: "git tag -v" now can get more than one tag to verify, like "git tag -d" does, "git tag" with no arguments prints all tags, more like "git branch" does, and "git tag -n" also prints all tags with annotations (without needing -l). Tests and documentation were also updated to reflect these changes. The program is currently calling the script "git verify-tag" for verify. This can be changed porting it to C and calling its functions directly from builtin-tag.c. Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- contrib/examples/git-tag.sh | 205 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100755 contrib/examples/git-tag.sh (limited to 'contrib') diff --git a/contrib/examples/git-tag.sh b/contrib/examples/git-tag.sh new file mode 100755 index 0000000000..5ee3f50a3c --- /dev/null +++ b/contrib/examples/git-tag.sh @@ -0,0 +1,205 @@ +#!/bin/sh +# Copyright (c) 2005 Linus Torvalds + +USAGE='[-n []] -l [] | [-a | -s | -u ] [-f | -d | -v] [-m ] []' +SUBDIRECTORY_OK='Yes' +. git-sh-setup + +message_given= +annotate= +signed= +force= +message= +username= +list= +verify= +LINES=0 +while case "$#" in 0) break ;; esac +do + case "$1" in + -a) + annotate=1 + shift + ;; + -s) + annotate=1 + signed=1 + shift + ;; + -f) + force=1 + shift + ;; + -n) + case "$#,$2" in + 1,* | *,-*) + LINES=1 # no argument + ;; + *) shift + LINES=$(expr "$1" : '\([0-9]*\)') + [ -z "$LINES" ] && LINES=1 # 1 line is default when -n is used + ;; + esac + shift + ;; + -l) + list=1 + shift + case $# in + 0) PATTERN= + ;; + *) + PATTERN="$1" # select tags by shell pattern, not re + shift + ;; + esac + git rev-parse --symbolic --tags | sort | + while read TAG + do + case "$TAG" in + *$PATTERN*) ;; + *) continue ;; + esac + [ "$LINES" -le 0 ] && { echo "$TAG"; continue ;} + OBJTYPE=$(git cat-file -t "$TAG") + case $OBJTYPE in + tag) + ANNOTATION=$(git cat-file tag "$TAG" | + sed -e '1,/^$/d' | + sed -n -e " + /^-----BEGIN PGP SIGNATURE-----\$/q + 2,\$s/^/ / + p + ${LINES}q + ") + printf "%-15s %s\n" "$TAG" "$ANNOTATION" + ;; + *) echo "$TAG" + ;; + esac + done + ;; + -m) + annotate=1 + shift + message="$1" + if test "$#" = "0"; then + die "error: option -m needs an argument" + else + message="$1" + message_given=1 + shift + fi + ;; + -F) + annotate=1 + shift + if test "$#" = "0"; then + die "error: option -F needs an argument" + else + message="$(cat "$1")" + message_given=1 + shift + fi + ;; + -u) + annotate=1 + signed=1 + shift + if test "$#" = "0"; then + die "error: option -u needs an argument" + else + username="$1" + shift + fi + ;; + -d) + shift + had_error=0 + for tag + do + cur=$(git show-ref --verify --hash -- "refs/tags/$tag") || { + echo >&2 "Seriously, what tag are you talking about?" + had_error=1 + continue + } + git update-ref -m 'tag: delete' -d "refs/tags/$tag" "$cur" || { + had_error=1 + continue + } + echo "Deleted tag $tag." + done + exit $had_error + ;; + -v) + shift + tag_name="$1" + tag=$(git show-ref --verify --hash -- "refs/tags/$tag_name") || + die "Seriously, what tag are you talking about?" + git-verify-tag -v "$tag" + exit $? + ;; + -*) + usage + ;; + *) + break + ;; + esac +done + +[ -n "$list" ] && exit 0 + +name="$1" +[ "$name" ] || usage +prev=0000000000000000000000000000000000000000 +if git show-ref --verify --quiet -- "refs/tags/$name" +then + test -n "$force" || die "tag '$name' already exists" + prev=`git rev-parse "refs/tags/$name"` +fi +shift +git check-ref-format "tags/$name" || + die "we do not like '$name' as a tag name." + +object=$(git rev-parse --verify --default HEAD "$@") || exit 1 +type=$(git cat-file -t $object) || exit 1 +tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1 + +test -n "$username" || + username=$(git repo-config user.signingkey) || + username=$(expr "z$tagger" : 'z\(.*>\)') + +trap 'rm -f "$GIT_DIR"/TAG_TMP* "$GIT_DIR"/TAG_FINALMSG "$GIT_DIR"/TAG_EDITMSG' 0 + +if [ "$annotate" ]; then + if [ -z "$message_given" ]; then + ( echo "#" + echo "# Write a tag message" + echo "#" ) > "$GIT_DIR"/TAG_EDITMSG + git_editor "$GIT_DIR"/TAG_EDITMSG || exit + else + printf '%s\n' "$message" >"$GIT_DIR"/TAG_EDITMSG + fi + + grep -v '^#' <"$GIT_DIR"/TAG_EDITMSG | + git stripspace >"$GIT_DIR"/TAG_FINALMSG + + [ -s "$GIT_DIR"/TAG_FINALMSG -o -n "$message_given" ] || { + echo >&2 "No tag message?" + exit 1 + } + + ( printf 'object %s\ntype %s\ntag %s\ntagger %s\n\n' \ + "$object" "$type" "$name" "$tagger"; + cat "$GIT_DIR"/TAG_FINALMSG ) >"$GIT_DIR"/TAG_TMP + rm -f "$GIT_DIR"/TAG_TMP.asc "$GIT_DIR"/TAG_FINALMSG + if [ "$signed" ]; then + gpg -bsa -u "$username" "$GIT_DIR"/TAG_TMP && + cat "$GIT_DIR"/TAG_TMP.asc >>"$GIT_DIR"/TAG_TMP || + die "failed to sign the tag with GPG." + fi + object=$(git-mktag < "$GIT_DIR"/TAG_TMP) +fi + +git update-ref "refs/tags/$name" "$object" "$prev" -- cgit v1.2.3 From 93c22eeb30d2f43fc1e7a397e71f9bf8fb767962 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 24 Jul 2007 12:12:47 +0200 Subject: git.el: Support for incremental status updates. When we know which files have been modified, we can now run diff-index or ls-files with a file list to refresh only the specified files instead of the whole project. This also allows proper refreshing of files upon add/delete/resolve, instead of making assumptions about the new file state. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 224 ++++++++++++++++++++++++++------------------------- 1 file changed, 113 insertions(+), 111 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 457f95fc05..b92bbe8728 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -314,8 +314,8 @@ and returns the process output as a string." (sort-lines nil (point-min) (point-max)) (save-buffer)) (when created - (git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name))) - (git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name)))) + (git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) + (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) ; propertize definition for XEmacs, stolen from erc-compat (eval-when-compile @@ -523,23 +523,39 @@ and returns the process output as a string." " " (git-escape-file-name (git-fileinfo->name info)) (git-rename-as-string info)))) -(defun git-parse-status (status) - "Parse the output of git-diff-index in the current buffer." - (goto-char (point-min)) - (while (re-search-forward - ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" - nil t 1) - (let ((old-perm (string-to-number (match-string 1) 8)) - (new-perm (string-to-number (match-string 2) 8)) - (state (or (match-string 4) (match-string 6))) - (name (or (match-string 5) (match-string 7))) - (new-name (match-string 8))) - (if new-name ; copy or rename - (if (eq ?C (string-to-char state)) - (ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name)) - (ewoc-enter-last status (git-create-fileinfo 'deleted name 0 0 'rename new-name)) - (ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name))) - (ewoc-enter-last status (git-create-fileinfo (git-state-code state) name old-perm new-perm)))))) +(defun git-insert-fileinfo (status info &optional refresh) + "Insert INFO in the status buffer, optionally refreshing an existing one." + (let ((node (and refresh + (git-find-status-file status (git-fileinfo->name info))))) + (setf (git-fileinfo->needs-refresh info) t) + (when node ;preserve the marked flag + (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))) + (if node (ewoc-set-data node info) (ewoc-enter-last status info)))) + +(defun git-run-diff-index (status files) + "Run git-diff-index on FILES and parse the results into STATUS. +Return the list of files that haven't been handled." + (let ((refresh files)) + (with-temp-buffer + (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files) + (goto-char (point-min)) + (while (re-search-forward + ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" + nil t 1) + (let ((old-perm (string-to-number (match-string 1) 8)) + (new-perm (string-to-number (match-string 2) 8)) + (state (or (match-string 4) (match-string 6))) + (name (or (match-string 5) (match-string 7))) + (new-name (match-string 8))) + (if new-name ; copy or rename + (if (eq ?C (string-to-char state)) + (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh) + (git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh) + (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh) + (git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh)) + (setq files (delete name files)) + (when new-name (setq files (delete new-name files))))))) + files) (defun git-find-status-file (status file) "Find a given file in the status ewoc and return its node." @@ -548,32 +564,59 @@ and returns the process output as a string." (setq node (ewoc-next status node))) node)) -(defun git-parse-ls-files (status default-state &optional skip-existing) - "Parse the output of git-ls-files in the current buffer." - (goto-char (point-min)) - (let (infolist) - (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1) - (let ((state (match-string 1)) - (name (match-string 2))) - (unless (and skip-existing (git-find-status-file status name)) - (push (git-create-fileinfo (or (git-state-code state) default-state) name) infolist)))) - (dolist (info (nreverse infolist)) - (ewoc-enter-last status info)))) - -(defun git-parse-ls-unmerged (status) - "Parse the output of git-ls-files -u in the current buffer." - (goto-char (point-min)) - (let (files) - (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) - (let ((node (git-find-status-file status (match-string 1)))) - (when node (push (ewoc-data node) files)))) - (git-set-files-state files 'unmerged))) - -(defun git-add-status-file (state name) - "Add a new file to the status list (if not existing already) and return its node." +(defun git-run-ls-files (status files default-state &rest options) + "Run git-ls-files on FILES and parse the results into STATUS. +Return the list of files that haven't been handled." + (let ((refresh files)) + (with-temp-buffer + (apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files)) + (goto-char (point-min)) + (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1) + (let ((state (match-string 1)) + (name (match-string 2))) + (git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh) + (setq files (delete name files)))))) + files) + +(defun git-run-ls-unmerged (status files) + "Run git-ls-files -u on FILES and parse the results into STATUS." + (with-temp-buffer + (apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files) + (goto-char (point-min)) + (let (unmerged-files) + (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) + (let ((node (git-find-status-file status (match-string 1)))) + (when node (push (ewoc-data node) unmerged-files)))) + (git-set-files-state unmerged-files 'unmerged)))) + +(defun git-update-status-files (files &optional default-state) + "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) - (or (git-find-status-file git-status name) - (ewoc-enter-last git-status (git-create-fileinfo state name)))) + (let* ((status git-status) + (remaining-files + (if (git-empty-db-p) ; we need some special handling for an empty db + (git-run-ls-files status files 'added "-c") + (git-run-diff-index status files)))) + (git-run-ls-unmerged status files) + (when (and (or (not files) remaining-files) + (file-readable-p ".git/info/exclude")) + (setq remaining-files (git-run-ls-files status remaining-files + 'unknown "-o" "--exclude-from=.git/info/exclude" + (concat "--exclude-per-directory=" git-per-dir-ignore-file)))) + ; mark remaining files with the default state (or remove them if nil) + (when remaining-files + (if default-state + (ewoc-map (lambda (info) + (when (member (git-fileinfo->name info) remaining-files) + (git-set-files-state (list info) default-state)) + nil) + status) + (ewoc-filter status + (lambda (info files) + (not (member (git-fileinfo->name info) files))) + remaining-files))) + (git-refresh-files) + (git-refresh-ewoc-hf status))) (defun git-marked-files () "Return a list of all marked files, or if none a list containing just the file at cursor position." @@ -789,54 +832,34 @@ and returns the process output as a string." (defun git-add-file () "Add marked file(s) to the index cache." (interactive) - (let ((files (git-marked-files-state 'unknown))) + (let ((files (git-get-filenames (git-marked-files-state 'unknown)))) (unless files - (push (ewoc-data - (git-add-status-file 'added (file-relative-name - (read-file-name "File to add: " nil nil t)))) - files)) - (apply #'git-run-command nil nil "update-index" "--info-only" "--add" "--" (git-get-filenames files)) - (git-set-files-state files 'added) - (git-refresh-files))) + (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) + (apply #'git-run-command nil nil "update-index" "--add" "--" files) + (git-update-status-files files 'uptodate))) (defun git-ignore-file () "Add marked file(s) to the ignore list." (interactive) - (let ((files (git-marked-files-state 'unknown))) + (let ((files (git-get-filenames (git-marked-files-state 'unknown)))) (unless files - (push (ewoc-data - (git-add-status-file 'unknown (file-relative-name - (read-file-name "File to ignore: " nil nil t)))) - files)) - (dolist (info files) (git-append-to-ignore (git-fileinfo->name info))) - (git-set-files-state files 'ignored) - (git-refresh-files))) + (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files)) + (dolist (f files) (git-append-to-ignore f)) + (git-update-status-files files 'ignored))) (defun git-remove-file () "Remove the marked file(s)." (interactive) - (let ((files (git-marked-files-state 'added 'modified 'unknown 'uptodate))) + (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate)))) (unless files - (push (ewoc-data - (git-add-status-file 'unknown (file-relative-name - (read-file-name "File to remove: " nil nil t)))) - files)) + (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files)) (if (yes-or-no-p (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" ""))) (progn - (dolist (info files) - (let ((name (git-fileinfo->name info))) - (when (file-exists-p name) (delete-file name)))) - (apply #'git-run-command nil nil "update-index" "--info-only" "--remove" "--" (git-get-filenames files)) - ; remove unknown files from the list, set the others to deleted - (ewoc-filter git-status - (lambda (info files) - (not (and (memq info files) (eq (git-fileinfo->state info) 'unknown)))) - files) - (git-set-files-state files 'deleted) - (git-refresh-files) - (unless (ewoc-nth git-status 0) ; refresh header if list is empty - (git-refresh-ewoc-hf git-status))) + (dolist (name files) + (when (file-exists-p name) (delete-file name))) + (apply #'git-run-command nil nil "update-index" "--remove" "--" files) + (git-update-status-files files nil)) (message "Aborting")))) (defun git-revert-file () @@ -849,26 +872,23 @@ and returns the process output as a string." (format "Revert %d file%s? " (length files) (if (> (length files) 1) "s" "")))) (dolist (info files) (case (git-fileinfo->state info) - ('added (push info added)) - ('deleted (push info modified)) - ('unmerged (push info modified)) - ('modified (push info modified)))) + ('added (push (git-fileinfo->name info) added)) + ('deleted (push (git-fileinfo->name info) modified)) + ('unmerged (push (git-fileinfo->name info) modified)) + ('modified (push (git-fileinfo->name info) modified)))) (when added - (apply #'git-run-command nil nil "update-index" "--force-remove" "--" (git-get-filenames added)) - (git-set-files-state added 'unknown)) + (apply #'git-run-command nil nil "update-index" "--force-remove" "--" added)) (when modified - (apply #'git-run-command nil nil "checkout" "HEAD" (git-get-filenames modified)) - (git-set-files-state modified 'uptodate)) - (git-refresh-files)))) + (apply #'git-run-command nil nil "checkout" "HEAD" modified)) + (git-update-status-files (append added modified) 'uptodate)))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." (interactive) - (let ((files (git-marked-files-state 'unmerged))) + (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files - (apply #'git-run-command nil nil "update-index" "--" (git-get-filenames files)) - (git-set-files-state files 'modified) - (git-refresh-files)))) + (apply #'git-run-command nil nil "update-index" "--" files) + (git-update-status-files files 'uptodate)))) (defun git-remove-handled () "Remove handled files from the status list." @@ -1071,27 +1091,9 @@ and returns the process output as a string." (pos (ewoc-locate status)) (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) + (git-run-command nil nil "update-index" "--refresh") (git-clear-status status) - (git-run-command nil nil "update-index" "--info-only" "--refresh") - (if (git-empty-db-p) - ; we need some special handling for an empty db - (with-temp-buffer - (git-run-command t nil "ls-files" "-z" "-t" "-c") - (git-parse-ls-files status 'added)) - (with-temp-buffer - (git-run-command t nil "diff-index" "-z" "-M" "HEAD") - (git-parse-status status))) - (with-temp-buffer - (git-run-command t nil "ls-files" "-z" "-u") - (git-parse-ls-unmerged status)) - (when (file-readable-p ".git/info/exclude") - (with-temp-buffer - (git-run-command t nil "ls-files" "-z" "-t" "-o" - "--exclude-from=.git/info/exclude" - (concat "--exclude-per-directory=" git-per-dir-ignore-file)) - (git-parse-ls-files status 'unknown))) - (git-refresh-files) - (git-refresh-ewoc-hf status) + (git-update-status-files nil) ; move point to the current file name if any (let ((node (and cur-name (git-find-status-file status cur-name)))) (when node (ewoc-goto-node status node))))) -- cgit v1.2.3 From ceefa44fe2d72f73548c4c36b42916b9c60ae16b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 24 Jul 2007 12:02:28 +0200 Subject: git.el: Pass an explicit argument to enable smerge-mode. Without argument the mode is toggled, which would do the wrong thing if the file was already open. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index b92bbe8728..53dd703260 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1058,7 +1058,7 @@ Return the list of files that haven't been handled." (let ((info (ewoc-data (ewoc-locate git-status)))) (find-file (git-fileinfo->name info)) (when (eq 'unmerged (git-fileinfo->state info)) - (smerge-mode)))) + (smerge-mode 1)))) (defun git-find-file-other-window () "Visit the current file in its own buffer in another window." -- cgit v1.2.3 From b2d2d16af7ff686fb96d344daaf49653fd67366a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 25 Jul 2007 09:31:38 +0200 Subject: git-p4: Fix p4 user cache population on Windows. Fall back to USERPROFILE if HOME isn't set. Signed-off-by: Simon Hausmann Signed-off-by: Marius Storm-Olsen Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e3404ca853..1f5a56ee7f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -901,7 +901,8 @@ class P4Sync(Command): % (labelDetails["label"], change)) def getUserCacheFilename(self): - return os.environ["HOME"] + "/.gitp4-usercache.txt" + home = os.environ.get("HOME", os.environ.get("USERPROFILE")) + return home + "/.gitp4-usercache.txt" def getUserMapFromPerforceServer(self): if self.userMapFromPerforceServer: -- cgit v1.2.3 From 2ae68fcb785a617793813abcea19893e13e436b0 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Fri, 27 Jul 2007 06:07:34 +0200 Subject: Make verify-tag a builtin. This replaces "git-verify-tag.sh" with "builtin-verify-tag.c". Testing relies on the "git tag -v" tests calling this command. A temporary file is needed when calling to gpg, because git is already creating detached signatures (gpg option -b) to sign tags (instead of leaving gpg to add the signature to the file by itself), and those signatures need to be supplied in a separate file to be verified by gpg. The program uses git_mkstemp to create that temporary file needed by gpg, instead of the previously used "$GIT_DIR/.tmp-vtag", in order to allow the command to be used in read-only repositories, and also prevent other instances of git to read or remove the same file. Signal SIGPIPE is ignored because the program sometimes was terminated because that signal when writing the input for gpg. The command now can receive many tag names to be verified. Documentation is also updated here to reflect this new behaviour. Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- contrib/examples/git-verify-tag.sh | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100755 contrib/examples/git-verify-tag.sh (limited to 'contrib') diff --git a/contrib/examples/git-verify-tag.sh b/contrib/examples/git-verify-tag.sh new file mode 100755 index 0000000000..37b0023b27 --- /dev/null +++ b/contrib/examples/git-verify-tag.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +USAGE='' +SUBDIRECTORY_OK='Yes' +. git-sh-setup + +verbose= +while case $# in 0) break;; esac +do + case "$1" in + -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) + verbose=t ;; + *) + break ;; + esac + shift +done + +if [ "$#" != "1" ] +then + usage +fi + +type="$(git cat-file -t "$1" 2>/dev/null)" || + die "$1: no such object." + +test "$type" = tag || + die "$1: cannot verify a non-tag object of type $type." + +case "$verbose" in +t) + git cat-file -p "$1" | + sed -n -e '/^-----BEGIN PGP SIGNATURE-----/q' -e p + ;; +esac + +trap 'rm -f "$GIT_DIR/.tmp-vtag"' 0 + +git cat-file tag "$1" >"$GIT_DIR/.tmp-vtag" || exit 1 +sed -n -e ' + /^-----BEGIN PGP SIGNATURE-----$/q + p +' <"$GIT_DIR/.tmp-vtag" | +gpg --verify "$GIT_DIR/.tmp-vtag" - || exit 1 +rm -f "$GIT_DIR/.tmp-vtag" -- cgit v1.2.3 From 61988f1127587f8597b8b41da78a65717851e1fa Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 31 Jul 2007 23:03:41 -0700 Subject: git.el: Avoid using ewoc-set-data for compatibility with Emacs 21. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexandre Julliard Acked-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 53dd703260..7470f13185 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -530,7 +530,7 @@ and returns the process output as a string." (setf (git-fileinfo->needs-refresh info) t) (when node ;preserve the marked flag (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))) - (if node (ewoc-set-data node info) (ewoc-enter-last status info)))) + (if node (setf (ewoc-data node) info) (ewoc-enter-last status info)))) (defun git-run-diff-index (status files) "Run git-diff-index on FILES and parse the results into STATUS. -- cgit v1.2.3 From 274e13e0e9c7e475bbc342a10d614dd40b0e4c15 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 31 Jul 2007 12:36:32 +0200 Subject: git.el: Take into account the core.excludesfile config option. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also don't require .git/info/exclude to exist in order to list unknown files. Signed-off-by: Alexandre Julliard Acked-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 7470f13185..f6102fc344 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -589,6 +589,16 @@ Return the list of files that haven't been handled." (when node (push (ewoc-data node) unmerged-files)))) (git-set-files-state unmerged-files 'unmerged)))) +(defun git-get-exclude-files () + "Get the list of exclude files to pass to git-ls-files." + (let (files + (config (git-config "core.excludesfile"))) + (when (file-readable-p ".git/info/exclude") + (push ".git/info/exclude" files)) + (when (and config (file-readable-p config)) + (push config files)) + files)) + (defun git-update-status-files (files &optional default-state) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) @@ -598,11 +608,11 @@ Return the list of files that haven't been handled." (git-run-ls-files status files 'added "-c") (git-run-diff-index status files)))) (git-run-ls-unmerged status files) - (when (and (or (not files) remaining-files) - (file-readable-p ".git/info/exclude")) - (setq remaining-files (git-run-ls-files status remaining-files - 'unknown "-o" "--exclude-from=.git/info/exclude" - (concat "--exclude-per-directory=" git-per-dir-ignore-file)))) + (when (or (not files) remaining-files) + (let ((exclude-files (git-get-exclude-files))) + (setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o" + (concat "--exclude-per-directory=" git-per-dir-ignore-file) + (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) ; mark remaining files with the default state (or remove them if nil) (when remaining-files (if default-state -- cgit v1.2.3 From a4eba020f96e1b85f856a992782b8bb2bab6a719 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Mon, 23 Jul 2007 15:51:49 -0700 Subject: Sort output of "p4 change" in incremental import before further processing P4 change outputs the changes sorted for each directory separately. We want the global ordering on the changes, hence we sort. Signed-off-by: Han-Wen Nienhuys Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1f5a56ee7f..f00c691a7b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1296,7 +1296,7 @@ class P4Sync(Command): changeNum = line.split(" ")[1] changes.append(changeNum) - changes.reverse() + changes.sort() if len(self.maxChanges) > 0: changes = changes[0:min(int(self.maxChanges), len(changes))] -- cgit v1.2.3 From 7fcff9def564fae7de5d2a34a095229a999b80b0 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Mon, 23 Jul 2007 15:56:37 -0700 Subject: Fix style nit in Python slicing. Python slices start at 0 by default. Signed-off-by: Han-Wen Nienhuys Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f00c691a7b..41e86e76cb 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1181,11 +1181,11 @@ class P4Sync(Command): elif ',' not in self.changeRange: self.revision = self.changeRange self.changeRange = "" - p = p[0:atIdx] + p = p[:atIdx] elif p.find("#") != -1: hashIdx = p.index("#") self.revision = p[hashIdx:] - p = p[0:hashIdx] + p = p[:hashIdx] elif self.previousDepotPaths == []: self.revision = "#head" @@ -1299,7 +1299,7 @@ class P4Sync(Command): changes.sort() if len(self.maxChanges) > 0: - changes = changes[0:min(int(self.maxChanges), len(changes))] + changes = changes[:min(int(self.maxChanges), len(changes))] if len(changes) == 0: if not self.silent: -- cgit v1.2.3 From 7fd53fce1c574f6a4940eedf36383a4e9ed7ae6a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 02:04:37 -0700 Subject: git-completion: add "git stash" This is a new addition to 1.5.3; let's teach it to the completion before the final release. [sp: Added missing git-stash completion configuration] Signed-off-by: Junio C Hamano Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f2b10fa5f6..82b9ed40d8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -972,6 +972,11 @@ _git_show () __git_complete_file } +_git_stash () +{ + __gitcomp 'list show apply clear' +} + _git () { local i c=1 command __git_dir @@ -1028,6 +1033,7 @@ _git () shortlog) _git_shortlog ;; show) _git_show ;; show-branch) _git_log ;; + stash) _git_stash ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; esac @@ -1073,6 +1079,7 @@ complete -o default -o nospace -F _git_remote git-remote complete -o default -o nospace -F _git_reset git-reset complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show +complete -o default -o nospace -F _git_stash git-stash complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged -- cgit v1.2.3 From 2ec39edad9c3be74b5fed5ab5c122493e6a53c66 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 20:19:09 -0700 Subject: INSTALL: add warning on docbook-xsl 1.72 and 1.73 Signed-off-by: Junio C Hamano --- contrib/patches/docbook-xsl-manpages-charmap.patch | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 contrib/patches/docbook-xsl-manpages-charmap.patch (limited to 'contrib') diff --git a/contrib/patches/docbook-xsl-manpages-charmap.patch b/contrib/patches/docbook-xsl-manpages-charmap.patch new file mode 100644 index 0000000000..f2b08b4f4a --- /dev/null +++ b/contrib/patches/docbook-xsl-manpages-charmap.patch @@ -0,0 +1,21 @@ +From: Ismail Dönmez + +Trying to build the documentation with docbook-xsl 1.73 may result in +the following error. This patch fixes it. + +$ xmlto -m callouts.xsl man git-add.xml +runtime error: file +file:///usr/share/sgml/docbook/xsl-stylesheets-1.73.0/manpages/other.xsl line +129 element call-template +The called template 'read-character-map' was not found. + +--- docbook-xsl-1.73.0/manpages/docbook.xsl.manpages-charmap 2007-07-23 16:24:23.000000000 +0100 ++++ docbook-xsl-1.73.0/manpages/docbook.xsl 2007-07-23 16:25:16.000000000 +0100 +@@ -37,6 +37,7 @@ + + + ++ + + + -- cgit v1.2.3 From 74276ec6f2d2123714066cee24b26a629930e7a8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 7 Aug 2007 12:28:00 +0200 Subject: git-p4: Fix support for symlinks. Detect symlinks as file type, set the git file mode accordingly and strip off the trailing newline in the p4 print output. Make the mode handling a bit more readable at the same time. Signed-off-by: Simon Hausmann Acked-by: Brian Swetland Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 41e86e76cb..3cbb2da221 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -839,16 +839,20 @@ class P4Sync(Command): if file["action"] == "delete": self.gitStream.write("D %s\n" % relPath) else: - mode = 644 - if file["type"].startswith("x"): - mode = 755 - data = file['data'] + mode = "644" + if file["type"].startswith("x"): + mode = "755" + elif file["type"] == "symlink": + mode = "120000" + # p4 print on a symlink contains "target\n", so strip it off + data = data[:-1] + if self.isWindows and file["type"].endswith("text"): data = data.replace("\r\n", "\n") - self.gitStream.write("M %d inline %s\n" % (mode, relPath)) + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) self.gitStream.write("data %s\n" % len(data)) self.gitStream.write(data) self.gitStream.write("\n") -- cgit v1.2.3 From ea99c3ae0e2cecaa0950532385319d60c59e97e0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 8 Aug 2007 17:06:55 +0200 Subject: git-p4: Fix git-p4 submit to include only changed files in the perforce submit template. Parse the files section in the "p4 change -o" output and remove lines with file changes in unrelated depot paths. Signed-off-by: Simon Hausmann Signed-off-by: Marius Storm-Olsen Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3cbb2da221..805d632a68 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -390,6 +390,30 @@ class P4Submit(Command): return result + def prepareSubmitTemplate(self): + # remove lines in the Files section that show changes to files outside the depot path we're committing into + template = "" + inFilesSection = False + for line in read_pipe_lines("p4 change -o"): + if inFilesSection: + if line.startswith("\t"): + # path starts and ends with a tab + path = line[1:] + lastTab = path.rfind("\t") + if lastTab != -1: + path = path[:lastTab] + if not path.startswith(self.depotPath): + continue + else: + inFilesSection = False + else: + if line.startswith("Files:"): + inFilesSection = True + + template += line + + return template + def applyCommit(self, id): if self.directSubmit: print "Applying local change in working directory/index" @@ -467,7 +491,7 @@ class P4Submit(Command): logMessage = logMessage.replace("\n", "\r\n") logMessage = logMessage.strip() - template = read_pipe("p4 change -o") + template = self.prepareSubmitTemplate() if self.interactive: submitTemplate = self.prepareLogMessage(template, logMessage) @@ -558,24 +582,24 @@ class P4Submit(Command): return False [upstream, settings] = findUpstreamBranchPoint() - depotPath = settings['depot-paths'][0] + self.depotPath = settings['depot-paths'][0] if len(self.origin) == 0: self.origin = upstream if self.verbose: print "Origin branch is " + self.origin - if len(depotPath) == 0: + if len(self.depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" sys.exit(128) - self.clientPath = p4Where(depotPath) + self.clientPath = p4Where(self.depotPath) if len(self.clientPath) == 0: - print "Error: Cannot locate perforce checkout of %s in client view" % depotPath + print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath sys.exit(128) - print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath) + print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath) self.oldWorkingDirectory = os.getcwd() if self.directSubmit: -- cgit v1.2.3 From b767c792fa202539cfb9bba36f46c62bcbf7c987 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 9 Aug 2007 02:38:09 -0400 Subject: Teach update-paranoid how to store ACLs organized by groups In some applications of this paranoid update hook the set of ACL rules that need to be applied to a user can be large, and the number of users that those rules must also be applied to can be more than a handful of individuals. Rather than repeating the same rules multiple times (once for each user) we now allow users to be members of groups, where the group supplies the list of ACL rules. For various reasons we don't depend on the underlying OS groups and instead perform our own group handling. Users can be made a member of one or more groups by setting the user.memberOf property within the "users/$who.acl" file: [user] memberOf = developer memberOf = administrator This will cause the hook to also parse the "groups/$groupname.acl" file for each value of user.memberOf, and merge any allow rules that match the current repository with the user's own private rules (if they had any). Since some rules are basically the same but may have a component differ based on the individual user, any user.* key may be inserted into a rule using the "${user.foo}" syntax. The allow rule does not match if the user does not define one (and exactly one) value for the key "foo". Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 60 +++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 16 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 5ee1835c80..fb2aca3628 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -118,22 +118,29 @@ sub info ($) { print STDERR "-Info- $_[0]\n" if $debug; } -sub parse_config ($$) { - my ($data, $fn) = @_; - info "Loading $fn"; - open(I,'-|','git',"--git-dir=$acl_git",'cat-file','blob',$fn); +sub git_value (@) { + open(T,'-|','git',@_); local $_ = ; chop; close T; $_; +} + +sub parse_config ($$$$) { + my $data = shift; + local $ENV{GIT_DIR} = shift; + my $br = shift; + my $fn = shift; + info "Loading $br:$fn"; + open(I,'-|','git','cat-file','blob',"$br:$fn"); my $section = ''; while () { chomp; if (/^\s*$/ || /^\s*#/) { } elsif (/^\[([a-z]+)\]$/i) { - $section = $1; + $section = lc $1; } elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) { - $section = "$1.$2"; + $section = join('.',lc $1,$2); } elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) { - push @{$data->{"$section.$1"}}, $2; + push @{$data->{join('.',$section,lc $1)}}, $2; } else { - deny "bad config file line $. in $fn"; + deny "bad config file line $. in $br:$fn"; } } close I; @@ -202,11 +209,6 @@ sub check_committers (@) { } } -sub git_value (@) { - open(T,'-|','git',@_); local $_ = ; chop; close T; - $_; -} - deny "No GIT_DIR inherited from caller" unless $git_dir; deny "Need a ref name" unless $ref; deny "Refusing funny ref $ref" unless $ref =~ s,^refs/,,; @@ -231,13 +233,39 @@ $op = 'U' if ($op eq 'R' && $ref =~ m,^heads/, && $old eq git_value('merge-base',$old,$new)); -# Load the user's ACL file. +# Load the user's ACL file. Expand groups (user.memberof) one level. { my %data = ('user.committer' => []); - parse_config(\%data, "$acl_branch:users/$this_user.acl"); + parse_config(\%data,$acl_git,$acl_branch,"external/$repository_name.acl"); + + %data = ( + 'user.committer' => $data{'user.committer'}, + 'user.memberof' => [], + ); + parse_config(\%data,$acl_git,$acl_branch,"users/$this_user.acl"); + %user_committer = map {$_ => $_} @{$data{'user.committer'}}; - my $rules = $data{"repository.$repository_name.allow"} || []; + my $rule_key = "repository.$repository_name.allow"; + my $rules = $data{$rule_key} || []; + + foreach my $group (@{$data{'user.memberof'}}) { + my %g; + parse_config(\%g,$acl_git,$acl_branch,"groups/$group.acl"); + my $group_rules = $g{$rule_key}; + push @$rules, @$group_rules if $group_rules; + } + +RULE: foreach (@$rules) { + while (/\${user\.([a-z][a-zA-Z0-9]+)}/) { + my $k = lc $1; + my $v = $data{"user.$k"}; + next RULE unless defined $v; + next RULE if @$v != 1; + next RULE unless defined $v->[0]; + s/\${user\.$k}/$v->[0]/g; + } + if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { my $ops = $1; my $ref = $2; -- cgit v1.2.3 From d47eed3272311acf16f136c49b0bb341c9a6e39c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 9 Aug 2007 02:38:12 -0400 Subject: Teach the update-paranoid to look at file differences In some applications of the update hook a user may be allowed to modify a branch, but only if the file level difference is also an allowed change. This is the commonly requested feature of allowing users to modify only certain files. A new repository.*.allow syntax permits granting the three basic file level operations: A: file is added relative to the other tree M: file exists in both trees, but its SHA-1 or mode differs D: file is removed relative to the other tree on a per-branch and path-name basis. The user must also have a branch level allow line already granting them access to create, rewind or update (CRU) that branch before the hook will consult any file level rules. In order for a branch change to succeed _all_ files that differ relative to some base (by default the old value of this branch, but it can also be any valid tree-ish) must be allowed by file level allow rules. A push is rejected if any diff exists that is not covered by at least one allow rule. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 112 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index fb2aca3628..84ed452480 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -102,6 +102,8 @@ my ($this_user) = getpwuid $<; # REAL_USER_ID my $repository_name; my %user_committer; my @allow_rules; +my @path_rules; +my %diff_cache; sub deny ($) { print STDERR "-Deny- $_[0]\n" if $debug; @@ -122,6 +124,13 @@ sub git_value (@) { open(T,'-|','git',@_); local $_ = ; chop; close T; $_; } +sub match_string ($$) { + my ($acl_n, $ref) = @_; + ($acl_n eq $ref) + || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n) + || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:); +} + sub parse_config ($$$$) { my $data = shift; local $ENV{GIT_DIR} = shift; @@ -209,6 +218,31 @@ sub check_committers (@) { } } +sub load_diff ($) { + my $base = shift; + my $d = $diff_cache{$base}; + unless ($d) { + local $/ = "\0"; + open(T,'-|','git','diff-tree', + '-r','--name-status','-z', + $base,$new) or return undef; + my %this_diff; + while () { + my $op = $_; + chop $op; + + my $path = ; + chop $path; + + $this_diff{$path} = $op; + } + close T or return undef; + $d = \%this_diff; + $diff_cache{$base} = $d; + } + return $d; +} + deny "No GIT_DIR inherited from caller" unless $git_dir; deny "Need a ref name" unless $ref; deny "Refusing funny ref $ref" unless $ref =~ s,^refs/,,; @@ -266,7 +300,19 @@ RULE: s/\${user\.$k}/$v->[0]/g; } - if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { + if (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)\s+diff\s+([^\s]+)$/) { + my ($ops, $pth, $ref, $bst) = ($1, $2, $3, $4); + $ops =~ s/ //g; + $pth =~ s/\\\\/\\/g; + $ref =~ s/\\\\/\\/g; + push @path_rules, [$ops, $pth, $ref, $bst]; + } elsif (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)$/) { + my ($ops, $pth, $ref) = ($1, $2, $3); + $ops =~ s/ //g; + $pth =~ s/\\\\/\\/g; + $ref =~ s/\\\\/\\/g; + push @path_rules, [$ops, $pth, $ref, $old]; + } elsif (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { my $ops = $1; my $ref = $2; $ops =~ s/ //g; @@ -300,13 +346,65 @@ foreach my $acl_entry (@allow_rules) { next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen. next unless $acl_n; next unless $op =~ /^[$acl_ops]$/; + next unless match_string $acl_n, $ref; + + # Don't test path rules on branch deletes. + # + grant "Allowed by: $acl_ops for $acl_n" if $op eq 'D'; + + # Aggregate matching path rules; allow if there aren't + # any matching this ref. + # + my %pr; + foreach my $p_entry (@path_rules) { + my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry; + next unless $p_ref; + push @{$pr{$p_bst}}, $p_entry if match_string $p_ref, $ref; + } + grant "Allowed by: $acl_ops for $acl_n" unless %pr; - grant "Allowed by: $acl_ops for $acl_n" - if ( - ($acl_n eq $ref) - || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n) - || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:) - ); + # Allow only if all changes against a single base are + # allowed by file path rules. + # + my @bad; + foreach my $p_bst (keys %pr) { + my $diff_ref = load_diff $p_bst; + deny "Cannot difference trees." unless ref $diff_ref; + + my %fd = %$diff_ref; + foreach my $p_entry (@{$pr{$p_bst}}) { + my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry; + next unless $p_ops =~ /^[AMD]+$/; + next unless $p_n; + + foreach my $f_n (keys %fd) { + my $f_op = $fd{$f_n}; + next unless $f_op; + next unless $f_op =~ /^[$p_ops]$/; + delete $fd{$f_n} if match_string $p_n, $f_n; + } + last unless %fd; + } + + if (%fd) { + push @bad, [$p_bst, \%fd]; + } else { + # All changes relative to $p_bst were allowed. + # + grant "Allowed by: $acl_ops for $acl_n diff $p_bst"; + } + } + + foreach my $bad_ref (@bad) { + my ($p_bst, $fd) = @$bad_ref; + print STDERR "\n"; + print STDERR "Not allowed to make the following changes:\n"; + print STDERR "(base: $p_bst)\n"; + foreach my $f_n (sort keys %$fd) { + print STDERR " $fd->{$f_n} $f_n\n"; + } + } + deny "You are not permitted to $op $ref"; } close A; deny "You are not permitted to $op $ref"; -- cgit v1.2.3 From cabead982b7cf40f8930e6e38327fc396b7fef81 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 9 Aug 2007 02:38:16 -0400 Subject: Use the empty tree for base diff in paranoid-update on new branches We have to load a tree difference for the purpose of testing file patterns. But if our branch is being created and there is no specific base to difference against in the rule our base will be '0'x40. This is (usually) not a valid tree-ish object in a Git repository, so there's nothing to difference against. Instead of creating the empty tree and running git-diff against that we just take the output of `ls-tree -r --name-only` and mark every returned pathname as an add. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 84ed452480..068fa37083 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -223,20 +223,31 @@ sub load_diff ($) { my $d = $diff_cache{$base}; unless ($d) { local $/ = "\0"; - open(T,'-|','git','diff-tree', - '-r','--name-status','-z', - $base,$new) or return undef; my %this_diff; - while () { - my $op = $_; - chop $op; + if ($base =~ /^0{40}$/) { + open(T,'-|','git','ls-tree', + '-r','--name-only','-z', + $new) or return undef; + while () { + chop; + $this_diff{$_} = 'A'; + } + close T or return undef; + } else { + open(T,'-|','git','diff-tree', + '-r','--name-status','-z', + $base,$new) or return undef; + while () { + my $op = $_; + chop $op; - my $path = ; - chop $path; + my $path = ; + chop $path; - $this_diff{$path} = $op; + $this_diff{$path} = $op; + } + close T or return undef; } - close T or return undef; $d = \%this_diff; $diff_cache{$base} = $d; } -- cgit v1.2.3 From 09afcd6933e8497c997205dda71d718e62b4de62 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 11 Aug 2007 12:22:47 +0200 Subject: git.el: Add support for interactive diffs. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index f6102fc344..214b75cf93 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -965,7 +965,13 @@ Return the list of files that haven't been handled." (defun git-diff-file-idiff () "Perform an interactive diff on the current file." (interactive) - (error "Interactive diffs not implemented yet.")) + (let ((files (git-marked-files-state 'added 'deleted 'modified))) + (unless (eq 1 (length files)) + (error "Cannot perform an interactive diff on multiple files.")) + (let* ((filename (car (git-get-filenames files))) + (buff1 (find-file-noselect filename)) + (buff2 (git-run-command-buffer (concat filename ".~HEAD~") "cat-file" "blob" (concat "HEAD:" filename)))) + (ediff-buffers buff1 buff2)))) (defun git-log-file () "Display a log of changes to the marked file(s)." -- cgit v1.2.3 From 8fdc39729b9aaa02e89eca9d7964182a72052665 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 11 Aug 2007 12:23:21 +0200 Subject: git.el: Always set the current directory in the git-diff buffer. This allows jumping to the correct file with the diff-mode commands. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 214b75cf93..be44e06c45 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -912,10 +912,12 @@ Return the list of files that haven't been handled." (defun git-setup-diff-buffer (buffer) "Setup a buffer for displaying a diff." - (with-current-buffer buffer - (diff-mode) - (goto-char (point-min)) - (setq buffer-read-only t)) + (let ((dir default-directory)) + (with-current-buffer buffer + (diff-mode) + (goto-char (point-min)) + (setq default-directory dir) + (setq buffer-read-only t))) (display-buffer buffer) (shrink-window-if-larger-than-buffer)) -- cgit v1.2.3 From 7da660f4372f4113184f782634c0fafb2cf46cef Mon Sep 17 00:00:00 2001 From: "Reece H. Dunn" Date: Mon, 13 Aug 2007 19:40:50 +0100 Subject: git-p4: Fix the sorting of changelists when cloning a Perforce repository. When performing a git-p4 clone operation on a Perforce repository, where the changelists change in order of magnitude (e.g. 100 to 1000), the set of changes to import from is not sorted properly. This is because the data in the list is strings not integers. The other place where this is done already converts the value to an integer, so it is not affected. Acked-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 805d632a68..6d0106237a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1322,7 +1322,7 @@ class P4Sync(Command): for line in output: changeNum = line.split(" ")[1] - changes.append(changeNum) + changes.append(int(changeNum)) changes.sort() -- cgit v1.2.3 From 374a58c9fbd4613800d01087c77373e561db90ae Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Sun, 12 Aug 2007 14:46:12 -0400 Subject: git-completion.bash - add support for git-bundle Signed-off-by: Mark Levedahl Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 82b9ed40d8..52b2893844 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -459,6 +459,35 @@ _git_branch () __gitcomp "$(__git_refs)" } +_git_bundle () +{ + local mycword="$COMP_CWORD" + case "${COMP_WORDS[0]}" in + git) + local cmd="${COMP_WORDS[2]}" + mycword="$((mycword-1))" + ;; + git-bundle*) + local cmd="${COMP_WORDS[1]}" + ;; + esac + case "$mycword" in + 1) + __gitcomp "create list-heads verify unbundle" + ;; + 2) + # looking for a file + ;; + *) + case "$cmd" in + create) + __git_complete_revlist + ;; + esac + ;; + esac +} + _git_checkout () { __gitcomp "$(__git_refs)" @@ -1009,6 +1038,7 @@ _git () add) _git_add ;; apply) _git_apply ;; bisect) _git_bisect ;; + bundle) _git_bundle ;; branch) _git_branch ;; checkout) _git_checkout ;; cherry) _git_cherry ;; @@ -1057,6 +1087,7 @@ complete -o default -o nospace -F _git_am git-am complete -o default -o nospace -F _git_apply git-apply complete -o default -o nospace -F _git_bisect git-bisect complete -o default -o nospace -F _git_branch git-branch +complete -o default -o nospace -F _git_bundle git-bundle complete -o default -o nospace -F _git_checkout git-checkout complete -o default -o nospace -F _git_cherry git-cherry complete -o default -o nospace -F _git_cherry_pick git-cherry-pick @@ -1092,6 +1123,7 @@ complete -o default -o nospace -F _git_add git-add.exe complete -o default -o nospace -F _git_apply git-apply.exe complete -o default -o nospace -F _git git.exe complete -o default -o nospace -F _git_branch git-branch.exe +complete -o default -o nospace -F _git_bundle git-bundle.exe complete -o default -o nospace -F _git_cherry git-cherry.exe complete -o default -o nospace -F _git_diff git-diff.exe complete -o default -o nospace -F _git_format_patch git-format-patch.exe -- cgit v1.2.3 From e301bfeea19e284344868840793c58d2e7529c74 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Aug 2007 21:50:12 -0400 Subject: Fix new-workdir (again) to work on bare repositories My day-job workflow involves using multiple workdirs attached to a bunch of bare repositories. Such repositories are stored inside of a directory called "foo.git", which means `git rev-parse --git-dir` will return "." and not ".git". Under such conditions new-workdir was getting confused about where the Git repository it was supplied is actually located. If we get "." for the result of --git-dir query it means we should use the user supplied path as-is, and not attempt to perform any magic on it, as the path is directly to the repository. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 3ff6bd166a..119cff9859 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -24,10 +24,14 @@ git_dir=$(cd "$orig_git" 2>/dev/null && git rev-parse --git-dir 2>/dev/null) || die "\"$orig_git\" is not a git repository!" -if test "$git_dir" = ".git" -then +case "$git_dir" in +.git) git_dir="$orig_git/.git" -fi + ;; +.) + git_dir=$orig_git + ;; +esac # don't link to a workdir if test -L "$git_dir/config" -- cgit v1.2.3 From 8fa0ee3b50736eb869a3e13375bb041c1bf5aa12 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 22 Aug 2007 01:33:49 -0400 Subject: Suggest unsetting core.bare when using new-workdir on a bare repository If core.bare is set to true in the config file of a repository that the user is trying to create a working directory from we should abort and suggest to the user that they remove the option first. If we leave the core.bare=true setting in the config file then working tree operations will get confused when they attempt to execute in the new workdir, as it shares its config file with the bare repository. The working tree operations will assume that the workdir is bare and abort, which is not what the user wants. If we changed core.bare to be false then working tree operations will function in the workdir but other operations may fail in the bare repository, as it claims to not be bare. If we remove core.bare from the config then Git can fallback on the legacy guessing behavior. This allows operations in the bare repository to work as though it were bare, while operations in the workdirs to act as though they are not bare. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 119cff9859..c6e154a84f 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -33,6 +33,14 @@ case "$git_dir" in ;; esac +# don't link to a configured bare repository +isbare=$(git --git-dir="$git_dir" config --bool --get core.bare) +if test ztrue = z$isbare +then + die "\"$git_dir\" has core.bare set to true," \ + " remove from \"$git_dir/config\" to use $0" +fi + # don't link to a workdir if test -L "$git_dir/config" then -- cgit v1.2.3 From ef08c14993a8d2881941667c0264c97c6874ccee Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 22 Aug 2007 12:21:38 +0200 Subject: git.el: Avoid a lisp error when there's no current branch (detached HEAD). Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index be44e06c45..abc799a287 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -664,9 +664,11 @@ Return the list of files that haven't been handled." (ewoc-set-hf status (format "Directory: %s\nBranch: %s\nHead: %s%s\n" default-directory - (if (string-match "^refs/heads/" branch) - (substring branch (match-end 0)) - branch) + (if branch + (if (string-match "^refs/heads/" branch) + (substring branch (match-end 0)) + branch) + "none (detached HEAD)") head (if merge-heads (concat "\nMerging: " -- cgit v1.2.3 From 47e98eecf3721dca64e3d453fc96e3a02debe54f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 23 Aug 2007 01:39:22 -0400 Subject: Update bash completion with new 1.5.3 command line options A number of commands have learned new tricks as part of git 1.5.3. If these are long options (--foo) we tend to support them in the bash completion, as it makes the user's task of using the option slightly easier. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 52b2893844..8f27aa9a13 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -419,7 +419,7 @@ _git_add () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--interactive" + __gitcomp "--interactive --refresh" return esac COMPREPLY=() @@ -573,6 +573,7 @@ _git_format_patch () --stdout --attach --thread --output-directory --numbered --start-number + --numbered-files --keep-subject --signoff --in-reply-to= @@ -590,7 +591,7 @@ _git_gc () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--prune" + __gitcomp "--prune --aggressive" return ;; esac @@ -617,14 +618,20 @@ _git_log () " "" "${cur##--pretty=}" return ;; + --date=*) + __gitcomp " + relative iso8601 rfc2822 short local default + " "" "${cur##--date=}" + return + ;; --*) __gitcomp " --max-count= --max-age= --since= --after= --min-age= --before= --until= --root --topo-order --date-order --reverse - --no-merges + --no-merges --follow --abbrev-commit --abbrev= - --relative-date + --relative-date --date= --author= --committer= --grep= --all-match --pretty= --name-status --name-only --raw @@ -796,7 +803,7 @@ _git_config () case "$cur" in --*) __gitcomp " - --global --system + --global --system --file= --list --replace-all --get --get-all --get-regexp --add --unset --unset-all @@ -839,6 +846,7 @@ _git_config () core.ignoreStat core.preferSymlinkRefs core.logAllRefUpdates + core.loosecompression core.repositoryFormatVersion core.sharedRepository core.warnAmbiguousRefs @@ -870,6 +878,7 @@ _git_config () diff.renames fetch.unpackLimit format.headers + format.subjectprefix gitcvs.enabled gitcvs.logfile gitcvs.allbinary @@ -896,6 +905,10 @@ _git_config () merge.verbosity pack.window pack.depth + pack.windowMemory + pack.compression + pack.deltaCacheSize + pack.deltaCacheLimit pull.octopus pull.twohead repack.useDeltaBaseOffset @@ -1024,7 +1037,14 @@ _git () if [ $c -eq $COMP_CWORD -a -z "$command" ]; then case "${COMP_WORDS[COMP_CWORD]}" in --*=*) COMPREPLY=() ;; - --*) __gitcomp "--git-dir= --bare --version --exec-path" ;; + --*) __gitcomp " + --no-pager + --git-dir= + --bare + --version + --exec-path + " + ;; *) __gitcomp "$(__git_commands) $(__git_aliases)" ;; esac return -- cgit v1.2.3 From 217926c08cb634e3b5394ea15b3fe4520069260b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 23 Aug 2007 01:42:11 -0400 Subject: Teach bash to complete ref arguments to git-describe I'm often finding that I need to run git-describe on very long remote tracking branch names, to find out what tagged revision the remote tracking branch is now at (or not at). Typing out the ref names is painful, so bash completion on them is a very useful feature. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8f27aa9a13..6ed6a51dc6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -525,6 +525,11 @@ _git_commit () COMPREPLY=() } +_git_describe () +{ + __gitcomp "$(__git_refs)" +} + _git_diff () { __git_complete_file @@ -1065,6 +1070,7 @@ _git () cherry-pick) _git_cherry_pick ;; commit) _git_commit ;; config) _git_config ;; + describe) _git_describe ;; diff) _git_diff ;; fetch) _git_fetch ;; format-patch) _git_format_patch ;; @@ -1112,6 +1118,7 @@ complete -o default -o nospace -F _git_checkout git-checkout complete -o default -o nospace -F _git_cherry git-cherry complete -o default -o nospace -F _git_cherry_pick git-cherry-pick complete -o default -o nospace -F _git_commit git-commit +complete -o default -o nospace -F _git_describe git-describe complete -o default -o nospace -F _git_diff git-diff complete -o default -o nospace -F _git_fetch git-fetch complete -o default -o nospace -F _git_format_patch git-format-patch @@ -1145,6 +1152,7 @@ complete -o default -o nospace -F _git git.exe complete -o default -o nospace -F _git_branch git-branch.exe complete -o default -o nospace -F _git_bundle git-bundle.exe complete -o default -o nospace -F _git_cherry git-cherry.exe +complete -o default -o nospace -F _git_describe git-describe.exe complete -o default -o nospace -F _git_diff git-diff.exe complete -o default -o nospace -F _git_format_patch git-format-patch.exe complete -o default -o nospace -F _git_log git-log.exe -- cgit v1.2.3 From be86f7a0dfa05dacb2ef512e99d1de576e77a633 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 23 Aug 2007 01:50:49 -0400 Subject: Teach bash about git-submodule and its subcommands The git-submodule command is new in 1.5.3 and contains a number of useful subcommands for working on submodules. We usually try to offer the subcommands of a git command in the bash completion, so here they are for git-submodule. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6ed6a51dc6..a652c88b27 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1024,6 +1024,31 @@ _git_stash () __gitcomp 'list show apply clear' } +_git_submodule () +{ + local i c=1 command + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + add|status|init|update) command="$i"; break ;; + esac + c=$((++c)) + done + + if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--quiet --cached" + ;; + *) + __gitcomp "add status init update" + ;; + esac + return + fi +} + _git () { local i c=1 command __git_dir @@ -1090,6 +1115,7 @@ _git () show) _git_show ;; show-branch) _git_log ;; stash) _git_stash ;; + submodule) _git_submodule ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; esac @@ -1138,6 +1164,7 @@ complete -o default -o nospace -F _git_reset git-reset complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_stash git-stash +complete -o default -o nospace -F _git_submodule git-submodule complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged -- cgit v1.2.3 From 5ca4461728adadf22b5b8833693702ab09e556db Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 24 Aug 2007 17:44:16 +0200 Subject: git-p4: Make 'git-p4 branches' work after an initial clone with git clone from an origin-updated repository. After a clone with "git clone" of a repository the p4 branches are only in remotes/origin/p4/* and not in remotes/p4/*. Separate the code for detection and creation out of the P4Sync command class into standalone methods and use them from the P4Branches command. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 104 ++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 49 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6d0106237a..b571e30473 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -231,6 +231,56 @@ def findUpstreamBranchPoint(head = "HEAD"): return ["", settings] +def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True): + if not silent: + print ("Creating/updating branch(es) in %s based on origin branch(es)" + % localRefPrefix) + + originPrefix = "origin/p4/" + + for line in read_pipe_lines("git rev-parse --symbolic --remotes"): + line = line.strip() + if (not line.startswith(originPrefix)) or line.endswith("HEAD"): + continue + + headName = line[len(originPrefix):] + remoteHead = localRefPrefix + headName + originHead = line + + original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead)) + if (not original.has_key('depot-paths') + or not original.has_key('change')): + continue + + update = False + if not gitBranchExists(remoteHead): + if verbose: + print "creating %s" % remoteHead + update = True + else: + settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead)) + if settings.has_key('change') > 0: + if settings['depot-paths'] == original['depot-paths']: + originP4Change = int(original['change']) + p4Change = int(settings['change']) + if originP4Change > p4Change: + print ("%s (%s) is newer than %s (%s). " + "Updating p4 branch from origin." + % (originHead, originP4Change, + remoteHead, p4Change)) + update = True + else: + print ("Ignoring: %s was imported from %s while " + "%s was imported from %s" + % (originHead, ','.join(original['depot-paths']), + remoteHead, ','.join(settings['depot-paths']))) + + if update: + system("git update-ref %s %s" % (remoteHead, originHead)) + +def originP4BranchesExist(): + return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") + class Command: def __init__(self): self.usage = "usage: %prog [options]" @@ -1041,53 +1091,6 @@ class P4Sync(Command): for branch in branches.keys(): self.initialParents[self.refPrefix + branch] = branches[branch] - def createOrUpdateBranchesFromOrigin(self): - if not self.silent: - print ("Creating/updating branch(es) in %s based on origin branch(es)" - % self.refPrefix) - - originPrefix = "origin/p4/" - - for line in read_pipe_lines("git rev-parse --symbolic --remotes"): - line = line.strip() - if (not line.startswith(originPrefix)) or line.endswith("HEAD"): - continue - - headName = line[len(originPrefix):] - remoteHead = self.refPrefix + headName - originHead = line - - original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead)) - if (not original.has_key('depot-paths') - or not original.has_key('change')): - continue - - update = False - if not gitBranchExists(remoteHead): - if self.verbose: - print "creating %s" % remoteHead - update = True - else: - settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead)) - if settings.has_key('change') > 0: - if settings['depot-paths'] == original['depot-paths']: - originP4Change = int(original['change']) - p4Change = int(settings['change']) - if originP4Change > p4Change: - print ("%s (%s) is newer than %s (%s). " - "Updating p4 branch from origin." - % (originHead, originP4Change, - remoteHead, p4Change)) - update = True - else: - print ("Ignoring: %s was imported from %s while " - "%s was imported from %s" - % (originHead, ','.join(original['depot-paths']), - remoteHead, ','.join(settings['depot-paths']))) - - if update: - system("git update-ref %s %s" % (remoteHead, originHead)) - def updateOptionDict(self, d): option_keys = {} if self.keepRepoPath: @@ -1108,7 +1111,7 @@ class P4Sync(Command): # map from branch depot path to parent branch self.knownBranches = {} self.initialParents = {} - self.hasOrigin = gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") + self.hasOrigin = originP4BranchesExist() if not self.syncWithOrigin: self.hasOrigin = False @@ -1135,7 +1138,7 @@ class P4Sync(Command): # merge with previous imports, if possible. if args == []: if self.hasOrigin: - self.createOrUpdateBranchesFromOrigin() + createOrUpdateBranchesFromOrigin(self.refPrefix, self.silent) self.listExistingP4GitBranches() if len(self.p4BranchesInGit) > 1: @@ -1518,6 +1521,9 @@ class P4Branches(Command): self.verbose = False def run(self, args): + if originP4BranchesExist(): + createOrUpdateBranchesFromOrigin() + cmdline = "git rev-parse --symbolic " cmdline += " --remotes" -- cgit v1.2.3 From 0058a33a8eb5a8bfcfc5f5e769a2517cff4b73f1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 24 Aug 2007 17:46:16 +0200 Subject: git-p4: Fix warnings about non-existant refs/remotes/p4/HEAD ref when running git-p4 sync the first time after a git clone. Don't create the p4/HEAD symbolic ref if p4/master doesn't exist yet. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b571e30473..55778c5775 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1131,7 +1131,7 @@ class P4Sync(Command): system("git update-ref %s refs/heads/p4" % self.branch) system("git branch -D p4"); # create it /after/ importing, when master exists - if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: + if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch): system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) # TODO: should always look at previous commits, -- cgit v1.2.3 From 1ff55ff27b819c1fd9c0d2e153974f2c1924a875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Mon, 27 Aug 2007 11:50:12 +0200 Subject: git.el: Added colors for dark background Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index abc799a287..280557ecd4 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -99,47 +99,56 @@ if there is already one that displays the same directory." (defface git-status-face - '((((class color) (background light)) (:foreground "purple"))) + '((((class color) (background light)) (:foreground "purple")) + (((class color) (background dark)) (:foreground "salmon"))) "Git mode face used to highlight added and modified files." :group 'git) (defface git-unmerged-face - '((((class color) (background light)) (:foreground "red" :bold t))) + '((((class color) (background light)) (:foreground "red" :bold t)) + (((class color) (background dark)) (:foreground "red" :bold t))) "Git mode face used to highlight unmerged files." :group 'git) (defface git-unknown-face - '((((class color) (background light)) (:foreground "goldenrod" :bold t))) + '((((class color) (background light)) (:foreground "goldenrod" :bold t)) + (((class color) (background dark)) (:foreground "goldenrod" :bold t))) "Git mode face used to highlight unknown files." :group 'git) (defface git-uptodate-face - '((((class color) (background light)) (:foreground "grey60"))) + '((((class color) (background light)) (:foreground "grey60")) + (((class color) (background dark)) (:foreground "grey40"))) "Git mode face used to highlight up-to-date files." :group 'git) (defface git-ignored-face - '((((class color) (background light)) (:foreground "grey60"))) + '((((class color) (background light)) (:foreground "grey60")) + (((class color) (background dark)) (:foreground "grey40"))) "Git mode face used to highlight ignored files." :group 'git) (defface git-mark-face - '((((class color) (background light)) (:foreground "red" :bold t))) + '((((class color) (background light)) (:foreground "red" :bold t)) + (((class color) (background dark)) (:foreground "tomato" :bold t))) "Git mode face used for the file marks." :group 'git) (defface git-header-face - '((((class color) (background light)) (:foreground "blue"))) + '((((class color) (background light)) (:foreground "blue")) + (((class color) (background dark)) (:foreground "blue"))) "Git mode face used for commit headers." :group 'git) (defface git-separator-face - '((((class color) (background light)) (:foreground "brown"))) + '((((class color) (background light)) (:foreground "brown")) + (((class color) (background dark)) (:foreground "brown"))) "Git mode face used for commit separator." :group 'git) (defface git-permission-face - '((((class color) (background light)) (:foreground "green" :bold t))) + '((((class color) (background light)) (:foreground "green" :bold t)) + (((class color) (background dark)) (:foreground "green" :bold t))) "Git mode face used for permission changes." :group 'git) -- cgit v1.2.3 From 7d37b5bf4eda8b2dcca371fb4b5ebd50fed64d67 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 29 Aug 2007 15:15:34 +0100 Subject: completion: also complete git-log's --left-right and --cherry-pick option Both --left-right and --cherry-pick are particularly long to type, so help the user there. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a652c88b27..5ed18215fd 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -641,6 +641,7 @@ _git_log () --all-match --pretty= --name-status --name-only --raw --not --all + --left-right --cherry-pick " return ;; -- cgit v1.2.3 From 88e21dc7461dca1ebc70d8579bcc9246364511ee Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 31 Aug 2007 23:47:01 -0400 Subject: Teach bash about completing arguments for git-tag Lately I have been doing a lot of calls to `git tag -d` and also to `git tag -v`. In both such cases being able to complete the names of existing tags saves the fingers some typing effort. We now look for the -d or -v option to git-tag in the bash completion support and offer up existing tag names as possible choices for these. When creating a new tag we now also offer bash completion support for the second argument to git-tag (the object to be tagged) as this can often be a specific existing branch name and is not necessarily the current HEAD. If the -f option is being used to recreate an existing tag we now also offer completion support on the existing tag names for the first argument of git-tag, helping to the user to reselect the prior tag name that they are trying to replace. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 5ed18215fd..cad842af45 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -114,6 +114,27 @@ __git_heads () done } +__git_tags () +{ + local cmd i is_hash=y dir="$(__gitdir "$1")" + if [ -d "$dir" ]; then + for i in $(git --git-dir="$dir" \ + for-each-ref --format='%(refname)' \ + refs/tags ); do + echo "${i#refs/tags/}" + done + return + fi + for i in $(git-ls-remote "$1" 2>/dev/null); do + case "$is_hash,$i" in + y,*) is_hash=n ;; + n,*^{}) is_hash=y ;; + n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;; + n,*) is_hash=y; echo "$i" ;; + esac + done +} + __git_refs () { local cmd i is_hash=y dir="$(__gitdir "$1")" @@ -1050,6 +1071,40 @@ _git_submodule () fi } +_git_tag () +{ + local i c=1 f=0 + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + -d|-v) + __gitcomp "$(__git_tags)" + return + ;; + -f) + f=1 + ;; + esac + c=$((++c)) + done + + case "${COMP_WORDS[COMP_CWORD-1]}" in + -m|-F) + COMPREPLY=() + ;; + -*|tag|git-tag) + if [ $f = 1 ]; then + __gitcomp "$(__git_tags)" + else + COMPREPLY=() + fi + ;; + *) + __gitcomp "$(__git_refs)" + ;; + esac +} + _git () { local i c=1 command __git_dir @@ -1117,6 +1172,7 @@ _git () show-branch) _git_log ;; stash) _git_stash ;; submodule) _git_submodule ;; + tag) _git_tag ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; esac @@ -1167,6 +1223,7 @@ complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_stash git-stash complete -o default -o nospace -F _git_submodule git-submodule complete -o default -o nospace -F _git_log git-show-branch +complete -o default -o nospace -F _git_tag git-tag complete -o default -o nospace -F _git_log git-whatchanged # The following are necessary only for Cygwin, and only are needed @@ -1192,5 +1249,6 @@ complete -o default -o nospace -F _git_config git-config complete -o default -o nospace -F _git_shortlog git-shortlog.exe complete -o default -o nospace -F _git_show git-show.exe complete -o default -o nospace -F _git_log git-show-branch.exe +complete -o default -o nospace -F _git_tag git-tag.exe complete -o default -o nospace -F _git_log git-whatchanged.exe fi -- cgit v1.2.3 From 31f9ec129ef37e50b5cacf26a2ebcb5420fcdc5e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 21 Aug 2007 11:53:02 +0200 Subject: git-p4: Always call 'p4 sync ...' before submitting to Perforce. Acked-by: Marius Storm-Olsen Acked-by: Thiago Macieira --- contrib/fast-import/git-p4 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 55778c5775..3728cbf9aa 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -664,9 +664,8 @@ class P4Submit(Command): f.close(); os.chdir(self.clientPath) - response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath) - if response == "y" or response == "yes": - system("p4 sync ...") + print "Syncronizing p4 checkout..." + system("p4 sync ...") if self.reset: self.firstTime = True -- cgit v1.2.3 From 14594f4b5747e51b051f647f6430089e6664e77d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 22 Aug 2007 09:07:15 +0200 Subject: git-p4: After submission to p4 always synchronize from p4 again (into refs/remotes). Whether to rebase HEAD or not is still left as question to the end-user. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3728cbf9aa..16e0a7bc81 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -704,10 +704,14 @@ class P4Submit(Command): else: print "All changes applied!" os.chdir(self.oldWorkingDirectory) - response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") + + sync = P4Sync() + sync.run([]) + + response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ") if response == "y" or response == "yes": rebase = P4Rebase() - rebase.run([]) + rebase.rebase() os.remove(self.configFile) return True @@ -1439,6 +1443,9 @@ class P4Rebase(Command): sync = P4Sync() sync.run([]) + return self.rebase() + + def rebase(self): [upstream, settings] = findUpstreamBranchPoint() if len(upstream) == 0: die("Cannot find upstream branchpoint for rebase") -- cgit v1.2.3 From 4f6432d8cc6ebdcdc366cf67ab39e8125c449d80 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 15:56:36 +0200 Subject: git-p4: Cleanup; moved the code for getting a sorted list of p4 changes for a list of given depot paths into a standalone method. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 16e0a7bc81..e9feb7498c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -281,6 +281,19 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent def originP4BranchesExist(): return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") +def p4ChangesForPaths(depotPaths, changeRange): + assert depotPaths + output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, changeRange) + for p in depotPaths])) + + changes = [] + for line in output: + changeNum = line.split(" ")[1] + changes.append(int(changeNum)) + + changes.sort() + return changes + class Command: def __init__(self): self.usage = "usage: %prog [options]" @@ -1322,15 +1335,7 @@ class P4Sync(Command): if self.verbose: print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths), self.changeRange) - assert self.depotPaths - output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange) - for p in self.depotPaths])) - - for line in output: - changeNum = line.split(" ")[1] - changes.append(int(changeNum)) - - changes.sort() + changes = p4ChangesForPaths(self.depotPaths, self.changeRange) if len(self.maxChanges) > 0: changes = changes[:min(int(self.maxChanges), len(changes))] -- cgit v1.2.3 From e87f37ae42dad89b39620c234fc29c94529a4d07 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:00:52 +0200 Subject: git-p4: Cleanup; moved the code to import a list of p4 changes using fast-import into a separate member function of P4Sync. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 140 +++++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 69 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e9feb7498c..c00702c895 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1118,6 +1118,76 @@ class P4Sync(Command): self.keepRepoPath = (d.has_key('options') and ('keepRepoPath' in d['options'])) + def importChanges(self, changes): + cnt = 1 + for change in changes: + description = p4Cmd("describe %s" % change) + self.updateOptionDict(description) + + if not self.silent: + sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() + cnt = cnt + 1 + + try: + if self.detectBranches: + branches = self.splitFilesIntoBranches(description) + for branch in branches.keys(): + ## HACK --hwn + branchPrefix = self.depotPaths[0] + branch + "/" + + parent = "" + + filesForCommit = branches[branch] + + if self.verbose: + print "branch is %s" % branch + + self.updatedBranches.add(branch) + + if branch not in self.createdBranches: + self.createdBranches.add(branch) + parent = self.knownBranches[branch] + if parent == branch: + parent = "" + elif self.verbose: + print "parent determined through known branches: %s" % parent + + # main branch? use master + if branch == "main": + branch = "master" + else: + + ## FIXME + branch = self.projectName + branch + + if parent == "main": + parent = "master" + elif len(parent) > 0: + ## FIXME + parent = self.projectName + parent + + branch = self.refPrefix + branch + if len(parent) > 0: + parent = self.refPrefix + parent + + if self.verbose: + print "looking for initial parent for %s; current parent is %s" % (branch, parent) + + if len(parent) == 0 and branch in self.initialParents: + parent = self.initialParents[branch] + del self.initialParents[branch] + + self.commit(description, filesForCommit, branch, [branchPrefix], parent) + else: + files = self.extractFilesFromCommit(description) + self.commit(description, files, self.branch, self.depotPaths, + self.initialParent) + self.initialParent = "" + except IOError: + print self.gitError.read() + sys.exit(1) + def run(self, args): self.depotPaths = [] self.changeRange = "" @@ -1350,74 +1420,7 @@ class P4Sync(Command): self.updatedBranches = set() - cnt = 1 - for change in changes: - description = p4Cmd("describe %s" % change) - self.updateOptionDict(description) - - if not self.silent: - sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) - sys.stdout.flush() - cnt = cnt + 1 - - try: - if self.detectBranches: - branches = self.splitFilesIntoBranches(description) - for branch in branches.keys(): - ## HACK --hwn - branchPrefix = self.depotPaths[0] + branch + "/" - - parent = "" - - filesForCommit = branches[branch] - - if self.verbose: - print "branch is %s" % branch - - self.updatedBranches.add(branch) - - if branch not in self.createdBranches: - self.createdBranches.add(branch) - parent = self.knownBranches[branch] - if parent == branch: - parent = "" - elif self.verbose: - print "parent determined through known branches: %s" % parent - - # main branch? use master - if branch == "main": - branch = "master" - else: - - ## FIXME - branch = self.projectName + branch - - if parent == "main": - parent = "master" - elif len(parent) > 0: - ## FIXME - parent = self.projectName + parent - - branch = self.refPrefix + branch - if len(parent) > 0: - parent = self.refPrefix + parent - - if self.verbose: - print "looking for initial parent for %s; current parent is %s" % (branch, parent) - - if len(parent) == 0 and branch in self.initialParents: - parent = self.initialParents[branch] - del self.initialParents[branch] - - self.commit(description, filesForCommit, branch, [branchPrefix], parent) - else: - files = self.extractFilesFromCommit(description) - self.commit(description, files, self.branch, self.depotPaths, - self.initialParent) - self.initialParent = "" - except IOError: - print self.gitError.read() - sys.exit(1) + self.importChanges(changes) if not self.silent: print "" @@ -1427,7 +1430,6 @@ class P4Sync(Command): sys.stdout.write("%s " % b) sys.stdout.write("\n") - self.gitStream.close() if importProcess.wait() != 0: die("fast-import failed: %s" % self.gitError.read()) -- cgit v1.2.3 From 1c49fc197bd05a4c2ed602efcdbe277ef798813a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:04:34 +0200 Subject: git-p4: Cleanup; Turn self.revision into a function local variable (it's not used anywhere outside the function). Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c00702c895..d7c5becc0e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1285,7 +1285,7 @@ class P4Sync(Command): self.depotPaths = sorted(args) - self.revision = "" + revision = "" self.users = {} newPaths = [] @@ -1296,15 +1296,15 @@ class P4Sync(Command): if self.changeRange == "@all": self.changeRange = "" elif ',' not in self.changeRange: - self.revision = self.changeRange + revision = self.changeRange self.changeRange = "" p = p[:atIdx] elif p.find("#") != -1: hashIdx = p.index("#") - self.revision = p[hashIdx:] + revision = p[hashIdx:] p = p[:hashIdx] elif self.previousDepotPaths == []: - self.revision = "#head" + revision = "#head" p = re.sub ("\.\.\.$", "", p) if not p.endswith("/"): @@ -1345,19 +1345,19 @@ class P4Sync(Command): self.gitStream = importProcess.stdin self.gitError = importProcess.stderr - if self.revision: - print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), self.revision, self.branch) + if revision: + print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch) details = { "user" : "git perforce import user", "time" : int(time.time()) } details["desc"] = ("Initial import of %s from the state at revision %s" - % (' '.join(self.depotPaths), self.revision)) - details["change"] = self.revision + % (' '.join(self.depotPaths), revision)) + details["change"] = revision newestRevision = 0 fileCnt = 0 for info in p4CmdList("files " + ' '.join(["%s...%s" - % (p, self.revision) + % (p, revision) for p in self.depotPaths])): if info['code'] == 'error': -- cgit v1.2.3 From c208a24310582d9cf337b66f41a0d7a9fe106bb4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:07:18 +0200 Subject: git-p4: Cleanup; moved the code for the initial #head or revision import into a separate function, out of P4Sync.run. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 87 ++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 42 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d7c5becc0e..2c67190ffc 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1188,6 +1188,50 @@ class P4Sync(Command): print self.gitError.read() sys.exit(1) + def importHeadRevision(self, revision): + print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch) + + details = { "user" : "git perforce import user", "time" : int(time.time()) } + details["desc"] = ("Initial import of %s from the state at revision %s" + % (' '.join(self.depotPaths), revision)) + details["change"] = revision + newestRevision = 0 + + fileCnt = 0 + for info in p4CmdList("files " + + ' '.join(["%s...%s" + % (p, revision) + for p in self.depotPaths])): + + if info['code'] == 'error': + sys.stderr.write("p4 returned an error: %s\n" + % info['data']) + sys.exit(1) + + + change = int(info["change"]) + if change > newestRevision: + newestRevision = change + + if info["action"] == "delete": + # don't increase the file cnt, otherwise details["depotFile123"] will have gaps! + #fileCnt = fileCnt + 1 + continue + + for prop in ["depotFile", "rev", "action", "type" ]: + details["%s%s" % (prop, fileCnt)] = info[prop] + + fileCnt = fileCnt + 1 + + details["change"] = newestRevision + self.updateOptionDict(details) + try: + self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths) + except IOError: + print "IO error with git fast-import. Is your git version recent enough?" + print self.gitError.read() + + def run(self, args): self.depotPaths = [] self.changeRange = "" @@ -1346,48 +1390,7 @@ class P4Sync(Command): self.gitError = importProcess.stderr if revision: - print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch) - - details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = ("Initial import of %s from the state at revision %s" - % (' '.join(self.depotPaths), revision)) - details["change"] = revision - newestRevision = 0 - - fileCnt = 0 - for info in p4CmdList("files " - + ' '.join(["%s...%s" - % (p, revision) - for p in self.depotPaths])): - - if info['code'] == 'error': - sys.stderr.write("p4 returned an error: %s\n" - % info['data']) - sys.exit(1) - - - change = int(info["change"]) - if change > newestRevision: - newestRevision = change - - if info["action"] == "delete": - # don't increase the file cnt, otherwise details["depotFile123"] will have gaps! - #fileCnt = fileCnt + 1 - continue - - for prop in ["depotFile", "rev", "action", "type" ]: - details["%s%s" % (prop, fileCnt)] = info[prop] - - fileCnt = fileCnt + 1 - - details["change"] = newestRevision - self.updateOptionDict(details) - try: - self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths) - except IOError: - print "IO error with git fast-import. Is your git version recent enough?" - print self.gitError.read() - + self.importHeadRevision(revision) else: changes = [] -- cgit v1.2.3 From 8134f69c21ff47283d8b3ea3cc5b306727cde256 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:44:55 +0200 Subject: git-p4: Cleanup; moved the (duplicated) code for turning a branch into a git ref (for example foo -> refs/remotes/p4//foo) into a separate method. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2c67190ffc..406bec1a29 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1118,6 +1118,15 @@ class P4Sync(Command): self.keepRepoPath = (d.has_key('options') and ('keepRepoPath' in d['options'])) + def gitRefForBranch(self, branch): + if branch == "main": + return self.refPrefix + "master" + + if len(branch) <= 0: + return branch + + return self.refPrefix + self.projectName + branch + def importChanges(self, changes): cnt = 1 for change in changes: @@ -1153,23 +1162,8 @@ class P4Sync(Command): elif self.verbose: print "parent determined through known branches: %s" % parent - # main branch? use master - if branch == "main": - branch = "master" - else: - - ## FIXME - branch = self.projectName + branch - - if parent == "main": - parent = "master" - elif len(parent) > 0: - ## FIXME - parent = self.projectName + parent - - branch = self.refPrefix + branch - if len(parent) > 0: - parent = self.refPrefix + parent + branch = self.gitRefForBranch(branch) + parent = self.gitRefForBranch(parent) if self.verbose: print "looking for initial parent for %s; current parent is %s" % (branch, parent) -- cgit v1.2.3 From 1ca3d71069620f1438d9f89165a3e69e8d47d302 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 17:36:55 +0200 Subject: git-p4: Added support for automatically importing newly appearing perforce branches. If a change in a p4 "branch" appears that hasn't seen any previous commit and that has a known branch mapping we now try to import it properly. First we find the p4 change of the source branch that the new p4 branch is based on. Then we using git rev-list --bisect to locate the corresponding git commit to that change. Finally we import all changes in the new p4 branch up to the current change and resume with the regular import. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 76 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 406bec1a29..adaaae6633 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1127,6 +1127,67 @@ class P4Sync(Command): return self.refPrefix + self.projectName + branch + def gitCommitByP4Change(self, ref, change): + if self.verbose: + print "looking in ref " + ref + " for change %s using bisect..." % change + + earliestCommit = "" + latestCommit = parseRevision(ref) + + while True: + if self.verbose: + print "trying: earliest %s latest %s" % (earliestCommit, latestCommit) + next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip() + if len(next) == 0: + if self.verbose: + print "argh" + return "" + log = extractLogMessageFromGitCommit(next) + settings = extractSettingsGitLog(log) + currentChange = int(settings['change']) + if self.verbose: + print "current change %s" % currentChange + + if currentChange == change: + if self.verbose: + print "found %s" % next + return next + + if currentChange < change: + earliestCommit = "^%s" % next + else: + latestCommit = "%s" % next + + return "" + + def importNewBranch(self, branch, maxChange): + # make fast-import flush all changes to disk and update the refs using the checkpoint + # command so that we can try to find the branch parent in the git history + self.gitStream.write("checkpoint\n\n"); + self.gitStream.flush(); + branchPrefix = self.depotPaths[0] + branch + "/" + range = "@1,%s" % maxChange + #print "prefix" + branchPrefix + changes = p4ChangesForPaths([branchPrefix], range) + if len(changes) <= 0: + return False + firstChange = changes[0] + #print "first change in branch: %s" % firstChange + sourceBranch = self.knownBranches[branch] + sourceDepotPath = self.depotPaths[0] + sourceBranch + sourceRef = self.gitRefForBranch(sourceBranch) + #print "source " + sourceBranch + + branchParentChange = int(p4Cmd("changes -m 1 %s...@1,%s" % (sourceDepotPath, firstChange))["change"]) + #print "branch parent: %s" % branchParentChange + gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange) + if len(gitParent) > 0: + self.initialParents[self.gitRefForBranch(branch)] = gitParent + #print "parent git commit: %s" % gitParent + + self.importChanges(changes) + return True + def importChanges(self, changes): cnt = 1 for change in changes: @@ -1159,8 +1220,19 @@ class P4Sync(Command): parent = self.knownBranches[branch] if parent == branch: parent = "" - elif self.verbose: - print "parent determined through known branches: %s" % parent + else: + fullBranch = self.projectName + branch + if fullBranch not in self.p4BranchesInGit: + if not self.silent: + print("\n Importing new branch %s" % fullBranch); + if self.importNewBranch(branch, change - 1): + parent = "" + self.p4BranchesInGit.append(fullBranch) + if not self.silent: + print("\n Resuming with change %s" % change); + + if self.verbose: + print "parent determined through known branches: %s" % parent branch = self.gitRefForBranch(branch) parent = self.gitRefForBranch(parent) -- cgit v1.2.3 From ea09ea22d65d118328642e03ad23c8257304499d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 5 Sep 2007 23:33:41 -0400 Subject: Don't allow contrib/workdir/git-new-workdir to trash existing dirs Recently I found that doing a sequence like the following: git-new-workdir a b ... git-new-workdir a b by accident will cause a (and now also b) to have an infinite cycle in its refs directory. This is caused by git-new-workdir trying to create the "refs" symlink over again, only during the second time it is being created within a's refs directory and is now also pointing back at a's refs. This causes confusion in git as suddenly branches are named things like "refs/refs/refs/refs/refs/refs/refs/heads/foo" instead of the more commonly accepted "refs/heads/foo". Plenty of commands start to see ambiguous ref names and others just take ages to compute. git-clone has the same safety check, so git-new-workdir should behave just like it. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index c6e154a84f..2838546d16 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -48,6 +48,12 @@ then "a complete repository." fi +# don't recreate a workdir over an existing repository +if test -e "$new_workdir" +then + die "destination directory '$new_workdir' already exists." +fi + # make sure the the links use full paths git_dir=$(cd "$git_dir"; pwd) -- cgit v1.2.3 From 0e5a7faa3a903cf7a0a66c81e20a76b91f17faab Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Tue, 11 Sep 2007 05:19:34 +0200 Subject: Make "git reset" a builtin. This replaces the script "git-reset.sh" with "builtin-reset.c". A few git commands used in the script are called from the builtin also: "ls-files" to check for unmerged files, "read-tree" for resetting the index file in "mixed" and "hard" resets, and "update-index" to refresh at the end in the "mixed" reset and also for the option that gets selected paths into the index. The reset option with paths was implemented by Johannes Schindelin. Since the option that gets selected paths into the index is not a "reset" like the others because it does not change the HEAD at all, now the command is showing a warning when the "--mixed" option is supplied for that purpose. The following table shows the behaviour of "git reset" for the different supported options, where X means "changing" the HEAD, index or working tree: reset: --soft --mixed --hard -- HEAD X X X - index - X X X files - - X - Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- contrib/examples/git-reset.sh | 106 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100755 contrib/examples/git-reset.sh (limited to 'contrib') diff --git a/contrib/examples/git-reset.sh b/contrib/examples/git-reset.sh new file mode 100755 index 0000000000..1dc606fbd3 --- /dev/null +++ b/contrib/examples/git-reset.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# +# Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano +# +USAGE='[--mixed | --soft | --hard] [] [ [--] ...]' +SUBDIRECTORY_OK=Yes +. git-sh-setup +set_reflog_action "reset $*" +require_work_tree + +update= reset_type=--mixed +unset rev + +while case $# in 0) break ;; esac +do + case "$1" in + --mixed | --soft | --hard) + reset_type="$1" + ;; + --) + break + ;; + -*) + usage + ;; + *) + rev=$(git rev-parse --verify "$1") || exit + shift + break + ;; + esac + shift +done + +: ${rev=HEAD} +rev=$(git rev-parse --verify $rev^0) || exit + +# Skip -- in "git reset HEAD -- foo" and "git reset -- foo". +case "$1" in --) shift ;; esac + +# git reset --mixed tree [--] paths... can be used to +# load chosen paths from the tree into the index without +# affecting the working tree nor HEAD. +if test $# != 0 +then + test "$reset_type" = "--mixed" || + die "Cannot do partial $reset_type reset." + + git diff-index --cached $rev -- "$@" | + sed -e 's/^:\([0-7][0-7]*\) [0-7][0-7]* \([0-9a-f][0-9a-f]*\) [0-9a-f][0-9a-f]* [A-Z] \(.*\)$/\1 \2 \3/' | + git update-index --add --remove --index-info || exit + git update-index --refresh + exit +fi + +cd_to_toplevel + +if test "$reset_type" = "--hard" +then + update=-u +fi + +# Soft reset does not touch the index file nor the working tree +# at all, but requires them in a good order. Other resets reset +# the index file to the tree object we are switching to. +if test "$reset_type" = "--soft" +then + if test -f "$GIT_DIR/MERGE_HEAD" || + test "" != "$(git ls-files --unmerged)" + then + die "Cannot do a soft reset in the middle of a merge." + fi +else + git read-tree -v --reset $update "$rev" || exit +fi + +# Any resets update HEAD to the head being switched to. +if orig=$(git rev-parse --verify HEAD 2>/dev/null) +then + echo "$orig" >"$GIT_DIR/ORIG_HEAD" +else + rm -f "$GIT_DIR/ORIG_HEAD" +fi +git update-ref -m "$GIT_REFLOG_ACTION" HEAD "$rev" +update_ref_status=$? + +case "$reset_type" in +--hard ) + test $update_ref_status = 0 && { + printf "HEAD is now at " + GIT_PAGER= git log --max-count=1 --pretty=oneline \ + --abbrev-commit HEAD + } + ;; +--soft ) + ;; # Nothing else to do +--mixed ) + # Report what has not been updated. + git update-index --refresh + ;; +esac + +rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" \ + "$GIT_DIR/SQUASH_MSG" "$GIT_DIR/MERGE_MSG" + +exit $update_ref_status -- cgit v1.2.3 From 1b655040bec1ff2f44fe309ac0f26085ed6372e9 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 13 Sep 2007 11:49:40 +0200 Subject: git.el: Keep the status buffer sorted by filename. This makes insertions and updates much more efficient. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 103 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 38 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 280557ecd4..b5d7c0664e 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -479,6 +479,27 @@ and returns the process output as a string." (setf (git-fileinfo->orig-name info) nil) (setf (git-fileinfo->needs-refresh info) t)))) +(defun git-set-filenames-state (status files state) + "Set the state of a list of named files." + (when files + (setq files (sort files #'string-lessp)) + (let ((file (pop files)) + (node (ewoc-nth status 0))) + (while (and file node) + (let ((info (ewoc-data node))) + (cond ((string-lessp (git-fileinfo->name info) file) + (setq node (ewoc-next status node))) + ((string-equal (git-fileinfo->name info) file) + (unless (eq (git-fileinfo->state info) state) + (setf (git-fileinfo->state info) state) + (setf (git-fileinfo->rename-state info) nil) + (setf (git-fileinfo->orig-name info) nil) + (setf (git-fileinfo->needs-refresh info) t)) + (setq file (pop files))) + (t (setq file (pop files))))))) + (unless state ;; delete files whose state has been set to nil + (ewoc-filter status (lambda (info) (git-fileinfo->state info)))))) + (defun git-state-code (code) "Convert from a string to a added/deleted/modified state." (case (string-to-char code) @@ -532,19 +553,36 @@ and returns the process output as a string." " " (git-escape-file-name (git-fileinfo->name info)) (git-rename-as-string info)))) -(defun git-insert-fileinfo (status info &optional refresh) - "Insert INFO in the status buffer, optionally refreshing an existing one." - (let ((node (and refresh - (git-find-status-file status (git-fileinfo->name info))))) - (setf (git-fileinfo->needs-refresh info) t) - (when node ;preserve the marked flag - (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))) - (if node (setf (ewoc-data node) info) (ewoc-enter-last status info)))) +(defun git-insert-info-list (status infolist) + "Insert a list of file infos in the status buffer, replacing existing ones if any." + (setq infolist (sort infolist + (lambda (info1 info2) + (string-lessp (git-fileinfo->name info1) + (git-fileinfo->name info2))))) + (let ((info (pop infolist)) + (node (ewoc-nth status 0))) + (while info + (setf (git-fileinfo->needs-refresh info) t) + (cond ((not node) + (ewoc-enter-last status info) + (setq info (pop infolist))) + ((string-lessp (git-fileinfo->name (ewoc-data node)) + (git-fileinfo->name info)) + (setq node (ewoc-next status node))) + ((string-equal (git-fileinfo->name (ewoc-data node)) + (git-fileinfo->name info)) + ;; preserve the marked flag + (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))) + (setf (ewoc-data node) info) + (setq info (pop infolist))) + (t + (ewoc-enter-before status node info) + (setq info (pop infolist))))))) (defun git-run-diff-index (status files) "Run git-diff-index on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let ((refresh files)) + (let (infolist) (with-temp-buffer (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) @@ -558,13 +596,14 @@ Return the list of files that haven't been handled." (new-name (match-string 8))) (if new-name ; copy or rename (if (eq ?C (string-to-char state)) - (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh) - (git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh) - (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh) - (git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh)) + (push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist) + (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist) + (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist)) + (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)) (setq files (delete name files)) - (when new-name (setq files (delete new-name files))))))) - files) + (when new-name (setq files (delete new-name files)))))) + (git-insert-info-list status infolist) + files)) (defun git-find-status-file (status file) "Find a given file in the status ewoc and return its node." @@ -576,16 +615,16 @@ Return the list of files that haven't been handled." (defun git-run-ls-files (status files default-state &rest options) "Run git-ls-files on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let ((refresh files)) + (let (infolist) (with-temp-buffer - (apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files)) + (apply #'git-run-command t nil "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) - (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1) - (let ((state (match-string 1)) - (name (match-string 2))) - (git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh) - (setq files (delete name files)))))) - files) + (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) + (let ((name (match-string 1))) + (push (git-create-fileinfo default-state name) infolist) + (setq files (delete name files))))) + (git-insert-info-list status infolist) + files)) (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." @@ -594,9 +633,8 @@ Return the list of files that haven't been handled." (goto-char (point-min)) (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) - (let ((node (git-find-status-file status (match-string 1)))) - (when node (push (ewoc-data node) unmerged-files)))) - (git-set-files-state unmerged-files 'unmerged)))) + (push (match-string 1) unmerged-files)) + (git-set-filenames-state status unmerged-files 'unmerged)))) (defun git-get-exclude-files () "Get the list of exclude files to pass to git-ls-files." @@ -622,18 +660,7 @@ Return the list of files that haven't been handled." (setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o" (concat "--exclude-per-directory=" git-per-dir-ignore-file) (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) - ; mark remaining files with the default state (or remove them if nil) - (when remaining-files - (if default-state - (ewoc-map (lambda (info) - (when (member (git-fileinfo->name info) remaining-files) - (git-set-files-state (list info) default-state)) - nil) - status) - (ewoc-filter status - (lambda (info files) - (not (member (git-fileinfo->name info) files))) - remaining-files))) + (git-set-filenames-state status remaining-files default-state) (git-refresh-files) (git-refresh-ewoc-hf status))) -- cgit v1.2.3 From 98acc3fabc2d197525f1b0119382a00a664014b7 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 13 Sep 2007 11:50:08 +0200 Subject: git.el: Allow selecting whether to display uptodate/unknown/ignored files. The default behavior for each state can be customized, and it can also be toggled directly from the status buffer. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 92 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index b5d7c0664e..d1068c6258 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -97,6 +97,21 @@ if there is already one that displays the same directory." :group 'git :type 'string) +(defcustom git-show-uptodate nil + "Whether to display up-to-date files." + :group 'git + :type 'boolean) + +(defcustom git-show-ignored nil + "Whether to display ignored files." + :group 'git + :type 'boolean) + +(defcustom git-show-unknown t + "Whether to display unknown files." + :group 'git + :type 'boolean) + (defface git-status-face '((((class color) (background light)) (:foreground "purple")) @@ -646,23 +661,30 @@ Return the list of files that haven't been handled." (push config files)) files)) +(defun git-run-ls-files-with-excludes (status files default-state &rest options) + "Run git-ls-files on FILES with appropriate --exclude-from options." + (let ((exclude-files (git-get-exclude-files))) + (apply #'git-run-ls-files status files default-state + (concat "--exclude-per-directory=" git-per-dir-ignore-file) + (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) + (defun git-update-status-files (files &optional default-state) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) - (let* ((status git-status) - (remaining-files + (unless files + (when git-show-uptodate (git-run-ls-files git-status nil 'uptodate "-c"))) + (let* ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db - (git-run-ls-files status files 'added "-c") - (git-run-diff-index status files)))) - (git-run-ls-unmerged status files) - (when (or (not files) remaining-files) - (let ((exclude-files (git-get-exclude-files))) - (setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o" - (concat "--exclude-per-directory=" git-per-dir-ignore-file) - (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) - (git-set-filenames-state status remaining-files default-state) + (git-run-ls-files git-status files 'added "-c") + (git-run-diff-index git-status files)))) + (git-run-ls-unmerged git-status files) + (when (or remaining-files (and git-show-unknown (not files))) + (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o"))) + (when (or remaining-files (and git-show-ignored (not files))) + (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i"))) + (git-set-filenames-state git-status remaining-files default-state) (git-refresh-files) - (git-refresh-ewoc-hf status))) + (git-refresh-ewoc-hf git-status))) (defun git-marked-files () "Return a list of all marked files, or if none a list containing just the file at cursor position." @@ -943,11 +965,41 @@ Return the list of files that haven't been handled." (interactive) (ewoc-filter git-status (lambda (info) - (not (or (eq (git-fileinfo->state info) 'ignored) - (eq (git-fileinfo->state info) 'uptodate))))) + (case (git-fileinfo->state info) + ('ignored git-show-ignored) + ('uptodate git-show-uptodate) + ('unknown git-show-unknown) + (t t)))) (unless (ewoc-nth git-status 0) ; refresh header if list is empty (git-refresh-ewoc-hf git-status))) +(defun git-toggle-show-uptodate () + "Toogle the option for showing up-to-date files." + (interactive) + (if (setq git-show-uptodate (not git-show-uptodate)) + (git-refresh-status) + (git-remove-handled))) + +(defun git-toggle-show-ignored () + "Toogle the option for showing ignored files." + (interactive) + (if (setq git-show-ignored (not git-show-ignored)) + (progn + (git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i") + (git-refresh-files) + (git-refresh-ewoc-hf git-status)) + (git-remove-handled))) + +(defun git-toggle-show-unknown () + "Toogle the option for showing unknown files." + (interactive) + (if (setq git-show-unknown (not git-show-unknown)) + (progn + (git-run-ls-files-with-excludes git-status nil 'unknown "-o") + (git-refresh-files) + (git-refresh-ewoc-hf git-status)) + (git-remove-handled))) + (defun git-setup-diff-buffer (buffer) "Setup a buffer for displaying a diff." (let ((dir default-directory)) @@ -1173,7 +1225,8 @@ Return the list of files that haven't been handled." (unless git-status-mode-map (let ((map (make-keymap)) - (diff-map (make-sparse-keymap))) + (diff-map (make-sparse-keymap)) + (toggle-map (make-sparse-keymap))) (suppress-keymap map) (define-key map "?" 'git-help) (define-key map "h" 'git-help) @@ -1197,6 +1250,7 @@ Return the list of files that haven't been handled." (define-key map "q" 'git-status-quit) (define-key map "r" 'git-remove-file) (define-key map "R" 'git-resolve-file) + (define-key map "t" toggle-map) (define-key map "T" 'git-toggle-all-marks) (define-key map "u" 'git-unmark-file) (define-key map "U" 'git-revert-file) @@ -1213,6 +1267,11 @@ Return the list of files that haven't been handled." (define-key diff-map "h" 'git-diff-file-merge-head) (define-key diff-map "m" 'git-diff-file-mine) (define-key diff-map "o" 'git-diff-file-other) + ; the toggle submap + (define-key toggle-map "u" 'git-toggle-show-uptodate) + (define-key toggle-map "i" 'git-toggle-show-ignored) + (define-key toggle-map "k" 'git-toggle-show-unknown) + (define-key toggle-map "m" 'git-toggle-all-marks) (setq git-status-mode-map map))) ;; git mode should only run in the *git status* buffer @@ -1234,6 +1293,9 @@ Commands: (let ((status (ewoc-create 'git-fileinfo-prettyprint "" ""))) (set (make-local-variable 'git-status) status)) (set (make-local-variable 'list-buffers-directory) default-directory) + (make-local-variable 'git-show-uptodate) + (make-local-variable 'git-show-ignored) + (make-local-variable 'git-show-unknown) (run-hooks 'git-status-mode-hook))) (defun git-find-status-buffer (dir) -- cgit v1.2.3 From 568d2cde9b9fe0fc6b3202c7987b13289cb1a4a0 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 13 Sep 2007 11:50:29 +0200 Subject: git.el: Allow the add and remove commands to be applied to ignored files. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d1068c6258..2d77fd47ec 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -902,7 +902,7 @@ Return the list of files that haven't been handled." (defun git-add-file () "Add marked file(s) to the index cache." (interactive) - (let ((files (git-get-filenames (git-marked-files-state 'unknown)))) + (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored)))) (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) (apply #'git-run-command nil nil "update-index" "--add" "--" files) @@ -920,7 +920,7 @@ Return the list of files that haven't been handled." (defun git-remove-file () "Remove the marked file(s)." (interactive) - (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate)))) + (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate 'ignored)))) (unless files (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files)) (if (yes-or-no-p -- cgit v1.2.3 From 7f8cfadf218c8b28caf52b1490fb8b881945b0ea Mon Sep 17 00:00:00 2001 From: Nguyen Thai Ngoc Duy Date: Tue, 18 Sep 2007 03:26:01 -0400 Subject: contrib/fast-import: add simple shell example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This example just puts a directory under git control. It is significantly slower than using the git tools directly, but hopefully shows a bit how fast-import works. [jk: added header comments] Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/fast-import/git-import.sh | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 contrib/fast-import/git-import.sh (limited to 'contrib') diff --git a/contrib/fast-import/git-import.sh b/contrib/fast-import/git-import.sh new file mode 100755 index 0000000000..0ca7718d05 --- /dev/null +++ b/contrib/fast-import/git-import.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# +# Performs an initial import of a directory. This is the equivalent +# of doing 'git init; git add .; git commit'. It's a lot slower, +# but is meant to be a simple fast-import example. + +if [ -z "$1" -o -z "$2" ]; then + echo "Usage: git-import branch import-message" + exit 1 +fi + +USERNAME="$(git config user.name)" +EMAIL="$(git config user.email)" + +if [ -z "$USERNAME" -o -z "$EMAIL" ]; then + echo "You need to set user name and email" + exit 1 +fi + +git init + +( + cat < now +data < Date: Tue, 18 Sep 2007 03:26:27 -0400 Subject: contrib/fast-import: add perl version of simple example This is based on the git-import.sh script, but is a little more robust and efficient. More importantly, it should serve as a quick template for interfacing fast-import with perl scripts. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/fast-import/git-import.perl | 64 +++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100755 contrib/fast-import/git-import.perl (limited to 'contrib') diff --git a/contrib/fast-import/git-import.perl b/contrib/fast-import/git-import.perl new file mode 100755 index 0000000000..f9fef6db28 --- /dev/null +++ b/contrib/fast-import/git-import.perl @@ -0,0 +1,64 @@ +#!/usr/bin/perl +# +# Performs an initial import of a directory. This is the equivalent +# of doing 'git init; git add .; git commit'. It's a little slower, +# but is meant to be a simple fast-import example. + +use strict; +use File::Find; + +my $USAGE = 'Usage: git-import branch import-message'; +my $branch = shift or die "$USAGE\n"; +my $message = shift or die "$USAGE\n"; + +chomp(my $username = `git config user.name`); +chomp(my $email = `git config user.email`); +die 'You need to set user name and email' + unless $username && $email; + +system('git init'); +open(my $fi, '|-', qw(git fast-import --date-format=now)) + or die "unable to spawn fast-import: $!"; + +print $fi < now +data < 0) { + my $r = read($in, my $buf, $len < 4096 ? $len : 4096); + defined($r) or die "read error from $fn: $!"; + $r > 0 or die "premature EOF from $fn: $!"; + print $fi $buf; + $len -= $r; + } + print $fi "\n"; + + }, '.' +); + +close($fi); +exit $?; -- cgit v1.2.3 From af6fb4c822a5a65c7671d810127171759dff38f6 Mon Sep 17 00:00:00 2001 From: Josh England Date: Tue, 11 Sep 2007 10:59:04 -0600 Subject: Added example hook script to save/restore permissions/ownership. Usage info is emebed in the script, but the gist of it is to run the script from a pre-commit hook to save permissions/ownership data to a file and check that file into the repository. Then, a post_merge hook reads the file and updates working tree permissions/ownership. All updates are transparent to the user (although there is a --verbose option). Merge conflicts are handled in the "read" phase (in pre-commit), and the script aborts the commit and tells you how to fix things in the case of a merge conflict in the metadata file. This same idea could be extended to handle file ACLs or other file metadata if desired. Signed-off-by: Josh England Signed-off-by: Junio C Hamano --- contrib/hooks/setgitperms.perl | 213 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 contrib/hooks/setgitperms.perl (limited to 'contrib') diff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl new file mode 100644 index 0000000000..5e3b89def2 --- /dev/null +++ b/contrib/hooks/setgitperms.perl @@ -0,0 +1,213 @@ +#!/usr/bin/perl +# +# Copyright (c) 2006 Josh England +# +# This script can be used to save/restore full permissions and ownership data +# within a git working tree. +# +# To save permissions/ownership data, place this script in your .git/hooks +# directory and enable a `pre-commit` hook with the following lines: +# #!/bin/sh +# . git-sh-setup +# $GIT_DIR/hooks/setgitperms.perl -r +# +# To restore permissions/ownership data, place this script in your .git/hooks +# directory and enable a `post-merge` hook with the following lines: +# #!/bin/sh +# . git-sh-setup +# $GIT_DIR/hooks/setgitperms.perl -w +# +use strict; +use Getopt::Long; +use File::Find; +use File::Basename; + +my $usage = +"Usage: setgitperms.perl [OPTION]... <--read|--write> +This program uses a file `.gitmeta` to store/restore permissions and uid/gid +info for all files/dirs tracked by git in the repository. + +---------------------------------Read Mode------------------------------------- +-r, --read Reads perms/etc from working dir into a .gitmeta file +-s, --stdout Output to stdout instead of .gitmeta +-d, --diff Show unified diff of perms file (XOR with --stdout) + +---------------------------------Write Mode------------------------------------ +-w, --write Modify perms/etc in working dir to match the .gitmeta file +-v, --verbose Be verbose + +\n"; + +my ($stdout, $showdiff, $verbose, $read_mode, $write_mode); + +if ((@ARGV < 0) || !GetOptions( + "stdout", \$stdout, + "diff", \$showdiff, + "read", \$read_mode, + "write", \$write_mode, + "verbose", \$verbose, + )) { die $usage; } +die $usage unless ($read_mode xor $write_mode); + +my $topdir = `git-rev-parse --show-cdup` or die "\n"; chomp $topdir; +my $gitdir = $topdir . '.git'; +my $gitmeta = $topdir . '.gitmeta'; + +if ($write_mode) { + # Update the working dir permissions/ownership based on data from .gitmeta + open (IN, "<$gitmeta") or die "Could not open $gitmeta for reading: $!\n"; + while (defined ($_ = )) { + chomp; + if (/^(.*) mode=(\S+)\s+uid=(\d+)\s+gid=(\d+)/) { + # Compare recorded perms to actual perms in the working dir + my ($path, $mode, $uid, $gid) = ($1, $2, $3, $4); + my $fullpath = $topdir . $path; + my (undef,undef,$wmode,undef,$wuid,$wgid) = lstat($fullpath); + $wmode = sprintf "%04o", $wmode & 07777; + if ($mode ne $wmode) { + $verbose && print "Updating permissions on $path: old=$wmode, new=$mode\n"; + chmod oct($mode), $fullpath; + } + if ($uid != $wuid || $gid != $wgid) { + if ($verbose) { + # Print out user/group names instead of uid/gid + my $pwname = getpwuid($uid); + my $grpname = getgrgid($gid); + my $wpwname = getpwuid($wuid); + my $wgrpname = getgrgid($wgid); + $pwname = $uid if !defined $pwname; + $grpname = $gid if !defined $grpname; + $wpwname = $wuid if !defined $wpwname; + $wgrpname = $wgid if !defined $wgrpname; + + print "Updating uid/gid on $path: old=$wpwname/$wgrpname, new=$pwname/$grpname\n"; + } + chown $uid, $gid, $fullpath; + } + } + else { + warn "Invalid input format in $gitmeta:\n\t$_\n"; + } + } + close IN; +} +elsif ($read_mode) { + # Handle merge conflicts in the .gitperms file + if (-e "$gitdir/MERGE_MSG") { + if (`grep ====== $gitmeta`) { + # Conflict not resolved -- abort the commit + print "PERMISSIONS/OWNERSHIP CONFLICT\n"; + print " Resolve the conflict in the $gitmeta file and then run\n"; + print " `.git/hooks/setgitperms.perl --write` to reconcile.\n"; + exit 1; + } + elsif (`grep $gitmeta $gitdir/MERGE_MSG`) { + # A conflict in .gitmeta has been manually resolved. Verify that + # the working dir perms matches the current .gitmeta perms for + # each file/dir that conflicted. + # This is here because a `setgitperms.perl --write` was not + # performed due to a merge conflict, so permissions/ownership + # may not be consistent with the manually merged .gitmeta file. + my @conflict_diff = `git show \$(cat $gitdir/MERGE_HEAD)`; + my @conflict_files; + my $metadiff = 0; + + # Build a list of files that conflicted from the .gitmeta diff + foreach my $line (@conflict_diff) { + if ($line =~ m|^diff --git a/$gitmeta b/$gitmeta|) { + $metadiff = 1; + } + elsif ($line =~ /^diff --git/) { + $metadiff = 0; + } + elsif ($metadiff && $line =~ /^\+(.*) mode=/) { + push @conflict_files, $1; + } + } + + # Verify that each conflict file now has permissions consistent + # with the .gitmeta file + foreach my $file (@conflict_files) { + my $absfile = $topdir . $file; + my $gm_entry = `grep "^$file mode=" $gitmeta`; + if ($gm_entry =~ /mode=(\d+) uid=(\d+) gid=(\d+)/) { + my ($gm_mode, $gm_uid, $gm_gid) = ($1, $2, $3); + my (undef,undef,$mode,undef,$uid,$gid) = lstat("$absfile"); + $mode = sprintf("%04o", $mode & 07777); + if (($gm_mode ne $mode) || ($gm_uid != $uid) + || ($gm_gid != $gid)) { + print "PERMISSIONS/OWNERSHIP CONFLICT\n"; + print " Mismatch found for file: $file\n"; + print " Run `.git/hooks/setgitperms.perl --write` to reconcile.\n"; + exit 1; + } + } + else { + print "Warning! Permissions/ownership no longer being tracked for file: $file\n"; + } + } + } + } + + # No merge conflicts -- write out perms/ownership data to .gitmeta file + unless ($stdout) { + open (OUT, ">$gitmeta.tmp") or die "Could not open $gitmeta.tmp for writing: $!\n"; + } + + my @files = `git-ls-files`; + my %dirs; + + foreach my $path (@files) { + chomp $path; + # We have to manually add stats for parent directories + my $parent = dirname($path); + while (!exists $dirs{$parent}) { + $dirs{$parent} = 1; + next if $parent eq '.'; + printstats($parent); + $parent = dirname($parent); + } + # Now the git-tracked file + printstats($path); + } + + # diff the temporary metadata file to see if anything has changed + # If no metadata has changed, don't overwrite the real file + # This is just so `git commit -a` doesn't try to commit a bogus update + unless ($stdout) { + if (! -e $gitmeta) { + rename "$gitmeta.tmp", $gitmeta; + } + else { + my $diff = `diff -U 0 $gitmeta $gitmeta.tmp`; + if ($diff ne '') { + rename "$gitmeta.tmp", $gitmeta; + } + else { + unlink "$gitmeta.tmp"; + } + if ($showdiff) { + print $diff; + } + } + close OUT; + } + # Make sure the .gitmeta file is tracked + system("git add $gitmeta"); +} + + +sub printstats { + my $path = $_[0]; + $path =~ s/@/\@/g; + my (undef,undef,$mode,undef,$uid,$gid) = lstat($path); + $path =~ s/%/\%/g; + if ($stdout) { + print $path; + printf " mode=%04o uid=$uid gid=$gid\n", $mode & 07777; + } + else { + print OUT $path; + printf OUT " mode=%04o uid=$uid gid=$gid\n", $mode & 07777; + } +} -- cgit v1.2.3 From b888d61c8308027433df9c243fa551f42db1c76a Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Mon, 10 Sep 2007 23:03:25 -0400 Subject: Make fetch a builtin Thanks to Johannes Schindelin for review and fixes, and Julian Phillips for the original C translation. This changes a few small bits of behavior: branch..merge is parsed as if it were the lhs of a fetch refspec, and does not have to exactly match the actual lhs of a refspec, so long as it is a valid abbreviation for the same ref. branch..merge is no longer ignored if the remote is configured with a branches/* file. Neither behavior is useful, because there can only be one ref that gets fetched, but this is more consistant. Also, fetch prints different information to standard out. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- contrib/examples/git-fetch.sh | 377 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100755 contrib/examples/git-fetch.sh (limited to 'contrib') diff --git a/contrib/examples/git-fetch.sh b/contrib/examples/git-fetch.sh new file mode 100755 index 0000000000..c3a200120d --- /dev/null +++ b/contrib/examples/git-fetch.sh @@ -0,0 +1,377 @@ +#!/bin/sh +# + +USAGE=' ...' +SUBDIRECTORY_OK=Yes +. git-sh-setup +set_reflog_action "fetch $*" +cd_to_toplevel ;# probably unnecessary... + +. git-parse-remote +_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' +_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" + +LF=' +' +IFS="$LF" + +no_tags= +tags= +append= +force= +verbose= +update_head_ok= +exec= +keep= +shallow_depth= +no_progress= +test -t 1 || no_progress=--no-progress +quiet= +while case "$#" in 0) break ;; esac +do + case "$1" in + -a|--a|--ap|--app|--appe|--appen|--append) + append=t + ;; + --upl|--uplo|--uploa|--upload|--upload-|--upload-p|\ + --upload-pa|--upload-pac|--upload-pack) + shift + exec="--upload-pack=$1" + ;; + --upl=*|--uplo=*|--uploa=*|--upload=*|\ + --upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=*) + exec=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)') + shift + ;; + -f|--f|--fo|--for|--forc|--force) + force=t + ;; + -t|--t|--ta|--tag|--tags) + tags=t + ;; + -n|--n|--no|--no-|--no-t|--no-ta|--no-tag|--no-tags) + no_tags=t + ;; + -u|--u|--up|--upd|--upda|--updat|--update|--update-|--update-h|\ + --update-he|--update-hea|--update-head|--update-head-|\ + --update-head-o|--update-head-ok) + update_head_ok=t + ;; + -q|--q|--qu|--qui|--quie|--quiet) + quiet=--quiet + ;; + -v|--verbose) + verbose="$verbose"Yes + ;; + -k|--k|--ke|--kee|--keep) + keep='-k -k' + ;; + --depth=*) + shallow_depth="--depth=`expr "z$1" : 'z-[^=]*=\(.*\)'`" + ;; + --depth) + shift + shallow_depth="--depth=$1" + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift +done + +case "$#" in +0) + origin=$(get_default_remote) + test -n "$(get_remote_url ${origin})" || + die "Where do you want to fetch from today?" + set x $origin ; shift ;; +esac + +if test -z "$exec" +then + # No command line override and we have configuration for the remote. + exec="--upload-pack=$(get_uploadpack $1)" +fi + +remote_nick="$1" +remote=$(get_remote_url "$@") +refs= +rref= +rsync_slurped_objects= + +if test "" = "$append" +then + : >"$GIT_DIR/FETCH_HEAD" +fi + +# Global that is reused later +ls_remote_result=$(git ls-remote $exec "$remote") || + die "Cannot get the repository state from $remote" + +append_fetch_head () { + flags= + test -n "$verbose" && flags="$flags$LF-v" + test -n "$force$single_force" && flags="$flags$LF-f" + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \ + git fetch--tool $flags append-fetch-head "$@" +} + +# updating the current HEAD with git-fetch in a bare +# repository is always fine. +if test -z "$update_head_ok" && test $(is_bare_repository) = false +then + orig_head=$(git rev-parse --verify HEAD 2>/dev/null) +fi + +# Allow --notags from remote.$1.tagopt +case "$tags$no_tags" in +'') + case "$(git config --get "remote.$1.tagopt")" in + --no-tags) + no_tags=t ;; + esac +esac + +# If --tags (and later --heads or --all) is specified, then we are +# not talking about defaults stored in Pull: line of remotes or +# branches file, and just fetch those and refspecs explicitly given. +# Otherwise we do what we always did. + +reflist=$(get_remote_refs_for_fetch "$@") +if test "$tags" +then + taglist=`IFS=' ' && + echo "$ls_remote_result" | + git show-ref --exclude-existing=refs/tags/ | + while read sha1 name + do + echo ".${name}:${name}" + done` || exit + if test "$#" -gt 1 + then + # remote URL plus explicit refspecs; we need to merge them. + reflist="$reflist$LF$taglist" + else + # No explicit refspecs; fetch tags only. + reflist=$taglist + fi +fi + +fetch_all_at_once () { + + eval=$(echo "$1" | git fetch--tool parse-reflist "-") + eval "$eval" + + ( : subshell because we muck with IFS + IFS=" $LF" + ( + if test "$remote" = . ; then + git show-ref $rref || echo failed "$remote" + elif test -f "$remote" ; then + test -n "$shallow_depth" && + die "shallow clone with bundle is not supported" + git bundle unbundle "$remote" $rref || + echo failed "$remote" + else + if test -d "$remote" && + + # The remote might be our alternate. With + # this optimization we will bypass fetch-pack + # altogether, which means we cannot be doing + # the shallow stuff at all. + test ! -f "$GIT_DIR/shallow" && + test -z "$shallow_depth" && + + # See if all of what we are going to fetch are + # connected to our repository's tips, in which + # case we do not have to do any fetch. + theirs=$(echo "$ls_remote_result" | \ + git fetch--tool -s pick-rref "$rref" "-") && + + # This will barf when $theirs reach an object that + # we do not have in our repository. Otherwise, + # we already have everything the fetch would bring in. + git rev-list --objects $theirs --not --all \ + >/dev/null 2>/dev/null + then + echo "$ls_remote_result" | \ + git fetch--tool pick-rref "$rref" "-" + else + flags= + case $verbose in + YesYes*) + flags="-v" + ;; + esac + git-fetch-pack --thin $exec $keep $shallow_depth \ + $quiet $no_progress $flags "$remote" $rref || + echo failed "$remote" + fi + fi + ) | + ( + flags= + test -n "$verbose" && flags="$flags -v" + test -n "$force" && flags="$flags -f" + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \ + git fetch--tool $flags native-store \ + "$remote" "$remote_nick" "$refs" + ) + ) || exit + +} + +fetch_per_ref () { + reflist="$1" + refs= + rref= + + for ref in $reflist + do + refs="$refs$LF$ref" + + # These are relative path from $GIT_DIR, typically starting at refs/ + # but may be HEAD + if expr "z$ref" : 'z\.' >/dev/null + then + not_for_merge=t + ref=$(expr "z$ref" : 'z\.\(.*\)') + else + not_for_merge= + fi + if expr "z$ref" : 'z+' >/dev/null + then + single_force=t + ref=$(expr "z$ref" : 'z+\(.*\)') + else + single_force= + fi + remote_name=$(expr "z$ref" : 'z\([^:]*\):') + local_name=$(expr "z$ref" : 'z[^:]*:\(.*\)') + + rref="$rref$LF$remote_name" + + # There are transports that can fetch only one head at a time... + case "$remote" in + http://* | https://* | ftp://*) + test -n "$shallow_depth" && + die "shallow clone with http not supported" + proto=`expr "$remote" : '\([^:]*\):'` + if [ -n "$GIT_SSL_NO_VERIFY" ]; then + curl_extra_args="-k" + fi + if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \ + "`git config --bool http.noEPSV`" = true ]; then + noepsv_opt="--disable-epsv" + fi + + # Find $remote_name from ls-remote output. + head=$(echo "$ls_remote_result" | \ + git fetch--tool -s pick-rref "$remote_name" "-") + expr "z$head" : "z$_x40\$" >/dev/null || + die "No such ref $remote_name at $remote" + echo >&2 "Fetching $remote_name from $remote using $proto" + case "$quiet" in '') v=-v ;; *) v= ;; esac + git-http-fetch $v -a "$head" "$remote" || exit + ;; + rsync://*) + test -n "$shallow_depth" && + die "shallow clone with rsync not supported" + TMP_HEAD="$GIT_DIR/TMP_HEAD" + rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1 + head=$(git rev-parse --verify TMP_HEAD) + rm -f "$TMP_HEAD" + case "$quiet" in '') v=-v ;; *) v= ;; esac + test "$rsync_slurped_objects" || { + rsync -a $v --ignore-existing --exclude info \ + "$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit + + # Look at objects/info/alternates for rsync -- http will + # support it natively and git native ones will do it on + # the remote end. Not having that file is not a crime. + rsync -q "$remote/objects/info/alternates" \ + "$GIT_DIR/TMP_ALT" 2>/dev/null || + rm -f "$GIT_DIR/TMP_ALT" + if test -f "$GIT_DIR/TMP_ALT" + then + resolve_alternates "$remote" <"$GIT_DIR/TMP_ALT" | + while read alt + do + case "$alt" in 'bad alternate: '*) die "$alt";; esac + echo >&2 "Getting alternate: $alt" + rsync -av --ignore-existing --exclude info \ + "$alt" "$GIT_OBJECT_DIRECTORY/" || exit + done + rm -f "$GIT_DIR/TMP_ALT" + fi + rsync_slurped_objects=t + } + ;; + esac + + append_fetch_head "$head" "$remote" \ + "$remote_name" "$remote_nick" "$local_name" "$not_for_merge" || exit + + done + +} + +fetch_main () { + case "$remote" in + http://* | https://* | ftp://* | rsync://* ) + fetch_per_ref "$@" + ;; + *) + fetch_all_at_once "$@" + ;; + esac +} + +fetch_main "$reflist" || exit + +# automated tag following +case "$no_tags$tags" in +'') + case "$reflist" in + *:refs/*) + # effective only when we are following remote branch + # using local tracking branch. + taglist=$(IFS=' ' && + echo "$ls_remote_result" | + git show-ref --exclude-existing=refs/tags/ | + while read sha1 name + do + git cat-file -t "$sha1" >/dev/null 2>&1 || continue + echo >&2 "Auto-following $name" + echo ".${name}:${name}" + done) + esac + case "$taglist" in + '') ;; + ?*) + # do not deepen a shallow tree when following tags + shallow_depth= + fetch_main "$taglist" || exit ;; + esac +esac + +# If the original head was empty (i.e. no "master" yet), or +# if we were told not to worry, we do not have to check. +case "$orig_head" in +'') + ;; +?*) + curr_head=$(git rev-parse --verify HEAD 2>/dev/null) + if test "$curr_head" != "$orig_head" + then + git update-ref \ + -m "$GIT_REFLOG_ACTION: Undoing incorrectly fetched HEAD." \ + HEAD "$orig_head" + die "Cannot fetch into the current branch." + fi + ;; +esac -- cgit v1.2.3 From b9fc6ea9efdc988d851666d45d80076839d9c225 Mon Sep 17 00:00:00 2001 From: David Brown Date: Wed, 19 Sep 2007 13:12:48 -0700 Subject: Detect exec bit in more cases. git-p4 was missing the execute bit setting if the file had other attribute bits set. Acked-By: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 55778c5775..65c57ac4d8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -63,6 +63,14 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) +def isP4Exec(kind): + """Determine if a Perforce 'kind' should have execute permission + + 'p4 help filetypes' gives a list of the types. If it starts with 'x', + or x follows one of a few letters. Otherwise, if there is an 'x' after + a plus sign, it is also executable""" + return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) + def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: @@ -916,7 +924,7 @@ class P4Sync(Command): data = file['data'] mode = "644" - if file["type"].startswith("x"): + if isP4Exec(file["type"]): mode = "755" elif file["type"] == "symlink": mode = "120000" -- cgit v1.2.3 From 822f7c7349d61f6075961ce42c1bd1a85cf999e5 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Sun, 23 Sep 2007 22:42:08 +0200 Subject: Supplant the "while case ... break ;; esac" idiom A lot of shell scripts contained stuff starting with while case "$#" in 0) break ;; esac and similar. I consider breaking out of the condition instead of the body od the loop ugly, and the implied "true" value of the non-matching case is not really obvious to humans at first glance. It happens not to be obvious to some BSD shells, either, but that's because they are not POSIX-compliant. In most cases, this has been replaced by a straight condition using "test". "case" has the advantage of being faster than "test" on vintage shells where "test" is not a builtin. Since none of them is likely to run the git scripts, anyway, the added readability should be worth the change. A few loops have had their termination condition expressed differently. Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- contrib/examples/git-gc.sh | 2 +- contrib/examples/git-tag.sh | 2 +- contrib/examples/git-verify-tag.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-gc.sh b/contrib/examples/git-gc.sh index 2ae235b081..1597e9f33f 100755 --- a/contrib/examples/git-gc.sh +++ b/contrib/examples/git-gc.sh @@ -9,7 +9,7 @@ SUBDIRECTORY_OK=Yes . git-sh-setup no_prune=: -while case $# in 0) break ;; esac +while test $# != 0 do case "$1" in --prune) diff --git a/contrib/examples/git-tag.sh b/contrib/examples/git-tag.sh index 5ee3f50a3c..ae7c531666 100755 --- a/contrib/examples/git-tag.sh +++ b/contrib/examples/git-tag.sh @@ -14,7 +14,7 @@ username= list= verify= LINES=0 -while case "$#" in 0) break ;; esac +while test $# != 0 do case "$1" in -a) diff --git a/contrib/examples/git-verify-tag.sh b/contrib/examples/git-verify-tag.sh index 37b0023b27..0902a5c21a 100755 --- a/contrib/examples/git-verify-tag.sh +++ b/contrib/examples/git-verify-tag.sh @@ -5,7 +5,7 @@ SUBDIRECTORY_OK='Yes' . git-sh-setup verbose= -while case $# in 0) break;; esac +while test $# != 0 do case "$1" in -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) -- cgit v1.2.3 From d1637a07f684acd80007723f94c4da9649d85525 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Tue, 25 Sep 2007 08:48:59 +0200 Subject: Do not over-quote the -f envelopesender value. Without this, the value passed to sendmail would have an extra set of single quotes. At least exim's sendmail emulation would object to that: exim: bad -f address "'list-addr@example.org'": malformed address: ' \ may not follow 'list-addr@example.org error: hooks/post-receive exited with error code 1 Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index c589a39a0c..1f88099df4 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -571,6 +571,15 @@ generate_delete_general_email() echo $LOGEND } +send_mail() +{ + if [ -n "$envelopesender" ]; then + /usr/sbin/sendmail -t -f "$envelopesender" + else + /usr/sbin/sendmail -t + fi +} + # ---------------------------- main() # --- Constants @@ -607,13 +616,8 @@ if [ -n "$1" -a -n "$2" -a -n "$3" ]; then # resend an email; they could redirect the output to sendmail themselves PAGER= generate_email $2 $3 $1 else - if [ -n "$envelopesender" ]; then - envelopesender="-f '$envelopesender'" - fi - while read oldrev newrev refname do - generate_email $oldrev $newrev $refname | - /usr/sbin/sendmail -t $envelopesender + generate_email $oldrev $newrev $refname | send_mail done fi -- cgit v1.2.3 From 2ecb5ea2ad375017eedf73bd0130fa9ca33010d2 Mon Sep 17 00:00:00 2001 From: Matt Kraai Date: Tue, 25 Sep 2007 07:03:46 -0700 Subject: Move convert-objects to contrib. convert-objects was needed to convert from an old-style repository, which hashed the compressed contents and used a different date format. Such repositories are presumably no longer common and, if such conversions are necessary, should be done by writing a frontend for git-fast-import. Linus, the original author, is OK with moving it to contrib. Signed-off-by: Matt Kraai Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 - contrib/convert-objects/convert-objects.c | 329 ++++++++++++++++++++++++ contrib/convert-objects/git-convert-objects.txt | 28 ++ 3 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 contrib/convert-objects/convert-objects.c create mode 100644 contrib/convert-objects/git-convert-objects.txt (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index cad842af45..e760930740 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -299,7 +299,6 @@ __git_commands () check-attr) : plumbing;; check-ref-format) : plumbing;; commit-tree) : plumbing;; - convert-objects) : plumbing;; cvsexportcommit) : export;; cvsimport) : import;; cvsserver) : daemon;; diff --git a/contrib/convert-objects/convert-objects.c b/contrib/convert-objects/convert-objects.c new file mode 100644 index 0000000000..90e7900e6d --- /dev/null +++ b/contrib/convert-objects/convert-objects.c @@ -0,0 +1,329 @@ +#include "cache.h" +#include "blob.h" +#include "commit.h" +#include "tree.h" + +struct entry { + unsigned char old_sha1[20]; + unsigned char new_sha1[20]; + int converted; +}; + +#define MAXOBJECTS (1000000) + +static struct entry *convert[MAXOBJECTS]; +static int nr_convert; + +static struct entry * convert_entry(unsigned char *sha1); + +static struct entry *insert_new(unsigned char *sha1, int pos) +{ + struct entry *new = xcalloc(1, sizeof(struct entry)); + hashcpy(new->old_sha1, sha1); + memmove(convert + pos + 1, convert + pos, (nr_convert - pos) * sizeof(struct entry *)); + convert[pos] = new; + nr_convert++; + if (nr_convert == MAXOBJECTS) + die("you're kidding me - hit maximum object limit"); + return new; +} + +static struct entry *lookup_entry(unsigned char *sha1) +{ + int low = 0, high = nr_convert; + + while (low < high) { + int next = (low + high) / 2; + struct entry *n = convert[next]; + int cmp = hashcmp(sha1, n->old_sha1); + if (!cmp) + return n; + if (cmp < 0) { + high = next; + continue; + } + low = next+1; + } + return insert_new(sha1, low); +} + +static void convert_binary_sha1(void *buffer) +{ + struct entry *entry = convert_entry(buffer); + hashcpy(buffer, entry->new_sha1); +} + +static void convert_ascii_sha1(void *buffer) +{ + unsigned char sha1[20]; + struct entry *entry; + + if (get_sha1_hex(buffer, sha1)) + die("expected sha1, got '%s'", (char*) buffer); + entry = convert_entry(sha1); + memcpy(buffer, sha1_to_hex(entry->new_sha1), 40); +} + +static unsigned int convert_mode(unsigned int mode) +{ + unsigned int newmode; + + newmode = mode & S_IFMT; + if (S_ISREG(mode)) + newmode |= (mode & 0100) ? 0755 : 0644; + return newmode; +} + +static int write_subdirectory(void *buffer, unsigned long size, const char *base, int baselen, unsigned char *result_sha1) +{ + char *new = xmalloc(size); + unsigned long newlen = 0; + unsigned long used; + + used = 0; + while (size) { + int len = 21 + strlen(buffer); + char *path = strchr(buffer, ' '); + unsigned char *sha1; + unsigned int mode; + char *slash, *origpath; + + if (!path || strtoul_ui(buffer, 8, &mode)) + die("bad tree conversion"); + mode = convert_mode(mode); + path++; + if (memcmp(path, base, baselen)) + break; + origpath = path; + path += baselen; + slash = strchr(path, '/'); + if (!slash) { + newlen += sprintf(new + newlen, "%o %s", mode, path); + new[newlen++] = '\0'; + hashcpy((unsigned char*)new + newlen, (unsigned char *) buffer + len - 20); + newlen += 20; + + used += len; + size -= len; + buffer = (char *) buffer + len; + continue; + } + + newlen += sprintf(new + newlen, "%o %.*s", S_IFDIR, (int)(slash - path), path); + new[newlen++] = 0; + sha1 = (unsigned char *)(new + newlen); + newlen += 20; + + len = write_subdirectory(buffer, size, origpath, slash-origpath+1, sha1); + + used += len; + size -= len; + buffer = (char *) buffer + len; + } + + write_sha1_file(new, newlen, tree_type, result_sha1); + free(new); + return used; +} + +static void convert_tree(void *buffer, unsigned long size, unsigned char *result_sha1) +{ + void *orig_buffer = buffer; + unsigned long orig_size = size; + + while (size) { + size_t len = 1+strlen(buffer); + + convert_binary_sha1((char *) buffer + len); + + len += 20; + if (len > size) + die("corrupt tree object"); + size -= len; + buffer = (char *) buffer + len; + } + + write_subdirectory(orig_buffer, orig_size, "", 0, result_sha1); +} + +static unsigned long parse_oldstyle_date(const char *buf) +{ + char c, *p; + char buffer[100]; + struct tm tm; + const char *formats[] = { + "%c", + "%a %b %d %T", + "%Z", + "%Y", + " %Y", + NULL + }; + /* We only ever did two timezones in the bad old format .. */ + const char *timezones[] = { + "PDT", "PST", "CEST", NULL + }; + const char **fmt = formats; + + p = buffer; + while (isspace(c = *buf)) + buf++; + while ((c = *buf++) != '\n') + *p++ = c; + *p++ = 0; + buf = buffer; + memset(&tm, 0, sizeof(tm)); + do { + const char *next = strptime(buf, *fmt, &tm); + if (next) { + if (!*next) + return mktime(&tm); + buf = next; + } else { + const char **p = timezones; + while (isspace(*buf)) + buf++; + while (*p) { + if (!memcmp(buf, *p, strlen(*p))) { + buf += strlen(*p); + break; + } + p++; + } + } + fmt++; + } while (*buf && *fmt); + printf("left: %s\n", buf); + return mktime(&tm); +} + +static int convert_date_line(char *dst, void **buf, unsigned long *sp) +{ + unsigned long size = *sp; + char *line = *buf; + char *next = strchr(line, '\n'); + char *date = strchr(line, '>'); + int len; + + if (!next || !date) + die("missing or bad author/committer line %s", line); + next++; date += 2; + + *buf = next; + *sp = size - (next - line); + + len = date - line; + memcpy(dst, line, len); + dst += len; + + /* Is it already in new format? */ + if (isdigit(*date)) { + int datelen = next - date; + memcpy(dst, date, datelen); + return len + datelen; + } + + /* + * Hacky hacky: one of the sparse old-style commits does not have + * any date at all, but we can fake it by using the committer date. + */ + if (*date == '\n' && strchr(next, '>')) + date = strchr(next, '>')+2; + + return len + sprintf(dst, "%lu -0700\n", parse_oldstyle_date(date)); +} + +static void convert_date(void *buffer, unsigned long size, unsigned char *result_sha1) +{ + char *new = xmalloc(size + 100); + unsigned long newlen = 0; + + /* "tree \n" */ + memcpy(new + newlen, buffer, 46); + newlen += 46; + buffer = (char *) buffer + 46; + size -= 46; + + /* "parent \n" */ + while (!memcmp(buffer, "parent ", 7)) { + memcpy(new + newlen, buffer, 48); + newlen += 48; + buffer = (char *) buffer + 48; + size -= 48; + } + + /* "author xyz date" */ + newlen += convert_date_line(new + newlen, &buffer, &size); + /* "committer xyz date" */ + newlen += convert_date_line(new + newlen, &buffer, &size); + + /* Rest */ + memcpy(new + newlen, buffer, size); + newlen += size; + + write_sha1_file(new, newlen, commit_type, result_sha1); + free(new); +} + +static void convert_commit(void *buffer, unsigned long size, unsigned char *result_sha1) +{ + void *orig_buffer = buffer; + unsigned long orig_size = size; + + if (memcmp(buffer, "tree ", 5)) + die("Bad commit '%s'", (char*) buffer); + convert_ascii_sha1((char *) buffer + 5); + buffer = (char *) buffer + 46; /* "tree " + "hex sha1" + "\n" */ + while (!memcmp(buffer, "parent ", 7)) { + convert_ascii_sha1((char *) buffer + 7); + buffer = (char *) buffer + 48; + } + convert_date(orig_buffer, orig_size, result_sha1); +} + +static struct entry * convert_entry(unsigned char *sha1) +{ + struct entry *entry = lookup_entry(sha1); + enum object_type type; + void *buffer, *data; + unsigned long size; + + if (entry->converted) + return entry; + data = read_sha1_file(sha1, &type, &size); + if (!data) + die("unable to read object %s", sha1_to_hex(sha1)); + + buffer = xmalloc(size); + memcpy(buffer, data, size); + + if (type == OBJ_BLOB) { + write_sha1_file(buffer, size, blob_type, entry->new_sha1); + } else if (type == OBJ_TREE) + convert_tree(buffer, size, entry->new_sha1); + else if (type == OBJ_COMMIT) + convert_commit(buffer, size, entry->new_sha1); + else + die("unknown object type %d in %s", type, sha1_to_hex(sha1)); + entry->converted = 1; + free(buffer); + free(data); + return entry; +} + +int main(int argc, char **argv) +{ + unsigned char sha1[20]; + struct entry *entry; + + setup_git_directory(); + + if (argc != 2) + usage("git-convert-objects "); + if (get_sha1(argv[1], sha1)) + die("Not a valid object name %s", argv[1]); + + entry = convert_entry(sha1); + printf("new sha1: %s\n", sha1_to_hex(entry->new_sha1)); + return 0; +} diff --git a/contrib/convert-objects/git-convert-objects.txt b/contrib/convert-objects/git-convert-objects.txt new file mode 100644 index 0000000000..9718abf86d --- /dev/null +++ b/contrib/convert-objects/git-convert-objects.txt @@ -0,0 +1,28 @@ +git-convert-objects(1) +====================== + +NAME +---- +git-convert-objects - Converts old-style git repository + + +SYNOPSIS +-------- +'git-convert-objects' + +DESCRIPTION +----------- +Converts old-style git repository to the latest format + + +Author +------ +Written by Linus Torvalds + +Documentation +-------------- +Documentation by David Greaves, Junio C Hamano and the git-list . + +GIT +--- +Part of the gitlink:git[7] suite -- cgit v1.2.3 From b9b7bab4b6190ef879bb72c7fabf879fd9ca852f Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:58:08 +0200 Subject: git.el: Preserve file marks when doing a full refresh. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2d77fd47ec..7b4a0d3602 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -494,24 +494,31 @@ and returns the process output as a string." (setf (git-fileinfo->orig-name info) nil) (setf (git-fileinfo->needs-refresh info) t)))) -(defun git-set-filenames-state (status files state) - "Set the state of a list of named files." +(defun git-status-filenames-map (status func files &rest args) + "Apply FUNC to the status files names in the FILES list." (when files (setq files (sort files #'string-lessp)) (let ((file (pop files)) (node (ewoc-nth status 0))) (while (and file node) (let ((info (ewoc-data node))) - (cond ((string-lessp (git-fileinfo->name info) file) - (setq node (ewoc-next status node))) - ((string-equal (git-fileinfo->name info) file) - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state) - (setf (git-fileinfo->rename-state info) nil) - (setf (git-fileinfo->orig-name info) nil) - (setf (git-fileinfo->needs-refresh info) t)) - (setq file (pop files))) - (t (setq file (pop files))))))) + (if (string-lessp (git-fileinfo->name info) file) + (setq node (ewoc-next status node)) + (if (string-equal (git-fileinfo->name info) file) + (apply func info args)) + (setq file (pop files)))))))) + +(defun git-set-filenames-state (status files state) + "Set the state of a list of named files." + (when files + (git-status-filenames-map status + (lambda (info state) + (unless (eq (git-fileinfo->state info) state) + (setf (git-fileinfo->state info) state) + (setf (git-fileinfo->rename-state info) nil) + (setf (git-fileinfo->orig-name info) nil) + (setf (git-fileinfo->needs-refresh info) t))) + files state) (unless state ;; delete files whose state has been set to nil (ewoc-filter status (lambda (info) (git-fileinfo->state info)))))) @@ -1197,11 +1204,20 @@ Return the list of files that haven't been handled." (interactive) (let* ((status git-status) (pos (ewoc-locate status)) + (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info))))) (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) (git-run-command nil nil "update-index" "--refresh") (git-clear-status status) (git-update-status-files nil) + ; restore file marks + (when marked-files + (git-status-filenames-map status + (lambda (info) + (setf (git-fileinfo->marked info) t) + (setf (git-fileinfo->needs-refresh info) t)) + marked-files) + (git-refresh-files)) ; move point to the current file name if any (let ((node (and cur-name (git-find-status-file status cur-name)))) (when node (ewoc-goto-node status node))))) -- cgit v1.2.3 From 9f5599b9829615379353f676369018c47296e1a1 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:58:39 +0200 Subject: git.el: Do not print a status message on every git command. Instead print a single message around sequences of commands that can potentially take some time. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 70 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 29 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 7b4a0d3602..ec2e699061 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -220,22 +220,15 @@ and returns the process output as a string." (message "Running git %s...done" (car args)) buffer)) -(defun git-run-command (buffer env &rest args) - (message "Running git %s..." (car args)) - (apply #'git-call-process-env buffer env args) - (message "Running git %s...done" (car args))) - (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (message "Running git %s..." (car args)) (unless (eq 0 (if env (git-run-process-region buffer start end "env" (append (git-get-env-strings env) (list "git") args)) (git-run-process-region buffer start end "git" args))) - (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))) - (message "Running git %s...done" (car args))) + (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) (defun git-run-hook (hook env &rest args) "Run a git hook and display its output if any." @@ -312,6 +305,13 @@ and returns the process output as a string." "\"") name)) +(defun git-success-message (text files) + "Print a success message after having handled FILES." + (let ((n (length files))) + (if (equal n 1) + (message "%s %s" text (car files)) + (message "%s %d files" text n)))) + (defun git-get-top-dir (dir) "Retrieve the top-level directory of a git tree." (let ((cdup (with-output-to-string @@ -338,7 +338,7 @@ and returns the process output as a string." (sort-lines nil (point-min) (point-max)) (save-buffer)) (when created - (git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) + (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) ; propertize definition for XEmacs, stolen from erc-compat @@ -606,7 +606,7 @@ and returns the process output as a string." Return the list of files that haven't been handled." (let (infolist) (with-temp-buffer - (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files) + (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) (while (re-search-forward ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" @@ -639,7 +639,7 @@ Return the list of files that haven't been handled." Return the list of files that haven't been handled." (let (infolist) (with-temp-buffer - (apply #'git-run-command t nil "ls-files" "-z" (append options (list "--") files)) + (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (let ((name (match-string 1))) @@ -651,7 +651,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." (with-temp-buffer - (apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files) + (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files) (goto-char (point-min)) (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) @@ -754,11 +754,11 @@ Return the list of files that haven't been handled." ('deleted (push info deleted)) ('modified (push info modified)))) (when added - (apply #'git-run-command nil env "update-index" "--add" "--" (git-get-filenames added))) + (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added))) (when deleted - (apply #'git-run-command nil env "update-index" "--remove" "--" (git-get-filenames deleted))) + (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted))) (when modified - (apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified))))) + (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified))))) (defun git-run-pre-commit-hook () "Run the pre-commit hook if any." @@ -790,6 +790,7 @@ Return the list of files that haven't been handled." head-tree (git-rev-parse "HEAD^{tree}"))) (if files (progn + (message "Running git commit...") (git-read-tree head-tree index-file) (git-update-index nil files) ;update both the default index (git-update-index index-file files) ;and the temporary one @@ -801,7 +802,7 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) (git-set-files-state files 'uptodate) - (git-run-command nil nil "rerere") + (git-call-process-env nil nil "rerere") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) @@ -912,8 +913,9 @@ Return the list of files that haven't been handled." (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored)))) (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) - (apply #'git-run-command nil nil "update-index" "--add" "--" files) - (git-update-status-files files 'uptodate))) + (apply #'git-call-process-env nil nil "update-index" "--add" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Added" files))) (defun git-ignore-file () "Add marked file(s) to the ignore list." @@ -922,7 +924,8 @@ Return the list of files that haven't been handled." (unless files (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files)) (dolist (f files) (git-append-to-ignore f)) - (git-update-status-files files 'ignored))) + (git-update-status-files files 'ignored) + (git-success-message "Ignored" files))) (defun git-remove-file () "Remove the marked file(s)." @@ -935,8 +938,9 @@ Return the list of files that haven't been handled." (progn (dolist (name files) (when (file-exists-p name) (delete-file name))) - (apply #'git-run-command nil nil "update-index" "--remove" "--" files) - (git-update-status-files files nil)) + (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files) + (git-update-status-files files nil) + (git-success-message "Removed" files)) (message "Aborting")))) (defun git-revert-file () @@ -954,18 +958,20 @@ Return the list of files that haven't been handled." ('unmerged (push (git-fileinfo->name info) modified)) ('modified (push (git-fileinfo->name info) modified)))) (when added - (apply #'git-run-command nil nil "update-index" "--force-remove" "--" added)) + (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added)) (when modified - (apply #'git-run-command nil nil "checkout" "HEAD" modified)) - (git-update-status-files (append added modified) 'uptodate)))) + (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) + (git-update-status-files (append added modified) 'uptodate) + (git-success-message "Reverted" files)))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." (interactive) (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files - (apply #'git-run-command nil nil "update-index" "--" files) - (git-update-status-files files 'uptodate)))) + (apply #'git-call-process-env nil nil "update-index" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Resolved" files)))) (defun git-remove-handled () "Remove handled files from the status list." @@ -992,9 +998,11 @@ Return the list of files that haven't been handled." (interactive) (if (setq git-show-ignored (not git-show-ignored)) (progn + (message "Inserting ignored files...") (git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i") (git-refresh-files) - (git-refresh-ewoc-hf git-status)) + (git-refresh-ewoc-hf git-status) + (message "Inserting ignored files...done")) (git-remove-handled))) (defun git-toggle-show-unknown () @@ -1002,9 +1010,11 @@ Return the list of files that haven't been handled." (interactive) (if (setq git-show-unknown (not git-show-unknown)) (progn + (message "Inserting unknown files...") (git-run-ls-files-with-excludes git-status nil 'unknown "-o") (git-refresh-files) - (git-refresh-ewoc-hf git-status)) + (git-refresh-ewoc-hf git-status) + (message "Inserting unknown files...done")) (git-remove-handled))) (defun git-setup-diff-buffer (buffer) @@ -1207,7 +1217,8 @@ Return the list of files that haven't been handled." (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info))))) (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) - (git-run-command nil nil "update-index" "--refresh") + (message "Refreshing git status...") + (git-call-process-env nil nil "update-index" "--refresh") (git-clear-status status) (git-update-status-files nil) ; restore file marks @@ -1219,6 +1230,7 @@ Return the list of files that haven't been handled." marked-files) (git-refresh-files)) ; move point to the current file name if any + (message "Refreshing git status...done") (let ((node (and cur-name (git-find-status-file status cur-name)))) (when node (ewoc-goto-node status node))))) -- cgit v1.2.3 From 0365d885ad17031de27440fec3553675d02aa4c3 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:59:07 +0200 Subject: git.el: Update a file status in the git buffer upon save. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index ec2e699061..c2a1c3d1a2 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -36,7 +36,6 @@ ;; TODO ;; - portability to XEmacs ;; - better handling of subprocess errors -;; - hook into file save (after-save-hook) ;; - diff against other branch ;; - renaming files from the status buffer ;; - creating tags @@ -1352,9 +1351,24 @@ Commands: (cd dir) (git-status-mode) (git-refresh-status) - (goto-char (point-min))) + (goto-char (point-min)) + (add-hook 'after-save-hook 'git-update-saved-file)) (message "%s is not a git working tree." dir))) +(defun git-update-saved-file () + "Update the corresponding git-status buffer when a file is saved. +Meant to be used in `after-save-hook'." + (let* ((file (expand-file-name buffer-file-name)) + (dir (condition-case nil (git-get-top-dir (file-name-directory file)))) + (buffer (and dir (git-find-status-buffer dir)))) + (when buffer + (with-current-buffer buffer + (let ((filename (file-relative-name file dir))) + ; skip files located inside the .git directory + (unless (string-match "^\\.git/" filename) + (git-call-process-env nil nil "add" "--refresh" "--" filename) + (git-update-status-files (list filename) 'uptodate))))))) + (defun git-help () "Display help for Git mode." (interactive) -- cgit v1.2.3 From 72dc52bfe6c56e37f290dd2c428d82686d6647df Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:59:32 +0200 Subject: git.el: Reset the permission flags when changing a file state. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c2a1c3d1a2..4286d160a0 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -484,14 +484,15 @@ and returns the process output as a string." "Remove everything from the status list." (ewoc-filter status (lambda (info) nil))) -(defun git-set-files-state (files state) - "Set the state of a list of files." - (dolist (info files) - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state) - (setf (git-fileinfo->rename-state info) nil) - (setf (git-fileinfo->orig-name info) nil) - (setf (git-fileinfo->needs-refresh info) t)))) +(defun git-set-fileinfo-state (info state) + "Set the state of a file info." + (unless (eq (git-fileinfo->state info) state) + (setf (git-fileinfo->state info) state + (git-fileinfo->old-perm info) 0 + (git-fileinfo->new-perm info) 0 + (git-fileinfo->rename-state info) nil + (git-fileinfo->orig-name info) nil + (git-fileinfo->needs-refresh info) t))) (defun git-status-filenames-map (status func files &rest args) "Apply FUNC to the status files names in the FILES list." @@ -510,14 +511,7 @@ and returns the process output as a string." (defun git-set-filenames-state (status files state) "Set the state of a list of named files." (when files - (git-status-filenames-map status - (lambda (info state) - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state) - (setf (git-fileinfo->rename-state info) nil) - (setf (git-fileinfo->orig-name info) nil) - (setf (git-fileinfo->needs-refresh info) t))) - files state) + (git-status-filenames-map status #'git-set-fileinfo-state files state) (unless state ;; delete files whose state has been set to nil (ewoc-filter status (lambda (info) (git-fileinfo->state info)))))) @@ -800,7 +794,7 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) - (git-set-files-state files 'uptodate) + (dolist (info files) (git-set-fileinfo-state info 'uptodate)) (git-call-process-env nil nil "rerere") (git-refresh-files) (git-refresh-ewoc-hf git-status) -- cgit v1.2.3 From e6dc8d60fbd2c84900a26545c5d360b0e202d95b Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Fri, 28 Sep 2007 15:24:26 +0100 Subject: post-receive-hook: Remove the From field from the generated email header so that the pusher's name is used Using the name of the committer of the revision at the tip of the updated ref is not sensible. That information is available in the email itself should it be wanted, and by supplying a "From", we were effectively hiding the person who performed the push - which is useful information in itself. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 1f88099df4..cbbd02fadd 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -177,7 +177,6 @@ generate_email_header() # --- Email (all stdout will be the email) # Generate header cat <<-EOF - From: $committer To: $recipients Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe X-Git-Refname: $refname -- cgit v1.2.3 From fdfeb87c14e36d4c49b4481d01cd8bc103ba95f1 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 11 Oct 2007 17:49:21 -0400 Subject: fix contrib/hooks/post-receive-email hooks.recipients error message Have the error message for missing recipients actually report the missing config variable and not a fictional one. Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- contrib/hooks/post-receive-email | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index cbbd02fadd..b188aa3d67 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -138,7 +138,15 @@ generate_email() # Check if we've got anyone to send to if [ -z "$recipients" ]; then - echo >&2 "*** hooks.recipients is not set so no email will be sent" + case "$refname_type" in + "annotated tag") + config_name="hooks.announcelist" + ;; + *) + config_name="hooks.mailinglist" + ;; + esac + echo >&2 "*** $config_name is not set so no email will be sent" echo >&2 "*** for $refname update $oldrev->$newrev" exit 0 fi -- cgit v1.2.3 From 24ccd8b88ee578d8ea1d2a9a7be9ec4cf225fe73 Mon Sep 17 00:00:00 2001 From: Frederick Akalin Date: Fri, 5 Oct 2007 00:20:49 -0700 Subject: gtksourceview2 support for gitview Added support for gtksourceview2 module (pygtksourceview 1.90.x) in gitview. Also refactored code that creates the source buffer and view. Signed-off-by: Frederick Akalin Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- contrib/gitview/gitview | 53 +++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 22 deletions(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 5931766620..449ee69bf4 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -27,12 +27,20 @@ import math import string import fcntl +try: + import gtksourceview2 + have_gtksourceview2 = True +except ImportError: + have_gtksourceview2 = False + try: import gtksourceview have_gtksourceview = True except ImportError: have_gtksourceview = False - print "Running without gtksourceview module" + +if not have_gtksourceview2 and not have_gtksourceview: + print "Running without gtksourceview2 or gtksourceview module" re_ident = re.compile('(author|committer) (?P.*) (?P\d+) (?P[+-]\d{4})') @@ -58,6 +66,26 @@ def show_date(epoch, tz): return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(secs)) +def get_source_buffer_and_view(): + if have_gtksourceview2: + buffer = gtksourceview2.Buffer() + slm = gtksourceview2.LanguageManager() + gsl = slm.get_language("diff") + buffer.set_highlight_syntax(True) + buffer.set_language(gsl) + view = gtksourceview2.View(buffer) + elif have_gtksourceview: + buffer = gtksourceview.SourceBuffer() + slm = gtksourceview.SourceLanguagesManager() + gsl = slm.get_language_from_mime_type("text/x-patch") + buffer.set_highlight(True) + buffer.set_language(gsl) + view = gtksourceview.SourceView(buffer) + else: + buffer = gtk.TextBuffer() + view = gtk.TextView(buffer) + return (buffer, view) + class CellRendererGraph(gtk.GenericCellRenderer): """Cell renderer for directed graph. @@ -582,17 +610,7 @@ class DiffWindow(object): hpan.pack1(scrollwin, True, True) scrollwin.show() - if have_gtksourceview: - self.buffer = gtksourceview.SourceBuffer() - slm = gtksourceview.SourceLanguagesManager() - gsl = slm.get_language_from_mime_type("text/x-patch") - self.buffer.set_highlight(True) - self.buffer.set_language(gsl) - sourceview = gtksourceview.SourceView(self.buffer) - else: - self.buffer = gtk.TextBuffer() - sourceview = gtk.TextView(self.buffer) - + (self.buffer, sourceview) = get_source_buffer_and_view() sourceview.set_editable(False) sourceview.modify_font(pango.FontDescription("Monospace")) @@ -956,16 +974,7 @@ class GitView(object): vbox.pack_start(scrollwin, expand=True, fill=True) scrollwin.show() - if have_gtksourceview: - self.message_buffer = gtksourceview.SourceBuffer() - slm = gtksourceview.SourceLanguagesManager() - gsl = slm.get_language_from_mime_type("text/x-patch") - self.message_buffer.set_highlight(True) - self.message_buffer.set_language(gsl) - sourceview = gtksourceview.SourceView(self.message_buffer) - else: - self.message_buffer = gtk.TextBuffer() - sourceview = gtk.TextView(self.message_buffer) + (self.message_buffer, sourceview) = get_source_buffer_and_view() sourceview.set_editable(False) sourceview.modify_font(pango.FontDescription("Monospace")) -- cgit v1.2.3 From 7c0d741a3e8db662419cc841e3068b2a8880a109 Mon Sep 17 00:00:00 2001 From: Michael Gebetsroither Date: Sat, 6 Oct 2007 23:16:51 +0200 Subject: hg-to-git speedup through selectable repack intervals Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- contrib/hg-to-git/hg-to-git.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 37337ff01f..7a1c3e497f 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -29,6 +29,8 @@ hgvers = {} hgchildren = {} # Current branch for each hg revision hgbranch = {} +# Number of new changesets converted from hg +hgnewcsets = 0 #------------------------------------------------------------------------------ @@ -40,6 +42,8 @@ def usage(): options: -s, --gitstate=FILE: name of the state to be saved/read for incrementals + -n, --nrepack=INT: number of changesets that will trigger + a repack (default=0, -1 to deactivate) required: hgprj: name of the HG project to import (directory) @@ -68,14 +72,16 @@ def getgitenv(user, date): #------------------------------------------------------------------------------ state = '' +opt_nrepack = 0 try: - opts, args = getopt.getopt(sys.argv[1:], 's:t:', ['gitstate=', 'tempdir=']) + opts, args = getopt.getopt(sys.argv[1:], 's:t:n:', ['gitstate=', 'tempdir=', 'nrepack=']) for o, a in opts: if o in ('-s', '--gitstate'): state = a state = os.path.abspath(state) - + if o in ('-n', '--nrepack'): + opt_nrepack = int(a) if len(args) != 1: raise('params') except: @@ -138,6 +144,7 @@ for cset in range(int(tip) + 1): # incremental, already seen if hgvers.has_key(str(cset)): continue + hgnewcsets += 1 # get info prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines() @@ -222,7 +229,8 @@ for cset in range(int(tip) + 1): print 'record', cset, '->', vvv hgvers[str(cset)] = vvv -os.system('git-repack -a -d') +if hgnewcsets >= opt_nrepack and opt_nrepack != -1: + os.system('git-repack -a -d') # write the state for incrementals if state: -- cgit v1.2.3 From a9834f58335b81f48e137beb33afb5bb799cdae8 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Tue, 9 Oct 2007 16:16:09 +0200 Subject: Add 'git-p4 commit' as an alias for 'git-p4 submit' Given that git uses 'commit', git-p4's 'sumbit' was a bit confusing at times; often making me do 'git submit' and 'git-p4 commit' instead. Signed-off-by: Marius Storm-Olsen Acked-By: Simon Hausmann Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- contrib/fast-import/git-p4 | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 557649a14a..52cd2a46ba 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1651,6 +1651,7 @@ def printUsage(commands): commands = { "debug" : P4Debug, "submit" : P4Submit, + "commit" : P4Submit, "sync" : P4Sync, "rebase" : P4Rebase, "clone" : P4Clone, -- cgit v1.2.3 From 03618b9df84a0e94e36fdb27060e605e85b956e9 Mon Sep 17 00:00:00 2001 From: Josh England Date: Tue, 9 Oct 2007 10:04:42 -0600 Subject: Minor usage update in setgitperms.perl Signed-off-by: Josh England Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- contrib/hooks/setgitperms.perl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl index 5e3b89def2..dab7c8e3a1 100644 --- a/contrib/hooks/setgitperms.perl +++ b/contrib/hooks/setgitperms.perl @@ -8,13 +8,14 @@ # To save permissions/ownership data, place this script in your .git/hooks # directory and enable a `pre-commit` hook with the following lines: # #!/bin/sh -# . git-sh-setup +# SUBDIRECTORY_OK=1 . git-sh-setup # $GIT_DIR/hooks/setgitperms.perl -r # # To restore permissions/ownership data, place this script in your .git/hooks -# directory and enable a `post-merge` hook with the following lines: +# directory and enable a `post-merge` and `post-checkout` hook with the +# following lines: # #!/bin/sh -# . git-sh-setup +# SUBDIRECTORY_OK=1 . git-sh-setup # $GIT_DIR/hooks/setgitperms.perl -w # use strict; -- cgit v1.2.3 From a2d6b872dbf4e65525c9ba55e820e2ea26011ce1 Mon Sep 17 00:00:00 2001 From: Robert Schiele Date: Thu, 18 Oct 2007 00:27:51 +0200 Subject: fixing output of non-fast-forward output of post-receive-email post-receive-email has one place where the variable fast_forward is not spelled correctly. At the same place the logic was reversed. The combination of both bugs made the script work correctly for fast-forward commits but not for non-fast-forward ones. This change fixes this to be correct in both cases. Signed-off-by: Robert Schiele Signed-off-by: Shawn O. Pearce --- contrib/hooks/post-receive-email | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index cbbd02fadd..28a06c7f38 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -323,7 +323,7 @@ generate_update_branch_email() echo " via $rev ($revtype)" done - if [ -z "$fastforward" ]; then + if [ "$fast_forward" ]; then echo " from $oldrev ($oldrev_type)" else # 1. Existing revisions were removed. In this case newrev is a -- cgit v1.2.3 From 209471493afbf30d5c1fc80feadfc4836b86dc42 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 13 Sep 2007 22:10:18 +0200 Subject: git-p4: When skipping a patch as part of "git-p4 submit" make sure we correctly revert to the previous state of the files using "p4 revert". Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 52cd2a46ba..e1dc263013 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -529,6 +529,10 @@ class P4Submit(Command): "and with .rej files / [w]rite the patch to a file (patch.txt) ") if response == "s": print "Skipping! Good luck with the next patches..." + for f in editedFiles: + system("p4 revert \"%s\"" % f); + for f in filesToAdd: + system("rm %s" %f) return elif response == "a": os.system(applyPatchCmd) -- cgit v1.2.3 From d9a5f25b67951eb68e80d872611542b58ce5aa33 Mon Sep 17 00:00:00 2001 From: Chris Pettitt Date: Mon, 15 Oct 2007 22:15:06 -0700 Subject: git-p4 support for perforce renames. The current git-p4 implementation does support file renames. However, because it does not use the "p4 integrate" command, the history for the renamed file is not linked to the new file. This changeset adds support for perforce renames with the integrate command. Currently this feature is only enabled when calling git-p4 submit with the -M option. This is intended to look and behave similar to the "detect renames" feature of other git commands. The following sequence is used for renamed files: p4 integrate -Dt x x' p4 edit x' rm x' git apply p4 delete x By default, perforce will not allow an integration with a target file that has been deleted. That is, if x' in the example above is the name of a previously deleted file then perforce will fail the integrate. The -Dt option tells perforce to allow the target of integrate to be a previously deleted file. Signed-off-by: Chris Pettitt Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e1dc263013..bf33f74b70 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -399,6 +399,7 @@ class P4Submit(Command): optparse.make_option("--dry-run", action="store_true"), optparse.make_option("--direct", dest="directSubmit", action="store_true"), optparse.make_option("--trust-me-like-a-fool", dest="trustMeLikeAFool", action="store_true"), + optparse.make_option("-M", dest="detectRename", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -411,6 +412,7 @@ class P4Submit(Command): self.origin = "" self.directSubmit = False self.trustMeLikeAFool = False + self.detectRename = False self.verbose = False self.isWindows = (platform.system() == "Windows") @@ -491,7 +493,8 @@ class P4Submit(Command): diff = self.diffStatus else: print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) - diff = read_pipe_lines("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)) + diffOpts = ("", "-M")[self.detectRename] + diff = read_pipe_lines("git diff-tree -r --name-status %s \"%s^\" \"%s\"" % (diffOpts, id, id)) filesToAdd = set() filesToDelete = set() editedFiles = set() @@ -509,6 +512,13 @@ class P4Submit(Command): filesToDelete.add(path) if path in filesToAdd: filesToAdd.remove(path) + elif modifier == "R": + src, dest = line.strip().split("\t")[1:3] + system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) + system("p4 edit \"%s\"" % (dest)) + os.unlink(dest) + editedFiles.add(dest) + filesToDelete.add(src) else: die("unknown modifier %s for %s" % (modifier, path)) -- cgit v1.2.3 From 3697c5f37a8b7b15d0a3be51d05147654a951115 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:05:11 +0100 Subject: git.el: Fix typo in "Reverted file" message. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 4286d160a0..8cfbdd7be4 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -955,7 +955,7 @@ Return the list of files that haven't been handled." (when modified (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) (git-update-status-files (append added modified) 'uptodate) - (git-success-message "Reverted" files)))) + (git-success-message "Reverted" (git-get-filenames files))))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." -- cgit v1.2.3 From 6df023884b87ef140829d78b67fab90a7f9b1211 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:05:45 +0100 Subject: git.el: Fix typo in git-update-saved-file error handling. Spotted by Matthieu Lemerre. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 8cfbdd7be4..0e5091c1b7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1353,7 +1353,7 @@ Commands: "Update the corresponding git-status buffer when a file is saved. Meant to be used in `after-save-hook'." (let* ((file (expand-file-name buffer-file-name)) - (dir (condition-case nil (git-get-top-dir (file-name-directory file)))) + (dir (condition-case nil (git-get-top-dir (file-name-directory file)) (error nil))) (buffer (and dir (git-find-status-buffer dir)))) (when buffer (with-current-buffer buffer -- cgit v1.2.3 From 2f6e86a86fb9830c0c3205a317f6205f156cfacc Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:06:27 +0100 Subject: git.el: Refresh only the changed file marks when marking/unmarking all. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 0e5091c1b7..e5ee8ce58b 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -842,7 +842,8 @@ Return the list of files that haven't been handled." "Mark all files." (interactive) (unless git-status (error "Not in git-status buffer.")) - (ewoc-map (lambda (info) (setf (git-fileinfo->marked info) t) t) git-status) + (ewoc-map (lambda (info) (unless (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) t))) git-status) ; move back to goal column after invalidate (when goal-column (move-to-column goal-column))) @@ -850,7 +851,9 @@ Return the list of files that haven't been handled." "Unmark all files." (interactive) (unless git-status (error "Not in git-status buffer.")) - (ewoc-map (lambda (info) (setf (git-fileinfo->marked info) nil) t) git-status) + (ewoc-map (lambda (info) (when (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) nil) + t)) git-status) ; move back to goal column after invalidate (when goal-column (move-to-column goal-column))) -- cgit v1.2.3 From d53a35020d380c199b010c9884ab15995f8e982b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:07:14 +0100 Subject: git.el: Run git-gc --auto after commits. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index e5ee8ce58b..e147da0596 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -796,6 +796,7 @@ Return the list of files that haven't been handled." (with-current-buffer buffer (erase-buffer)) (dolist (info files) (git-set-fileinfo-state info 'uptodate)) (git-call-process-env nil nil "rerere") + (git-call-process-env nil nil "gc" "--auto") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) -- cgit v1.2.3 From fee9832a8dea5d9c98c5c3a4797615d52814df16 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 30 Oct 2007 14:24:27 +0000 Subject: No longer install git-svnimport, move to contrib/examples This has been proposed for a few times without much reaction from the list. Actually remove it to see who screams. Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 - contrib/examples/git-svnimport.perl | 976 +++++++++++++++++++++++++++++++++ contrib/examples/git-svnimport.txt | 179 ++++++ 3 files changed, 1155 insertions(+), 1 deletion(-) create mode 100755 contrib/examples/git-svnimport.perl create mode 100644 contrib/examples/git-svnimport.txt (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e760930740..599b2fc571 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -346,7 +346,6 @@ __git_commands () ssh-*) : transport;; stripspace) : plumbing;; svn) : import export;; - svnimport) : import;; symbolic-ref) : plumbing;; tar-tree) : deprecated;; unpack-file) : plumbing;; diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl new file mode 100755 index 0000000000..ea8c1b2f60 --- /dev/null +++ b/contrib/examples/git-svnimport.perl @@ -0,0 +1,976 @@ +#!/usr/bin/perl -w + +# This tool is copyright (c) 2005, Matthias Urlichs. +# It is released under the Gnu Public License, version 2. +# +# The basic idea is to pull and analyze SVN changes. +# +# Checking out the files is done by a single long-running SVN connection. +# +# The head revision is on branch "origin" by default. +# You can change that with the '-o' option. + +use strict; +use warnings; +use Getopt::Std; +use File::Copy; +use File::Spec; +use File::Temp qw(tempfile); +use File::Path qw(mkpath); +use File::Basename qw(basename dirname); +use Time::Local; +use IO::Pipe; +use POSIX qw(strftime dup2); +use IPC::Open2; +use SVN::Core; +use SVN::Ra; + +die "Need SVN:Core 1.2.1 or better" if $SVN::Core::VERSION lt "1.2.1"; + +$SIG{'PIPE'}="IGNORE"; +$ENV{'TZ'}="UTC"; + +our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T, + $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F, + $opt_P,$opt_R); + +sub usage() { + print STDERR <new_default; + +@ARGV == 1 or @ARGV == 2 or usage(); + +$opt_o ||= "origin"; +$opt_s ||= 1; +my $git_tree = $opt_C; +$git_tree ||= "."; + +my $svn_url = $ARGV[0]; +my $svn_dir = $ARGV[1]; + +our @mergerx = (); +if ($opt_m) { + my $branch_esc = quotemeta ($branch_name); + my $trunk_esc = quotemeta ($trunk_name); + @mergerx = + ( + qr!\b(?:merg(?:ed?|ing))\b.*?\b((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i, + qr!\b(?:from|of)\W+((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i, + qr!\b(?:from|of)\W+(?:the )?([\w\.\-]+)[-\s]branch\b!i + ); +} +if ($opt_M) { + unshift (@mergerx, qr/$opt_M/); +} + +# Absolutize filename now, since we will have chdir'ed by the time we +# get around to opening it. +$opt_A = File::Spec->rel2abs($opt_A) if $opt_A; + +our %users = (); +our $users_file = undef; +sub read_users($) { + $users_file = File::Spec->rel2abs(@_); + die "Cannot open $users_file\n" unless -f $users_file; + open(my $authors,$users_file); + while(<$authors>) { + chomp; + next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/; + (my $user,my $name,my $email) = ($1,$2,$3); + $users{$user} = [$name,$email]; + } + close($authors); +} + +select(STDERR); $|=1; select(STDOUT); + + +package SVNconn; +# Basic SVN connection. +# We're only interested in connecting and downloading, so ... + +use File::Spec; +use File::Temp qw(tempfile); +use POSIX qw(strftime dup2); +use Fcntl qw(SEEK_SET); + +sub new { + my($what,$repo) = @_; + $what=ref($what) if ref($what); + + my $self = {}; + $self->{'buffer'} = ""; + bless($self,$what); + + $repo =~ s#/+$##; + $self->{'fullrep'} = $repo; + $self->conn(); + + return $self; +} + +sub conn { + my $self = shift; + my $repo = $self->{'fullrep'}; + my $auth = SVN::Core::auth_open ([SVN::Client::get_simple_provider, + SVN::Client::get_ssl_server_trust_file_provider, + SVN::Client::get_username_provider]); + my $s = SVN::Ra->new(url => $repo, auth => $auth, pool => $root_pool); + die "SVN connection to $repo: $!\n" unless defined $s; + $self->{'svn'} = $s; + $self->{'repo'} = $repo; + $self->{'maxrev'} = $s->get_latest_revnum(); +} + +sub file { + my($self,$path,$rev) = @_; + + my ($fh, $name) = tempfile('gitsvn.XXXXXX', + DIR => File::Spec->tmpdir(), UNLINK => 1); + + print "... $rev $path ...\n" if $opt_v; + my (undef, $properties); + $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; + eval { (undef, $properties) + = $self->{'svn'}->get_file($path,$rev,$fh); }; + if($@) { + return undef if $@ =~ /Attempted to get checksum/; + die $@; + } + my $mode; + if (exists $properties->{'svn:executable'}) { + $mode = '100755'; + } elsif (exists $properties->{'svn:special'}) { + my ($special_content, $filesize); + $filesize = tell $fh; + seek $fh, 0, SEEK_SET; + read $fh, $special_content, $filesize; + if ($special_content =~ s/^link //) { + $mode = '120000'; + seek $fh, 0, SEEK_SET; + truncate $fh, 0; + print $fh $special_content; + } else { + die "unexpected svn:special file encountered"; + } + } else { + $mode = '100644'; + } + close ($fh); + + return ($name, $mode); +} + +sub ignore { + my($self,$path,$rev) = @_; + + print "... $rev $path ...\n" if $opt_v; + $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; + my (undef,undef,$properties) + = $self->{'svn'}->get_dir($path,$rev,undef); + if (exists $properties->{'svn:ignore'}) { + my ($fh, $name) = tempfile('gitsvn.XXXXXX', + DIR => File::Spec->tmpdir(), + UNLINK => 1); + print $fh $properties->{'svn:ignore'}; + close($fh); + return $name; + } else { + return undef; + } +} + +sub dir_list { + my($self,$path,$rev) = @_; + $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; + my ($dirents,undef,$properties) + = $self->{'svn'}->get_dir($path,$rev,undef); + return $dirents; +} + +package main; +use URI; + +our $svn = $svn_url; +$svn .= "/$svn_dir" if defined $svn_dir; +my $svn2 = SVNconn->new($svn); +$svn = SVNconn->new($svn); + +my $lwp_ua; +if($opt_d or $opt_D) { + $svn_url = URI->new($svn_url)->canonical; + if($opt_D) { + $svn_dir =~ s#/*$#/#; + } else { + $svn_dir = ""; + } + if ($svn_url->scheme eq "http") { + use LWP::UserAgent; + $lwp_ua = LWP::UserAgent->new(keep_alive => 1, requests_redirectable => []); + } else { + print STDERR "Warning: not HTTP; turning off direct file access\n"; + $opt_d=0; + } +} + +sub pdate($) { + my($d) = @_; + $d =~ m#(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)# + or die "Unparseable date: $d\n"; + my $y=$1; $y-=1900 if $y>1900; + return timegm($6||0,$5,$4,$3,$2-1,$y); +} + +sub getwd() { + my $pwd = `pwd`; + chomp $pwd; + return $pwd; +} + + +sub get_headref($$) { + my $name = shift; + my $git_dir = shift; + my $sha; + + if (open(C,"$git_dir/refs/heads/$name")) { + chomp($sha = ); + close(C); + length($sha) == 40 + or die "Cannot get head id for $name ($sha): $!\n"; + } + return $sha; +} + + +-d $git_tree + or mkdir($git_tree,0777) + or die "Could not create $git_tree: $!"; +chdir($git_tree); + +my $orig_branch = ""; +my $forward_master = 0; +my %branches; + +my $git_dir = $ENV{"GIT_DIR"} || ".git"; +$git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#; +$ENV{"GIT_DIR"} = $git_dir; +my $orig_git_index; +$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE}; +my ($git_ih, $git_index) = tempfile('gitXXXXXX', SUFFIX => '.idx', + DIR => File::Spec->tmpdir()); +close ($git_ih); +$ENV{GIT_INDEX_FILE} = $git_index; +my $maxnum = 0; +my $last_rev = ""; +my $last_branch; +my $current_rev = $opt_s || 1; +unless(-d $git_dir) { + system("git-init"); + die "Cannot init the GIT db at $git_tree: $?\n" if $?; + system("git-read-tree"); + die "Cannot init an empty tree: $?\n" if $?; + + $last_branch = $opt_o; + $orig_branch = ""; +} else { + -f "$git_dir/refs/heads/$opt_o" + or die "Branch '$opt_o' does not exist.\n". + "Either use the correct '-o branch' option,\n". + "or import to a new repository.\n"; + + -f "$git_dir/svn2git" + or die "'$git_dir/svn2git' does not exist.\n". + "You need that file for incremental imports.\n"; + open(F, "git-symbolic-ref HEAD |") or + die "Cannot run git-symbolic-ref: $!\n"; + chomp ($last_branch = ); + $last_branch = basename($last_branch); + close(F); + unless($last_branch) { + warn "Cannot read the last branch name: $! -- assuming 'master'\n"; + $last_branch = "master"; + } + $orig_branch = $last_branch; + $last_rev = get_headref($orig_branch, $git_dir); + if (-f "$git_dir/SVN2GIT_HEAD") { + die <) { + chomp; + my($num,$branch,$ref) = split; + $branches{$branch}{$num} = $ref; + $branches{$branch}{"LAST"} = $ref; + $current_rev = $num+1 if $current_rev <= $num; + } + close($B); +} +-d $git_dir + or die "Could not create git subdir ($git_dir).\n"; + +my $default_authors = "$git_dir/svn-authors"; +if ($opt_A) { + read_users($opt_A); + copy($opt_A,$default_authors) or die "Copy failed: $!"; +} else { + read_users($default_authors) if -f $default_authors; +} + +open BRANCHES,">>", "$git_dir/svn2git"; + +sub node_kind($$) { + my ($svnpath, $revision) = @_; + $svnpath =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; + my $kind = $svn->{'svn'}->check_path($svnpath,$revision); + return $kind; +} + +sub get_file($$$) { + my($svnpath,$rev,$path) = @_; + + # now get it + my ($name,$mode); + if($opt_d) { + my($req,$res); + + # /svn/!svn/bc/2/django/trunk/django-docs/build.py + my $url=$svn_url->clone(); + $url->path($url->path."/!svn/bc/$rev/$svn_dir$svnpath"); + print "... $path...\n" if $opt_v; + $req = HTTP::Request->new(GET => $url); + $res = $lwp_ua->request($req); + if ($res->is_success) { + my $fh; + ($fh, $name) = tempfile('gitsvn.XXXXXX', + DIR => File::Spec->tmpdir(), UNLINK => 1); + print $fh $res->content; + close($fh) or die "Could not write $name: $!\n"; + } else { + return undef if $res->code == 301; # directory? + die $res->status_line." at $url\n"; + } + $mode = '0644'; # can't obtain mode via direct http request? + } else { + ($name,$mode) = $svn->file("$svnpath",$rev); + return undef unless defined $name; + } + + my $pid = open(my $F, '-|'); + die $! unless defined $pid; + if (!$pid) { + exec("git-hash-object", "-w", $name) + or die "Cannot create object: $!\n"; + } + my $sha = <$F>; + chomp $sha; + close $F; + unlink $name; + return [$mode, $sha, $path]; +} + +sub get_ignore($$$$$) { + my($new,$old,$rev,$path,$svnpath) = @_; + + return unless $opt_I; + my $name = $svn->ignore("$svnpath",$rev); + if ($path eq '/') { + $path = $opt_I; + } else { + $path = File::Spec->catfile($path,$opt_I); + } + if (defined $name) { + my $pid = open(my $F, '-|'); + die $! unless defined $pid; + if (!$pid) { + exec("git-hash-object", "-w", $name) + or die "Cannot create object: $!\n"; + } + my $sha = <$F>; + chomp $sha; + close $F; + unlink $name; + push(@$new,['0644',$sha,$path]); + } elsif (defined $old) { + push(@$old,$path); + } +} + +sub project_path($$) +{ + my ($path, $project) = @_; + + $path = "/".$path unless ($path =~ m#^\/#) ; + return $1 if ($path =~ m#^$project\/(.*)$#); + + $path =~ s#\.#\\\.#g; + $path =~ s#\+#\\\+#g; + return "/" if ($project =~ m#^$path.*$#); + + return undef; +} + +sub split_path($$) { + my($rev,$path) = @_; + my $branch; + + if($path =~ s#^/\Q$tag_name\E/([^/]+)/?##) { + $branch = "/$1"; + } elsif($path =~ s#^/\Q$trunk_name\E/?##) { + $branch = "/"; + } elsif($path =~ s#^/\Q$branch_name\E/([^/]+)/?##) { + $branch = $1; + } else { + my %no_error = ( + "/" => 1, + "/$tag_name" => 1, + "/$branch_name" => 1 + ); + print STDERR "$rev: Unrecognized path: $path\n" unless (defined $no_error{$path}); + return () + } + if ($path eq "") { + $path = "/"; + } elsif ($project_name) { + $path = project_path($path, $project_name); + } + return ($branch,$path); +} + +sub branch_rev($$) { + + my ($srcbranch,$uptorev) = @_; + + my $bbranches = $branches{$srcbranch}; + my @revs = reverse sort { ($a eq 'LAST' ? 0 : $a) <=> ($b eq 'LAST' ? 0 : $b) } keys %$bbranches; + my $therev; + foreach my $arev(@revs) { + next if ($arev eq 'LAST'); + if ($arev <= $uptorev) { + $therev = $arev; + last; + } + } + return $therev; +} + +sub expand_svndir($$$); + +sub expand_svndir($$$) +{ + my ($svnpath, $rev, $path) = @_; + my @list; + get_ignore(\@list, undef, $rev, $path, $svnpath); + my $dirents = $svn->dir_list($svnpath, $rev); + foreach my $p(keys %$dirents) { + my $kind = node_kind($svnpath.'/'.$p, $rev); + if ($kind eq $SVN::Node::file) { + my $f = get_file($svnpath.'/'.$p, $rev, $path.'/'.$p); + push(@list, $f) if $f; + } elsif ($kind eq $SVN::Node::dir) { + push(@list, + expand_svndir($svnpath.'/'.$p, $rev, $path.'/'.$p)); + } + } + return @list; +} + +sub copy_path($$$$$$$$) { + # Somebody copied a whole subdirectory. + # We need to find the index entries from the old version which the + # SVN log entry points to, and add them to the new place. + + my($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_; + + my($srcbranch,$srcpath) = split_path($rev,$oldpath); + unless(defined $srcbranch && defined $srcpath) { + print "Path not found when copying from $oldpath @ $rev.\n". + "Will try to copy from original SVN location...\n" + if $opt_v; + push (@$new, expand_svndir($oldpath, $rev, $path)); + return; + } + my $therev = branch_rev($srcbranch, $rev); + my $gitrev = $branches{$srcbranch}{$therev}; + unless($gitrev) { + print STDERR "$newrev:$newbranch: could not find $oldpath \@ $rev\n"; + return; + } + if ($srcbranch ne $newbranch) { + push(@$parents, $branches{$srcbranch}{'LAST'}); + } + print "$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\n" if $opt_v; + if ($node_kind eq $SVN::Node::dir) { + $srcpath =~ s#/*$#/#; + } + + my $pid = open my $f,'-|'; + die $! unless defined $pid; + if (!$pid) { + exec("git-ls-tree","-r","-z",$gitrev,$srcpath) + or die $!; + } + local $/ = "\0"; + while(<$f>) { + chomp; + my($m,$p) = split(/\t/,$_,2); + my($mode,$type,$sha1) = split(/ /,$m); + next if $type ne "blob"; + if ($node_kind eq $SVN::Node::dir) { + $p = $path . substr($p,length($srcpath)-1); + } else { + $p = $path; + } + push(@$new,[$mode,$sha1,$p]); + } + close($f) or + print STDERR "$newrev:$newbranch: could not list files in $oldpath \@ $rev\n"; +} + +sub commit { + my($branch, $changed_paths, $revision, $author, $date, $message) = @_; + my($committer_name,$committer_email,$dest); + my($author_name,$author_email); + my(@old,@new,@parents); + + if (not defined $author or $author eq "") { + $committer_name = $committer_email = "unknown"; + } elsif (defined $users_file) { + die "User $author is not listed in $users_file\n" + unless exists $users{$author}; + ($committer_name,$committer_email) = @{$users{$author}}; + } elsif ($author =~ /^(.*?)\s+<(.*)>$/) { + ($committer_name, $committer_email) = ($1, $2); + } else { + $author =~ s/^<(.*)>$/$1/; + $committer_name = $committer_email = $author; + } + + if ($opt_F && $message =~ /From:\s+(.*?)\s+<(.*)>\s*\n/) { + ($author_name, $author_email) = ($1, $2); + print "Author from From: $1 <$2>\n" if ($opt_v);; + } elsif ($opt_S && $message =~ /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) { + ($author_name, $author_email) = ($1, $2); + print "Author from Signed-off-by: $1 <$2>\n" if ($opt_v);; + } else { + $author_name = $committer_name; + $author_email = $committer_email; + } + + $date = pdate($date); + + my $tag; + my $parent; + if($branch eq "/") { # trunk + $parent = $opt_o; + } elsif($branch =~ m#^/(.+)#) { # tag + $tag = 1; + $parent = $1; + } else { # "normal" branch + # nothing to do + $parent = $branch; + } + $dest = $parent; + + my $prev = $changed_paths->{"/"}; + if($prev and $prev->[0] eq "A") { + delete $changed_paths->{"/"}; + my $oldpath = $prev->[1]; + my $rev; + if(defined $oldpath) { + my $p; + ($parent,$p) = split_path($revision,$oldpath); + if(defined $parent) { + if($parent eq "/") { + $parent = $opt_o; + } else { + $parent =~ s#^/##; # if it's a tag + } + } + } else { + $parent = undef; + } + } + + my $rev; + if($revision > $opt_s and defined $parent) { + open(H,'-|',"git-rev-parse","--verify",$parent); + $rev = ; + close(H) or do { + print STDERR "$revision: cannot find commit '$parent'!\n"; + return; + }; + chop $rev; + if(length($rev) != 40) { + print STDERR "$revision: cannot find commit '$parent'!\n"; + return; + } + $rev = $branches{($parent eq $opt_o) ? "/" : $parent}{"LAST"}; + if($revision != $opt_s and not $rev) { + print STDERR "$revision: do not know ancestor for '$parent'!\n"; + return; + } + } else { + $rev = undef; + } + +# if($prev and $prev->[0] eq "A") { +# if(not $tag) { +# unless(open(H,"> $git_dir/refs/heads/$branch")) { +# print STDERR "$revision: Could not create branch $branch: $!\n"; +# $state=11; +# next; +# } +# print H "$rev\n" +# or die "Could not write branch $branch: $!"; +# close(H) +# or die "Could not write branch $branch: $!"; +# } +# } + if(not defined $rev) { + unlink($git_index); + } elsif ($rev ne $last_rev) { + print "Switching from $last_rev to $rev ($branch)\n" if $opt_v; + system("git-read-tree", $rev); + die "read-tree failed for $rev: $?\n" if $?; + $last_rev = $rev; + } + + push (@parents, $rev) if defined $rev; + + my $cid; + if($tag and not %$changed_paths) { + $cid = $rev; + } else { + my @paths = sort keys %$changed_paths; + foreach my $path(@paths) { + my $action = $changed_paths->{$path}; + + if ($action->[0] eq "R") { + # refer to a file/tree in an earlier commit + push(@old,$path); # remove any old stuff + } + if(($action->[0] eq "A") || ($action->[0] eq "R")) { + my $node_kind = node_kind($action->[3], $revision); + if ($node_kind eq $SVN::Node::file) { + my $f = get_file($action->[3], + $revision, $path); + if ($f) { + push(@new,$f) if $f; + } else { + my $opath = $action->[3]; + print STDERR "$revision: $branch: could not fetch '$opath'\n"; + } + } elsif ($node_kind eq $SVN::Node::dir) { + if($action->[1]) { + copy_path($revision, $branch, + $path, $action->[1], + $action->[2], $node_kind, + \@new, \@parents); + } else { + get_ignore(\@new, \@old, $revision, + $path, $action->[3]); + } + } + } elsif ($action->[0] eq "D") { + push(@old,$path); + } elsif ($action->[0] eq "M") { + my $node_kind = node_kind($action->[3], $revision); + if ($node_kind eq $SVN::Node::file) { + my $f = get_file($action->[3], + $revision, $path); + push(@new,$f) if $f; + } elsif ($node_kind eq $SVN::Node::dir) { + get_ignore(\@new, \@old, $revision, + $path, $action->[3]); + } + } else { + die "$revision: unknown action '".$action->[0]."' for $path\n"; + } + } + + while(@old) { + my @o1; + if(@old > 55) { + @o1 = splice(@old,0,50); + } else { + @o1 = @old; + @old = (); + } + my $pid = open my $F, "-|"; + die "$!" unless defined $pid; + if (!$pid) { + exec("git-ls-files", "-z", @o1) or die $!; + } + @o1 = (); + local $/ = "\0"; + while(<$F>) { + chomp; + push(@o1,$_); + } + close($F); + + while(@o1) { + my @o2; + if(@o1 > 55) { + @o2 = splice(@o1,0,50); + } else { + @o2 = @o1; + @o1 = (); + } + system("git-update-index","--force-remove","--",@o2); + die "Cannot remove files: $?\n" if $?; + } + } + while(@new) { + my @n2; + if(@new > 12) { + @n2 = splice(@new,0,10); + } else { + @n2 = @new; + @new = (); + } + system("git-update-index","--add", + (map { ('--cacheinfo', @$_) } @n2)); + die "Cannot add files: $?\n" if $?; + } + + my $pid = open(C,"-|"); + die "Cannot fork: $!" unless defined $pid; + unless($pid) { + exec("git-write-tree"); + die "Cannot exec git-write-tree: $!\n"; + } + chomp(my $tree = ); + length($tree) == 40 + or die "Cannot get tree id ($tree): $!\n"; + close(C) + or die "Error running git-write-tree: $?\n"; + print "Tree ID $tree\n" if $opt_v; + + my $pr = IO::Pipe->new() or die "Cannot open pipe: $!\n"; + my $pw = IO::Pipe->new() or die "Cannot open pipe: $!\n"; + $pid = fork(); + die "Fork: $!\n" unless defined $pid; + unless($pid) { + $pr->writer(); + $pw->reader(); + open(OUT,">&STDOUT"); + dup2($pw->fileno(),0); + dup2($pr->fileno(),1); + $pr->close(); + $pw->close(); + + my @par = (); + + # loose detection of merges + # based on the commit msg + foreach my $rx (@mergerx) { + if ($message =~ $rx) { + my $mparent = $1; + if ($mparent eq 'HEAD') { $mparent = $opt_o }; + if ( -e "$git_dir/refs/heads/$mparent") { + $mparent = get_headref($mparent, $git_dir); + push (@parents, $mparent); + print OUT "Merge parent branch: $mparent\n" if $opt_v; + } + } + } + my %seen_parents = (); + my @unique_parents = grep { ! $seen_parents{$_} ++ } @parents; + foreach my $bparent (@unique_parents) { + push @par, '-p', $bparent; + print OUT "Merge parent branch: $bparent\n" if $opt_v; + } + + exec("env", + "GIT_AUTHOR_NAME=$author_name", + "GIT_AUTHOR_EMAIL=$author_email", + "GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), + "GIT_COMMITTER_NAME=$committer_name", + "GIT_COMMITTER_EMAIL=$committer_email", + "GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), + "git-commit-tree", $tree,@par); + die "Cannot exec git-commit-tree: $!\n"; + } + $pw->writer(); + $pr->reader(); + + $message =~ s/[\s\n]+\z//; + $message = "r$revision: $message" if $opt_r; + + print $pw "$message\n" + or die "Error writing to git-commit-tree: $!\n"; + $pw->close(); + + print "Committed change $revision:$branch ".strftime("%Y-%m-%d %H:%M:%S",gmtime($date)).")\n" if $opt_v; + chomp($cid = <$pr>); + length($cid) == 40 + or die "Cannot get commit id ($cid): $!\n"; + print "Commit ID $cid\n" if $opt_v; + $pr->close(); + + waitpid($pid,0); + die "Error running git-commit-tree: $?\n" if $?; + } + + if (not defined $cid) { + $cid = $branches{"/"}{"LAST"}; + } + + if(not defined $dest) { + print "... no known parent\n" if $opt_v; + } elsif(not $tag) { + print "Writing to refs/heads/$dest\n" if $opt_v; + open(C,">$git_dir/refs/heads/$dest") and + print C ("$cid\n") and + close(C) + or die "Cannot write branch $dest for update: $!\n"; + } + + if ($tag) { + $last_rev = "-" if %$changed_paths; + # the tag was 'complex', i.e. did not refer to a "real" revision + + $dest =~ tr/_/\./ if $opt_u; + + system('git-tag', '-f', $dest, $cid) == 0 + or die "Cannot create tag $dest: $!\n"; + + print "Created tag '$dest' on '$branch'\n" if $opt_v; + } + $branches{$branch}{"LAST"} = $cid; + $branches{$branch}{$revision} = $cid; + $last_rev = $cid; + print BRANCHES "$revision $branch $cid\n"; + print "DONE: $revision $dest $cid\n" if $opt_v; +} + +sub commit_all { + # Recursive use of the SVN connection does not work + local $svn = $svn2; + + my ($changed_paths, $revision, $author, $date, $message) = @_; + my %p; + while(my($path,$action) = each %$changed_paths) { + $p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ]; + } + $changed_paths = \%p; + + my %done; + my @col; + my $pref; + my $branch; + + while(my($path,$action) = each %$changed_paths) { + ($branch,$path) = split_path($revision,$path); + next if not defined $branch; + next if not defined $path; + $done{$branch}{$path} = $action; + } + while(($branch,$changed_paths) = each %done) { + commit($branch, $changed_paths, $revision, $author, $date, $message); + } +} + +$opt_l = $svn->{'maxrev'} if not defined $opt_l or $opt_l > $svn->{'maxrev'}; + +if ($opt_l < $current_rev) { + print "Up to date: no new revisions to fetch!\n" if $opt_v; + unlink("$git_dir/SVN2GIT_HEAD"); + exit; +} + +print "Processing from $current_rev to $opt_l ...\n" if $opt_v; + +my $from_rev; +my $to_rev = $current_rev - 1; + +my $subpool = SVN::Pool::new_default_sub; +while ($to_rev < $opt_l) { + $subpool->clear; + $from_rev = $to_rev + 1; + $to_rev = $from_rev + $repack_after; + $to_rev = $opt_l if $opt_l < $to_rev; + print "Fetching from $from_rev to $to_rev ...\n" if $opt_v; + $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all); + my $pid = fork(); + die "Fork: $!\n" unless defined $pid; + unless($pid) { + exec("git-repack", "-d") + or die "Cannot repack: $!\n"; + } + waitpid($pid, 0); +} + + +unlink($git_index); + +if (defined $orig_git_index) { + $ENV{GIT_INDEX_FILE} = $orig_git_index; +} else { + delete $ENV{GIT_INDEX_FILE}; +} + +# Now switch back to the branch we were in before all of this happened +if($orig_branch) { + print "DONE\n" if $opt_v and (not defined $opt_l or $opt_l > 0); + system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") + if $forward_master; + unless ($opt_i) { + system('git-read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD'); + die "read-tree failed: $?\n" if $?; + } +} else { + $orig_branch = "master"; + print "DONE; creating $orig_branch branch\n" if $opt_v and (not defined $opt_l or $opt_l > 0); + system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") + unless -f "$git_dir/refs/heads/master"; + system('git-update-ref', 'HEAD', "$orig_branch"); + unless ($opt_i) { + system('git checkout'); + die "checkout failed: $?\n" if $?; + } +} +unlink("$git_dir/SVN2GIT_HEAD"); +close(BRANCHES); diff --git a/contrib/examples/git-svnimport.txt b/contrib/examples/git-svnimport.txt new file mode 100644 index 0000000000..71aad8b45b --- /dev/null +++ b/contrib/examples/git-svnimport.txt @@ -0,0 +1,179 @@ +git-svnimport(1) +================ +v0.1, July 2005 + +NAME +---- +git-svnimport - Import a SVN repository into git + + +SYNOPSIS +-------- +[verse] +'git-svnimport' [ -o ] [ -h ] [ -v ] [ -d | -D ] + [ -C ] [ -i ] [ -u ] [-l limit_rev] + [ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ] + [ -s start_chg ] [ -m ] [ -r ] [ -M regex ] + [ -I ] [ -A ] + [ -R ] [ -P ] + [ ] + + +DESCRIPTION +----------- +Imports a SVN repository into git. It will either create a new +repository, or incrementally import into an existing one. + +SVN access is done by the SVN::Perl module. + +git-svnimport assumes that SVN repositories are organized into one +"trunk" directory where the main development happens, "branches/FOO" +directories for branches, and "/tags/FOO" directories for tags. +Other subdirectories are ignored. + +git-svnimport creates a file ".git/svn2git", which is required for +incremental SVN imports. + +OPTIONS +------- +-C :: + The GIT repository to import to. If the directory doesn't + exist, it will be created. Default is the current directory. + +-s :: + Start importing at this SVN change number. The default is 1. ++ +When importing incrementally, you might need to edit the .git/svn2git file. + +-i:: + Import-only: don't perform a checkout after importing. This option + ensures the working directory and index remain untouched and will + not create them if they do not exist. + +-T :: + Name the SVN trunk. Default "trunk". + +-t :: + Name the SVN subdirectory for tags. Default "tags". + +-b :: + Name the SVN subdirectory for branches. Default "branches". + +-o :: + The 'trunk' branch from SVN is imported to the 'origin' branch within + the git repository. Use this option if you want to import into a + different branch. + +-r:: + Prepend 'rX: ' to commit messages, where X is the imported + subversion revision. + +-u:: + Replace underscores in tag names with periods. + +-I :: + Import the svn:ignore directory property to files with this + name in each directory. (The Subversion and GIT ignore + syntaxes are similar enough that using the Subversion patterns + directly with "-I .gitignore" will almost always just work.) + +-A :: + Read a file with lines on the form ++ +------ + username = User's Full Name + +------ ++ +and use "User's Full Name " as the GIT +author and committer for Subversion commits made by +"username". If encountering a commit made by a user not in the +list, abort. ++ +For convenience, this data is saved to $GIT_DIR/svn-authors +each time the -A option is provided, and read from that same +file each time git-svnimport is run with an existing GIT +repository without -A. + +-m:: + Attempt to detect merges based on the commit message. This option + will enable default regexes that try to capture the name source + branch name from the commit message. + +-M :: + Attempt to detect merges based on the commit message with a custom + regex. It can be used with -m to also see the default regexes. + You must escape forward slashes. + +-l :: + Specify a maximum revision number to pull. ++ +Formerly, this option controlled how many revisions to pull, +due to SVN memory leaks. (These have been worked around.) + +-R :: + Specify how often git repository should be repacked. ++ +The default value is 1000. git-svnimport will do import in chunks of 1000 +revisions, after each chunk git repository will be repacked. To disable +this behavior specify some big value here which is mote than number of +revisions to import. + +-P :: + Partial import of the SVN tree. ++ +By default, the whole tree on the SVN trunk (/trunk) is imported. +'-P my/proj' will import starting only from '/trunk/my/proj'. +This option is useful when you want to import one project from a +svn repo which hosts multiple projects under the same trunk. + +-v:: + Verbosity: let 'svnimport' report what it is doing. + +-d:: + Use direct HTTP requests if possible. The "" argument is used + only for retrieving the SVN logs; the path to the contents is + included in the SVN log. + +-D:: + Use direct HTTP requests if possible. The "" argument is used + for retrieving the logs, as well as for the contents. ++ +There's no safe way to automatically find out which of these options to +use, so you need to try both. Usually, the one that's wrong will die +with a 40x error pretty quickly. + +:: + The URL of the SVN module you want to import. For local + repositories, use "file:///absolute/path". ++ +If you're using the "-d" or "-D" option, this is the URL of the SVN +repository itself; it usually ends in "/svn". + +:: + The path to the module you want to check out. + +-h:: + Print a short usage message and exit. + +OUTPUT +------ +If '-v' is specified, the script reports what it is doing. + +Otherwise, success is indicated the Unix way, i.e. by simply exiting with +a zero exit status. + +Author +------ +Written by Matthias Urlichs , with help from +various participants of the git-list . + +Based on a cvs2git script by the same author. + +Documentation +-------------- +Documentation by Matthias Urlichs . + +GIT +--- +Part of the gitlink:git[7] suite -- cgit v1.2.3 From b43b0a3c5c313619397b2f81d5a4acad2c3cb4a9 Mon Sep 17 00:00:00 2001 From: Chris Pettitt Date: Thu, 1 Nov 2007 20:43:13 -0700 Subject: git-p4: Add a helper function to parse the full git diff-tree output. Signed-off-by: Chris Pettitt Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 49 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bf33f74b70..c7fc564a5b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -71,6 +71,46 @@ def isP4Exec(kind): a plus sign, it is also executable""" return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) +def diffTreePattern(): + # This is a simple generator for the diff tree regex pattern. This could be + # a class variable if this and parseDiffTreeEntry were a part of a class. + pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)') + while True: + yield pattern + +def parseDiffTreeEntry(entry): + """Parses a single diff tree entry into its component elements. + + See git-diff-tree(1) manpage for details about the format of the diff + output. This method returns a dictionary with the following elements: + + src_mode - The mode of the source file + dst_mode - The mode of the destination file + src_sha1 - The sha1 for the source file + dst_sha1 - The sha1 fr the destination file + status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc) + status_score - The score for the status (applicable for 'C' and 'R' + statuses). This is None if there is no score. + src - The path for the source file. + dst - The path for the destination file. This is only present for + copy or renames. If it is not present, this is None. + + If the pattern is not matched, None is returned.""" + + match = diffTreePattern().next().match(entry) + if match: + return { + 'src_mode': match.group(1), + 'dst_mode': match.group(2), + 'src_sha1': match.group(3), + 'dst_sha1': match.group(4), + 'status': match.group(5), + 'status_score': match.group(6), + 'src': match.group(7), + 'dst': match.group(10) + } + return None + def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: @@ -494,13 +534,14 @@ class P4Submit(Command): else: print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) diffOpts = ("", "-M")[self.detectRename] - diff = read_pipe_lines("git diff-tree -r --name-status %s \"%s^\" \"%s\"" % (diffOpts, id, id)) + diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id)) filesToAdd = set() filesToDelete = set() editedFiles = set() for line in diff: - modifier = line[0] - path = line[1:].strip() + diff = parseDiffTreeEntry(line) + modifier = diff['status'] + path = diff['src'] if modifier == "M": system("p4 edit \"%s\"" % path) editedFiles.add(path) @@ -513,7 +554,7 @@ class P4Submit(Command): if path in filesToAdd: filesToAdd.remove(path) elif modifier == "R": - src, dest = line.strip().split("\t")[1:3] + src, dest = diff['src'], diff['dst'] system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) system("p4 edit \"%s\"" % (dest)) os.unlink(dest) -- cgit v1.2.3 From c65b670e858a0fc010c8b8bb3cbf064bb22e1839 Mon Sep 17 00:00:00 2001 From: Chris Pettitt Date: Thu, 1 Nov 2007 20:43:14 -0700 Subject: git-p4: Detect changes to executable bit and include them in p4 submit. This changeset takes advantage of the new parseDiffTreeEntry(...) function to detect changes to the execute bit in the git repository. During submit, git-p4 now looks for changes to the executable bit and if it finds them it "reopens" the file in perforce, which allows it to change the file type. The logic for adding the executable bit in perforce is straightforward: the +x modifier can be used. Removing the executable bit in perforce requires that the entire filetype be redefined (there is no way to join remove the bit with a -x modifier, for example). This changeset includes logic to remove the executable bit from the full file type while preserving the base file type and other modifiers. Signed-off-by: Chris Pettitt Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c7fc564a5b..c148b5ab7d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -71,6 +71,31 @@ def isP4Exec(kind): a plus sign, it is also executable""" return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) +def setP4ExecBit(file, mode): + # Reopens an already open file and changes the execute bit to match + # the execute bit setting in the passed in mode. + + p4Type = "+x" + + if not isModeExec(mode): + p4Type = getP4OpenedType(file) + p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type) + p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type) + if p4Type[-1] == "+": + p4Type = p4Type[0:-1] + + system("p4 reopen -t %s %s" % (p4Type, file)) + +def getP4OpenedType(file): + # Returns the perforce file type for the given file. + + result = read_pipe("p4 opened %s" % file) + match = re.match(".*\((.+)\)$", result) + if match: + return match.group(1) + else: + die("Could not determine file type for %s" % file) + def diffTreePattern(): # This is a simple generator for the diff tree regex pattern. This could be # a class variable if this and parseDiffTreeEntry were a part of a class. @@ -111,6 +136,14 @@ def parseDiffTreeEntry(entry): } return None +def isModeExec(mode): + # Returns True if the given git mode represents an executable file, + # otherwise False. + return mode[-3:] == "755" + +def isModeExecChanged(src_mode, dst_mode): + return isModeExec(src_mode) != isModeExec(dst_mode) + def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: @@ -538,15 +571,19 @@ class P4Submit(Command): filesToAdd = set() filesToDelete = set() editedFiles = set() + filesToChangeExecBit = {} for line in diff: diff = parseDiffTreeEntry(line) modifier = diff['status'] path = diff['src'] if modifier == "M": system("p4 edit \"%s\"" % path) + if isModeExecChanged(diff['src_mode'], diff['dst_mode']): + filesToChangeExecBit[path] = diff['dst_mode'] editedFiles.add(path) elif modifier == "A": filesToAdd.add(path) + filesToChangeExecBit[path] = diff['dst_mode'] if path in filesToDelete: filesToDelete.remove(path) elif modifier == "D": @@ -557,6 +594,8 @@ class P4Submit(Command): src, dest = diff['src'], diff['dst'] system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) system("p4 edit \"%s\"" % (dest)) + if isModeExecChanged(diff['src_mode'], diff['dst_mode']): + filesToChangeExecBit[dest] = diff['dst_mode'] os.unlink(dest) editedFiles.add(dest) filesToDelete.add(src) @@ -609,6 +648,11 @@ class P4Submit(Command): system("p4 revert \"%s\"" % f) system("p4 delete \"%s\"" % f) + # Set/clear executable bits + for f in filesToChangeExecBit.keys(): + mode = filesToChangeExecBit[f] + setP4ExecBit(f, mode) + logMessage = "" if not self.directSubmit: logMessage = extractLogMessageFromGitCommit(id) -- cgit v1.2.3 From 8951d7c1f1ae38f34617b6c2490bf65e73e371f7 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 4 Nov 2007 15:51:17 -0500 Subject: Build in ls-remote This actually replaces peek-remote with ls-remote, since peek-remote now handles everything. peek-remote remains an a second name for ls-remote, although its help message now gives the "ls-remote" name. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- contrib/examples/git-ls-remote.sh | 142 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100755 contrib/examples/git-ls-remote.sh (limited to 'contrib') diff --git a/contrib/examples/git-ls-remote.sh b/contrib/examples/git-ls-remote.sh new file mode 100755 index 0000000000..fec70bbf88 --- /dev/null +++ b/contrib/examples/git-ls-remote.sh @@ -0,0 +1,142 @@ +#!/bin/sh +# + +usage () { + echo >&2 "usage: $0 [--heads] [--tags] [-u|--upload-pack ]" + echo >&2 " ..." + exit 1; +} + +die () { + echo >&2 "$*" + exit 1 +} + +exec= +while test $# != 0 +do + case "$1" in + -h|--h|--he|--hea|--head|--heads) + heads=heads; shift ;; + -t|--t|--ta|--tag|--tags) + tags=tags; shift ;; + -u|--u|--up|--upl|--uploa|--upload|--upload-|--upload-p|--upload-pa|\ + --upload-pac|--upload-pack) + shift + exec="--upload-pack=$1" + shift;; + -u=*|--u=*|--up=*|--upl=*|--uplo=*|--uploa=*|--upload=*|\ + --upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=*) + exec=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)') + shift;; + --) + shift; break ;; + -*) + usage ;; + *) + break ;; + esac +done + +case "$#" in 0) usage ;; esac + +case ",$heads,$tags," in +,,,) heads=heads tags=tags other=other ;; +esac + +. git-parse-remote +peek_repo="$(get_remote_url "$@")" +shift + +tmp=.ls-remote-$$ +trap "rm -fr $tmp-*" 0 1 2 3 15 +tmpdir=$tmp-d + +case "$peek_repo" in +http://* | https://* | ftp://* ) + if [ -n "$GIT_SSL_NO_VERIFY" -o \ + "`git config --bool http.sslVerify`" = false ]; then + curl_extra_args="-k" + fi + if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \ + "`git config --bool http.noEPSV`" = true ]; then + curl_extra_args="${curl_extra_args} --disable-epsv" + fi + curl -nsf $curl_extra_args --header "Pragma: no-cache" "$peek_repo/info/refs" || + echo "failed slurping" + ;; + +rsync://* ) + mkdir $tmpdir && + rsync -rlq "$peek_repo/HEAD" $tmpdir && + rsync -rq "$peek_repo/refs" $tmpdir || { + echo "failed slurping" + exit + } + head=$(cat "$tmpdir/HEAD") && + case "$head" in + ref:' '*) + head=$(expr "z$head" : 'zref: \(.*\)') && + head=$(cat "$tmpdir/$head") || exit + esac && + echo "$head HEAD" + (cd $tmpdir && find refs -type f) | + while read path + do + tr -d '\012' <"$tmpdir/$path" + echo " $path" + done && + rm -fr $tmpdir + ;; + +* ) + if test -f "$peek_repo" ; then + git bundle list-heads "$peek_repo" || + echo "failed slurping" + else + git-peek-remote $exec "$peek_repo" || + echo "failed slurping" + fi + ;; +esac | +sort -t ' ' -k 2 | +while read sha1 path +do + case "$sha1" in + failed) + exit 1 ;; + esac + case "$path" in + refs/heads/*) + group=heads ;; + refs/tags/*) + group=tags ;; + *) + group=other ;; + esac + case ",$heads,$tags,$other," in + *,$group,*) + ;; + *) + continue;; + esac + case "$#" in + 0) + match=yes ;; + *) + match=no + for pat + do + case "/$path" in + */$pat ) + match=yes + break ;; + esac + done + esac + case "$match" in + no) + continue ;; + esac + echo "$sha1 $path" +done -- cgit v1.2.3 From b5786c828304c7dcf42742729374d04f30ac1a09 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 6 Nov 2007 13:48:07 +0000 Subject: contrib/hooks/post-receive-email: fix typo Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 2aa9bb501c..379cedc577 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -30,7 +30,7 @@ # This is the list that all pushes of annotated tags will go to. Leave it # blank to default to the mailinglist field. The announce emails lists the # short log summary of the changes since the last annotated tag. -# hook.envelopesender +# hooks.envelopesender # If set then the -f option is passed to sendmail to allow the envelope sender # address to be set # -- cgit v1.2.3 From 15a2f530111f32d66eb0c25527e540b5e09804ad Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 6 Nov 2007 13:48:34 +0000 Subject: contrib/hooks/post-receive-email: reformat to wrap comments at 76 chars Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 199 +++++++++++++++++++++------------------ 1 file changed, 107 insertions(+), 92 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 379cedc577..9b9a977771 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -2,24 +2,26 @@ # # Copyright (c) 2007 Andy Parkins # -# An example hook script to mail out commit update information. This hook sends emails -# listing new revisions to the repository introduced by the change being reported. The -# rule is that (for branch updates) each commit will appear on one email and one email -# only. +# An example hook script to mail out commit update information. This hook +# sends emails listing new revisions to the repository introduced by the +# change being reported. The rule is that (for branch updates) each commit +# will appear on one email and one email only. # -# This hook is stored in the contrib/hooks directory. Your distribution will have put -# this somewhere standard. You should make this script executable then link to it in -# the repository you would like to use it in. For example, on debian the hook is stored -# in /usr/share/doc/git-core/contrib/hooks/post-receive-email: +# This hook is stored in the contrib/hooks directory. Your distribution +# will have put this somewhere standard. You should make this script +# executable then link to it in the repository you would like to use it in. +# For example, on debian the hook is stored in +# /usr/share/doc/git-core/contrib/hooks/post-receive-email: # # chmod a+x post-receive-email # cd /path/to/your/repository.git # ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive # -# This hook script assumes it is enabled on the central repository of a project, with -# all users pushing only to it and not between each other. It will still work if you -# don't operate in that style, but it would become possible for the email to be from -# someone other than the person doing the push. +# This hook script assumes it is enabled on the central repository of a +# project, with all users pushing only to it and not between each other. It +# will still work if you don't operate in that style, but it would become +# possible for the email to be from someone other than the person doing the +# push. # # Config # ------ @@ -28,11 +30,11 @@ # emails for every ref update. # hooks.announcelist # This is the list that all pushes of annotated tags will go to. Leave it -# blank to default to the mailinglist field. The announce emails lists the -# short log summary of the changes since the last annotated tag. +# blank to default to the mailinglist field. The announce emails lists +# the short log summary of the changes since the last annotated tag. # hooks.envelopesender -# If set then the -f option is passed to sendmail to allow the envelope sender -# address to be set +# If set then the -f option is passed to sendmail to allow the envelope +# sender address to be set # # Notes # ----- @@ -49,8 +51,8 @@ # this is and calls the appropriate body-generation routine after outputting # the common header # -# Note this function doesn't actually generate any email output, that is taken -# care of by the functions it calls: +# Note this function doesn't actually generate any email output, that is +# taken care of by the functions it calls: # - generate_email_header # - generate_create_XXXX_email # - generate_update_XXXX_email @@ -225,8 +227,9 @@ generate_create_branch_email() echo $LOGBEGIN # This shows all log entries that are not already covered by # another ref - i.e. commits that are now accessible from this - # ref that were previously not accessible (see generate_update_branch_email - # for the explanation of this command) + # ref that were previously not accessible + # (see generate_update_branch_email for the explanation of this + # command) git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --pretty --stdin $newrev echo $LOGEND @@ -254,9 +257,10 @@ generate_update_branch_email() # # git-rev-list N ^O ^X ^N # - # So, we need to build up the list more carefully. git-rev-parse will - # generate a list of revs that may be fed into git-rev-list. We can get - # it to make the "--not --all" part and then filter out the "^N" with: + # So, we need to build up the list more carefully. git-rev-parse + # will generate a list of revs that may be fed into git-rev-list. + # We can get it to make the "--not --all" part and then filter out + # the "^N" with: # # git-rev-parse --not --all | grep -v N # @@ -266,16 +270,17 @@ generate_update_branch_email() # git-rev-list N ^O ^X # # This leaves a problem when someone else updates the repository - # while this script is running. Their new value of the ref we're working - # on would be included in the "--not --all" output; and as our $newrev - # would be an ancestor of that commit, it would exclude all of our - # commits. What we really want is to exclude the current value of - # $refname from the --not list, rather than N itself. So: + # while this script is running. Their new value of the ref we're + # working on would be included in the "--not --all" output; and as + # our $newrev would be an ancestor of that commit, it would exclude + # all of our commits. What we really want is to exclude the current + # value of $refname from the --not list, rather than N itself. So: # # git-rev-parse --not --all | grep -v $(git-rev-parse $refname) # - # Get's us to something pretty safe (apart from the small time between - # refname being read, and git-rev-parse running - for that, I give up) + # Get's us to something pretty safe (apart from the small time + # between refname being read, and git-rev-parse running - for that, + # I give up) # # # Next problem, consider this: @@ -283,18 +288,18 @@ generate_update_branch_email() # \ # * --- X --- * --- N ($newrev) # - # That is to say, there is no guarantee that oldrev is a strict subset of - # newrev (it would have required a --force, but that's allowed). So, we - # can't simply say rev-list $oldrev..$newrev. Instead we find the common - # base of the two revs and list from there. + # That is to say, there is no guarantee that oldrev is a strict + # subset of newrev (it would have required a --force, but that's + # allowed). So, we can't simply say rev-list $oldrev..$newrev. + # Instead we find the common base of the two revs and list from + # there. # - # As above, we need to take into account the presence of X; if another - # branch is already in the repository and points at some of the revisions - # that we are about to output - we don't want them. The solution is as - # before: git-rev-parse output filtered. + # As above, we need to take into account the presence of X; if + # another branch is already in the repository and points at some of + # the revisions that we are about to output - we don't want them. + # The solution is as before: git-rev-parse output filtered. # - # Finally, tags: - # 1 --- 2 --- O --- T --- 3 --- 4 --- N + # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N # # Tags pushed into the repository generate nice shortlog emails that # summarise the commits between them and the previous tag. However, @@ -302,13 +307,14 @@ generate_update_branch_email() # for a branch update. Therefore we still want to output revisions # that have been output on a tag email. # - # Luckily, git-rev-parse includes just the tool. Instead of using "--all" - # we use "--branches"; this has the added benefit that "remotes/" will - # be ignored as well. - - # List all of the revisions that were removed by this update, in a fast forward - # update, this list will be empty, because rev-list O ^N is empty. For a non - # fast forward, O ^N is the list of removed revisions + # Luckily, git-rev-parse includes just the tool. Instead of using + # "--all" we use "--branches"; this has the added benefit that + # "remotes/" will be ignored as well. + + # List all of the revisions that were removed by this update, in a + # fast forward update, this list will be empty, because rev-list O + # ^N is empty. For a non fast forward, O ^N is the list of removed + # revisions fast_forward="" rev="" for rev in $(git rev-list $newrev..$oldrev) @@ -321,10 +327,10 @@ generate_update_branch_email() fi # List all the revisions from baserev to newrev in a kind of - # "table-of-contents"; note this list can include revisions that have - # already had notification emails and is present to show the full detail - # of the change from rolling back the old revision to the base revision and - # then forward to the new revision + # "table-of-contents"; note this list can include revisions that + # have already had notification emails and is present to show the + # full detail of the change from rolling back the old revision to + # the base revision and then forward to the new revision for rev in $(git rev-list $oldrev..$newrev) do revtype=$(git cat-file -t "$rev") @@ -334,19 +340,20 @@ generate_update_branch_email() if [ "$fast_forward" ]; then echo " from $oldrev ($oldrev_type)" else - # 1. Existing revisions were removed. In this case newrev is a - # subset of oldrev - this is the reverse of a fast-forward, - # a rewind - # 2. New revisions were added on top of an old revision, this is - # a rewind and addition. + # 1. Existing revisions were removed. In this case newrev + # is a subset of oldrev - this is the reverse of a + # fast-forward, a rewind + # 2. New revisions were added on top of an old revision, + # this is a rewind and addition. - # (1) certainly happened, (2) possibly. When (2) hasn't happened, - # we set a flag to indicate that no log printout is required. + # (1) certainly happened, (2) possibly. When (2) hasn't + # happened, we set a flag to indicate that no log printout + # is required. echo "" - # Find the common ancestor of the old and new revisions and compare - # it with newrev + # Find the common ancestor of the old and new revisions and + # compare it with newrev baserev=$(git merge-base $oldrev $newrev) rewind_only="" if [ "$baserev" = "$newrev" ]; then @@ -387,21 +394,22 @@ generate_update_branch_email() git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --pretty --stdin $oldrev..$newrev - # XXX: Need a way of detecting whether git rev-list actually outputted - # anything, so that we can issue a "no new revisions added by this - # update" message + # XXX: Need a way of detecting whether git rev-list actually + # outputted anything, so that we can issue a "no new + # revisions added by this update" message echo $LOGEND else echo "No new revisions were added by this update." fi - # The diffstat is shown from the old revision to the new revision. This - # is to show the truth of what happened in this change. There's no point - # showing the stat from the base to the new revision because the base - # is effectively a random revision at this point - the user will be - # interested in what this revision changed - including the undoing of - # previous revisions in the case of non-fast forward updates. + # The diffstat is shown from the old revision to the new revision. + # This is to show the truth of what happened in this change. + # There's no point showing the stat from the base to the new + # revision because the base is effectively a random revision at this + # point - the user will be interested in what this revision changed + # - including the undoing of previous revisions in the case of + # non-fast forward updates. echo "" echo "Summary of changes:" git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev @@ -448,7 +456,8 @@ generate_update_atag_email() # generate_atag_email() { - # Use git-for-each-ref to pull out the individual fields from the tag + # Use git-for-each-ref to pull out the individual fields from the + # tag eval $(git for-each-ref --shell --format=' tagobject=%(*objectname) tagtype=%(*objecttype) @@ -459,8 +468,10 @@ generate_atag_email() echo " tagging $tagobject ($tagtype)" case "$tagtype" in commit) + # If the tagged object is a commit, then we assume this is a - # release, and so we calculate which tag this tag is replacing + # release, and so we calculate which tag this tag is + # replacing prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) if [ -n "$prevtag" ]; then @@ -477,25 +488,27 @@ generate_atag_email() echo "" echo $LOGBEGIN - # Show the content of the tag message; this might contain a change log - # or release notes so is worth displaying. + # Show the content of the tag message; this might contain a change + # log or release notes so is worth displaying. git cat-file tag $newrev | sed -e '1,/^$/d' echo "" case "$tagtype" in commit) - # Only commit tags make sense to have rev-list operations performed - # on them + # Only commit tags make sense to have rev-list operations + # performed on them if [ -n "$prevtag" ]; then # Show changes since the previous release git rev-list --pretty=short "$prevtag..$newrev" | git shortlog else - # No previous tag, show all the changes since time began + # No previous tag, show all the changes since time + # began git rev-list --pretty=short $newrev | git shortlog fi ;; *) - # XXX: Is there anything useful we can do for non-commit objects? + # XXX: Is there anything useful we can do for non-commit + # objects? ;; esac @@ -544,13 +557,14 @@ generate_update_general_email() # generate_general_email() { - # Unannotated tags are more about marking a point than releasing a version; - # therefore we don't do the shortlog summary that we do for annotated tags - # above - we simply show that the point has been marked, and print the log - # message for the marked point for reference purposes + # Unannotated tags are more about marking a point than releasing a + # version; therefore we don't do the shortlog summary that we do for + # annotated tags above - we simply show that the point has been + # marked, and print the log message for the marked point for + # reference purposes # - # Note this section also catches any other reference type (although there - # aren't any) and deals with them in the same way. + # Note this section also catches any other reference type (although + # there aren't any) and deals with them in the same way. echo "" if [ "$newrev_type" = "commit" ]; then @@ -558,10 +572,10 @@ generate_general_email() git show --no-color --root -s $newrev echo $LOGEND else - # What can we do here? The tag marks an object that is not a commit, - # so there is no log for us to display. It's probably not wise to - # output git-cat-file as it could be a binary blob. We'll just say how - # big it is + # What can we do here? The tag marks an object that is not + # a commit, so there is no log for us to display. It's + # probably not wise to output git-cat-file as it could be a + # binary blob. We'll just say how big it is echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." fi } @@ -604,8 +618,8 @@ if [ -z "$GIT_DIR" ]; then fi projectdesc=$(sed -ne '1p' "$GIT_DIR/description") -# Check if the description is unchanged from it's default, and shorten it to a -# more manageable length if it is +# Check if the description is unchanged from it's default, and shorten it to +# a more manageable length if it is if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null then projectdesc="UNNAMED PROJECT" @@ -616,11 +630,12 @@ announcerecipients=$(git repo-config hooks.announcelist) envelopesender=$(git-repo-config hooks.envelopesender) # --- Main loop -# Allow dual mode: run from the command line just like the update hook, or if -# no arguments are given then run as a hook script +# Allow dual mode: run from the command line just like the update hook, or +# if no arguments are given then run as a hook script if [ -n "$1" -a -n "$2" -a -n "$3" ]; then # Output to the terminal in command line mode - if someone wanted to - # resend an email; they could redirect the output to sendmail themselves + # resend an email; they could redirect the output to sendmail + # themselves PAGER= generate_email $2 $3 $1 else while read oldrev newrev refname -- cgit v1.2.3 From e7509ee388480046a685f885431291f484de66de Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 6 Nov 2007 13:49:30 +0000 Subject: contrib/hooks/post-receive-email: make subject prefix configurable Email subjects are prefixed with "[SCM] " by default, make this optionally configurable through the hooks.emailprefix config option. Suggested by martin f krafft through http://bugs.debian.org/428418 Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 9b9a977771..3904c182e7 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -35,10 +35,12 @@ # hooks.envelopesender # If set then the -f option is passed to sendmail to allow the envelope # sender address to be set +# hooks.emailprefix +# All emails have their subjects prefixed with this prefix, or "[SCM]" +# if emailprefix is unset, to aid filtering # # Notes # ----- -# All emails have their subjects prefixed with "[SCM]" to aid filtering. # All emails include the headers "X-Git-Refname", "X-Git-Oldrev", # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and # give information for debugging. @@ -188,7 +190,7 @@ generate_email_header() # Generate header cat <<-EOF To: $recipients - Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe + Subject: ${emailprefix}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe X-Git-Refname: $refname X-Git-Reftype: $refname_type X-Git-Oldrev: $oldrev @@ -604,7 +606,6 @@ send_mail() # ---------------------------- main() # --- Constants -EMAILPREFIX="[SCM] " LOGBEGIN="- Log -----------------------------------------------------------------" LOGEND="-----------------------------------------------------------------------" @@ -628,6 +629,7 @@ fi recipients=$(git repo-config hooks.mailinglist) announcerecipients=$(git repo-config hooks.announcelist) envelopesender=$(git-repo-config hooks.envelopesender) +emailprefix=$(git-repo-config hooks.emailprefix || echo '[SCM] ') # --- Main loop # Allow dual mode: run from the command line just like the update hook, or -- cgit v1.2.3 From 5c355059c379a55366bed573d4e65dba4b5a0565 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Thu, 8 Nov 2007 12:11:57 +0000 Subject: contrib/hooks/post-receive-email: remove cruft, $committer is not used Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 4 ---- 1 file changed, 4 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 3904c182e7..7511ea0797 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -156,10 +156,6 @@ generate_email() fi # Email parameters - # The committer will be obtained from the latest existing rev; so - # for a deletion it will be the oldrev, for the others, then newrev - committer=$(git show --pretty=full -s $rev | sed -ne "s/^Commit: //p" | - sed -ne 's/\(.*\) /dev/null) -- cgit v1.2.3 From 38f9f5ec41803558b4ccefc9db08fac5696aff8d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Nov 2007 10:38:45 +0100 Subject: git-p4: Fix direct import from perforce after fetching changes through git from origin When using an existing git repository to cache the perforce import we don't fetch the branch mapping from perforce as that is a slow operation. However the origin repository may not be fully up-to-date and therefore it may be necessary to import more changes directly from Perforce. Such a direct import needs self.knownBranches to be set up though, so initialize it from the existing p4/* git branches. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c148b5ab7d..c869bb8864 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1207,6 +1207,15 @@ class P4Sync(Command): for branch in lostAndFoundBranches: self.knownBranches[branch] = branch + def getBranchMappingFromGitBranches(self): + branches = p4BranchesInGit(self.importIntoRemotes) + for branch in branches.keys(): + if branch == "master": + branch = "main" + else: + branch = branch[len(self.projectName):] + self.knownBranches[branch] = branch + def listExistingP4GitBranches(self): # branches holds mapping from name to commit branches = p4BranchesInGit(self.importIntoRemotes) @@ -1541,8 +1550,10 @@ class P4Sync(Command): ## FIXME - what's a P4 projectName ? self.projectName = self.guessProjectName() - if not self.hasOrigin: - self.getBranchMapping(); + if self.hasOrigin: + self.getBranchMappingFromGitBranches() + else: + self.getBranchMapping() if self.verbose: print "p4-git branches: %s" % self.p4BranchesInGit print "initial parents: %s" % self.initialParents -- cgit v1.2.3 From 113f10f22f4b3b599e44e192e241e0bace9cc39e Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Sun, 11 Nov 2007 19:48:47 -0600 Subject: Make git-clean a builtin This replaces git-clean.sh with builtin-clean.c, and moves git-clean.sh to the examples. This also introduces a change in behavior when removing directories explicitly specified as a path. For example currently: 1. When dir has only untracked files, these two behave differently: $ git clean -n dir $ git clean -n dir/ the former says "Would not remove dir/", while the latter would say "Would remove dir/untracked" for all paths under it, but not the directory itself. With -d, the former would stop refusing, however since the user explicitly asked to remove the directory the -d is no longer required. 2. When there are more parameters: $ git clean -n dir foo $ git clean -n dir/ foo both cases refuse to remove dir/ unless -d is specified. Once again since both cases requested to remove dir the -d is no longer required. Thanks to Johannes Schindelin for the conversion to using the parse-options API. Signed-off-by: Shawn Bohrer Signed-off-by: Junio C Hamano --- contrib/examples/git-clean.sh | 118 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100755 contrib/examples/git-clean.sh (limited to 'contrib') diff --git a/contrib/examples/git-clean.sh b/contrib/examples/git-clean.sh new file mode 100755 index 0000000000..01c95e9fe8 --- /dev/null +++ b/contrib/examples/git-clean.sh @@ -0,0 +1,118 @@ +#!/bin/sh +# +# Copyright (c) 2005-2006 Pavel Roskin +# + +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-clean [options] ... + +Clean untracked files from the working directory + +When optional ... arguments are given, the paths +affected are further limited to those that match them. +-- +d remove directories as well +f override clean.requireForce and clean anyway +n don't remove anything, just show what would be done +q be quiet, only report errors +x remove ignored files as well +X remove only ignored files" + +SUBDIRECTORY_OK=Yes +. git-sh-setup +require_work_tree + +ignored= +ignoredonly= +cleandir= +rmf="rm -f --" +rmrf="rm -rf --" +rm_refuse="echo Not removing" +echo1="echo" + +disabled=$(git config --bool clean.requireForce) + +while test $# != 0 +do + case "$1" in + -d) + cleandir=1 + ;; + -f) + disabled=false + ;; + -n) + disabled=false + rmf="echo Would remove" + rmrf="echo Would remove" + rm_refuse="echo Would not remove" + echo1=":" + ;; + -q) + echo1=":" + ;; + -x) + ignored=1 + ;; + -X) + ignoredonly=1 + ;; + --) + shift + break + ;; + *) + usage # should not happen + ;; + esac + shift +done + +# requireForce used to default to false but now it defaults to true. +# IOW, lack of explicit "clean.requireForce = false" is taken as +# "clean.requireForce = true". +case "$disabled" in +"") + die "clean.requireForce not set and -n or -f not given; refusing to clean" + ;; +"true") + die "clean.requireForce set and -n or -f not given; refusing to clean" + ;; +esac + +if [ "$ignored,$ignoredonly" = "1,1" ]; then + die "-x and -X cannot be set together" +fi + +if [ -z "$ignored" ]; then + excl="--exclude-per-directory=.gitignore" + excl_info= excludes_file= + if [ -f "$GIT_DIR/info/exclude" ]; then + excl_info="--exclude-from=$GIT_DIR/info/exclude" + fi + if cfg_excl=$(git config core.excludesfile) && test -f "$cfg_excl" + then + excludes_file="--exclude-from=$cfg_excl" + fi + if [ "$ignoredonly" ]; then + excl="$excl --ignored" + fi +fi + +git ls-files --others --directory \ + $excl ${excl_info:+"$excl_info"} ${excludes_file:+"$excludes_file"} \ + -- "$@" | +while read -r file; do + if [ -d "$file" -a ! -L "$file" ]; then + if [ -z "$cleandir" ]; then + $rm_refuse "$file" + continue + fi + $echo1 "Removing $file" + $rmrf "$file" + else + $echo1 "Removing $file" + $rmf "$file" + fi +done -- cgit v1.2.3 From 59adeef48fcc3ea3e1288ce62260fdd8f46240da Mon Sep 17 00:00:00 2001 From: Anton Gyllenberg Date: Mon, 19 Nov 2007 12:37:16 +0200 Subject: gitview: import only one of gtksourceview and gtksourceview2 Importing both gtksourceview and gtksourceview2 will make python segfault on my system (ubuntu 7.10). Change so that gtksourceview is only imported if importing gtksourceview2 fails. This should be safe as gtksourceview is only used if gtksourceview2 is not available. Signed-off-by: Anton Gyllenberg Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 449ee69bf4..4c99dfb903 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -27,20 +27,17 @@ import math import string import fcntl +have_gtksourceview2 = False +have_gtksourceview = False try: import gtksourceview2 have_gtksourceview2 = True except ImportError: - have_gtksourceview2 = False - -try: - import gtksourceview - have_gtksourceview = True -except ImportError: - have_gtksourceview = False - -if not have_gtksourceview2 and not have_gtksourceview: - print "Running without gtksourceview2 or gtksourceview module" + try: + import gtksourceview + have_gtksourceview = True + except ImportError: + print "Running without gtksourceview2 or gtksourceview module" re_ident = re.compile('(author|committer) (?P.*) (?P\d+) (?P[+-]\d{4})') -- cgit v1.2.3 From 183f84365de7b4b1fe0e15cebce80a95023aa1d6 Mon Sep 17 00:00:00 2001 From: Shun Kei Leung Date: Wed, 21 Nov 2007 11:01:19 +0800 Subject: git-p4: Fix typo in --detect-labels Signed-off-by: Kevin Leung Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c869bb8864..c80a6da252 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1141,7 +1141,7 @@ class P4Sync(Command): l = p4CmdList("labels %s..." % ' '.join (self.depotPaths)) if len(l) > 0 and not self.silent: - print "Finding files belonging to labels in %s" % `self.depotPath` + print "Finding files belonging to labels in %s" % `self.depotPaths` for output in l: label = output["label"] -- cgit v1.2.3 From a00a42ae33708caa742d9e9fbf10692cfa42f032 Mon Sep 17 00:00:00 2001 From: Thomas Harning Date: Thu, 22 Nov 2007 15:19:40 -0500 Subject: git-merge-ours: make it a builtin. Except that this fixes a longstanding corner case bug by tightening the way underlying diff-index command is run, it is functionally equivalent to the scripted version. Signed-off-by: Thomas Harning Jr Signed-off-by: Junio C Hamano --- contrib/examples/git-merge-ours.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 contrib/examples/git-merge-ours.sh (limited to 'contrib') diff --git a/contrib/examples/git-merge-ours.sh b/contrib/examples/git-merge-ours.sh new file mode 100755 index 0000000000..c81a790aa6 --- /dev/null +++ b/contrib/examples/git-merge-ours.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# +# Pretend we resolved the heads, but declare our tree trumps everybody else. +# + +# We need to exit with 2 if the index does not match our HEAD tree, +# because the current index is what we will be committing as the +# merge result. + +git diff-index --quiet --cached HEAD || exit 2 + +exit 0 -- cgit v1.2.3 From f5bbc3225c4b073a7ff3218164a0c820299bc9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Thu, 8 Nov 2007 11:59:00 -0500 Subject: Port git commit to C. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes git commit a builtin and moves git-commit.sh to contrib/examples. This also removes the git-runstatus helper, which was mostly just a git-status.sh implementation detail. Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano --- contrib/examples/git-commit.sh | 629 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 629 insertions(+) create mode 100755 contrib/examples/git-commit.sh (limited to 'contrib') diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh new file mode 100755 index 0000000000..485339754c --- /dev/null +++ b/contrib/examples/git-commit.sh @@ -0,0 +1,629 @@ +#!/bin/sh +# +# Copyright (c) 2005 Linus Torvalds +# Copyright (c) 2006 Junio C Hamano + +USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m | -F | (-C|-c) | --amend] [-u] [-e] [--author ] [--template ] [[-i | -o] ...]' +SUBDIRECTORY_OK=Yes +OPTIONS_SPEC= +. git-sh-setup +require_work_tree + +git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t + +case "$0" in +*status) + status_only=t + ;; +*commit) + status_only= + ;; +esac + +refuse_partial () { + echo >&2 "$1" + echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?" + exit 1 +} + +TMP_INDEX= +THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}" +NEXT_INDEX="$GIT_DIR/next-index$$" +rm -f "$NEXT_INDEX" +save_index () { + cp -p "$THIS_INDEX" "$NEXT_INDEX" +} + +run_status () { + # If TMP_INDEX is defined, that means we are doing + # "--only" partial commit, and that index file is used + # to build the tree for the commit. Otherwise, if + # NEXT_INDEX exists, that is the index file used to + # make the commit. Otherwise we are using as-is commit + # so the regular index file is what we use to compare. + if test '' != "$TMP_INDEX" + then + GIT_INDEX_FILE="$TMP_INDEX" + export GIT_INDEX_FILE + elif test -f "$NEXT_INDEX" + then + GIT_INDEX_FILE="$NEXT_INDEX" + export GIT_INDEX_FILE + fi + + if test "$status_only" = "t" -o "$use_status_color" = "t"; then + color= + else + color=--nocolor + fi + git runstatus ${color} \ + ${verbose:+--verbose} \ + ${amend:+--amend} \ + ${untracked_files:+--untracked} +} + +trap ' + test -z "$TMP_INDEX" || { + test -f "$TMP_INDEX" && rm -f "$TMP_INDEX" + } + rm -f "$NEXT_INDEX" +' 0 + +################################################################ +# Command line argument parsing and sanity checking + +all= +also= +interactive= +only= +logfile= +use_commit= +amend= +edit_flag= +no_edit= +log_given= +log_message= +verify=t +quiet= +verbose= +signoff= +force_author= +only_include_assumed= +untracked_files= +templatefile="`git config commit.template`" +while test $# != 0 +do + case "$1" in + -F|--F|-f|--f|--fi|--fil|--file) + case "$#" in 1) usage ;; esac + shift + no_edit=t + log_given=t$log_given + logfile="$1" + ;; + -F*|-f*) + no_edit=t + log_given=t$log_given + logfile="${1#-[Ff]}" + ;; + --F=*|--f=*|--fi=*|--fil=*|--file=*) + no_edit=t + log_given=t$log_given + logfile="${1#*=}" + ;; + -a|--a|--al|--all) + all=t + ;; + --au=*|--aut=*|--auth=*|--autho=*|--author=*) + force_author="${1#*=}" + ;; + --au|--aut|--auth|--autho|--author) + case "$#" in 1) usage ;; esac + shift + force_author="$1" + ;; + -e|--e|--ed|--edi|--edit) + edit_flag=t + ;; + -i|--i|--in|--inc|--incl|--inclu|--includ|--include) + also=t + ;; + --int|--inte|--inter|--intera|--interac|--interact|--interacti|\ + --interactiv|--interactive) + interactive=t + ;; + -o|--o|--on|--onl|--only) + only=t + ;; + -m|--m|--me|--mes|--mess|--messa|--messag|--message) + case "$#" in 1) usage ;; esac + shift + log_given=m$log_given + log_message="${log_message:+${log_message} + +}$1" + no_edit=t + ;; + -m*) + log_given=m$log_given + log_message="${log_message:+${log_message} + +}${1#-m}" + no_edit=t + ;; + --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*) + log_given=m$log_given + log_message="${log_message:+${log_message} + +}${1#*=}" + no_edit=t + ;; + -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\ + --no-verify) + verify= + ;; + --a|--am|--ame|--amen|--amend) + amend=t + use_commit=HEAD + ;; + -c) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit= + ;; + --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\ + --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\ + --reedit-messag=*|--reedit-message=*) + log_given=t$log_given + use_commit="${1#*=}" + no_edit= + ;; + --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\ + --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\ + --reedit-message) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit= + ;; + -C) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit=t + ;; + --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\ + --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\ + --reuse-message=*) + log_given=t$log_given + use_commit="${1#*=}" + no_edit=t + ;; + --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\ + --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit=t + ;; + -s|--s|--si|--sig|--sign|--signo|--signof|--signoff) + signoff=t + ;; + -t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template) + case "$#" in 1) usage ;; esac + shift + templatefile="$1" + no_edit= + ;; + -q|--q|--qu|--qui|--quie|--quiet) + quiet=t + ;; + -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) + verbose=t + ;; + -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\ + --untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\ + --untracked-file|--untracked-files) + untracked_files=t + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift +done +case "$edit_flag" in t) no_edit= ;; esac + +################################################################ +# Sanity check options + +case "$amend,$initial_commit" in +t,t) + die "You do not have anything to amend." ;; +t,) + if [ -f "$GIT_DIR/MERGE_HEAD" ]; then + die "You are in the middle of a merge -- cannot amend." + fi ;; +esac + +case "$log_given" in +tt*) + die "Only one of -c/-C/-F can be used." ;; +*tm*|*mt*) + die "Option -m cannot be combined with -c/-C/-F." ;; +esac + +case "$#,$also,$only,$amend" in +*,t,t,*) + die "Only one of --include/--only can be used." ;; +0,t,,* | 0,,t,) + die "No paths with --include/--only does not make sense." ;; +0,,t,t) + only_include_assumed="# Clever... amending the last one with dirty index." ;; +0,,,*) + ;; +*,,,*) + only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..." + also= + ;; +esac +unset only +case "$all,$interactive,$also,$#" in +*t,*t,*) + die "Cannot use -a, --interactive or -i at the same time." ;; +t,,,[1-9]*) + die "Paths with -a does not make sense." ;; +,t,,[1-9]*) + die "Paths with --interactive does not make sense." ;; +,,t,0) + die "No paths with -i does not make sense." ;; +esac + +if test ! -z "$templatefile" -a -z "$log_given" +then + if test ! -f "$templatefile" + then + die "Commit template file does not exist." + fi +fi + +################################################################ +# Prepare index to have a tree to be committed + +case "$all,$also" in +t,) + if test ! -f "$THIS_INDEX" + then + die 'nothing to commit (use "git add file1 file2" to include for commit)' + fi + save_index && + ( + cd_to_toplevel && + GIT_INDEX_FILE="$NEXT_INDEX" && + export GIT_INDEX_FILE && + git diff-files --name-only -z | + git update-index --remove -z --stdin + ) || exit + ;; +,t) + save_index && + git ls-files --error-unmatch -- "$@" >/dev/null || exit + + git diff-files --name-only -z -- "$@" | + ( + cd_to_toplevel && + GIT_INDEX_FILE="$NEXT_INDEX" && + export GIT_INDEX_FILE && + git update-index --remove -z --stdin + ) || exit + ;; +,) + if test "$interactive" = t; then + git add --interactive || exit + fi + case "$#" in + 0) + ;; # commit as-is + *) + if test -f "$GIT_DIR/MERGE_HEAD" + then + refuse_partial "Cannot do a partial commit during a merge." + fi + + TMP_INDEX="$GIT_DIR/tmp-index$$" + W= + test -z "$initial_commit" && W=--with-tree=HEAD + commit_only=`git ls-files --error-unmatch $W -- "$@"` || exit + + # Build a temporary index and update the real index + # the same way. + if test -z "$initial_commit" + then + GIT_INDEX_FILE="$THIS_INDEX" \ + git read-tree --index-output="$TMP_INDEX" -i -m HEAD + else + rm -f "$TMP_INDEX" + fi || exit + + printf '%s\n' "$commit_only" | + GIT_INDEX_FILE="$TMP_INDEX" \ + git update-index --add --remove --stdin && + + save_index && + printf '%s\n' "$commit_only" | + ( + GIT_INDEX_FILE="$NEXT_INDEX" + export GIT_INDEX_FILE + git update-index --add --remove --stdin + ) || exit + ;; + esac + ;; +esac + +################################################################ +# If we do as-is commit, the index file will be THIS_INDEX, +# otherwise NEXT_INDEX after we make this commit. We leave +# the index as is if we abort. + +if test -f "$NEXT_INDEX" +then + USE_INDEX="$NEXT_INDEX" +else + USE_INDEX="$THIS_INDEX" +fi + +case "$status_only" in +t) + # This will silently fail in a read-only repository, which is + # what we want. + GIT_INDEX_FILE="$USE_INDEX" git update-index -q --unmerged --refresh + run_status + exit $? + ;; +'') + GIT_INDEX_FILE="$USE_INDEX" git update-index -q --refresh || exit + ;; +esac + +################################################################ +# Grab commit message, write out tree and make commit. + +if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit +then + GIT_INDEX_FILE="${TMP_INDEX:-${USE_INDEX}}" "$GIT_DIR"/hooks/pre-commit \ + || exit +fi + +if test "$log_message" != '' +then + printf '%s\n' "$log_message" +elif test "$logfile" != "" +then + if test "$logfile" = - + then + test -t 0 && + echo >&2 "(reading log message from standard input)" + cat + else + cat <"$logfile" + fi +elif test "$use_commit" != "" +then + encoding=$(git config i18n.commitencoding || echo UTF-8) + git show -s --pretty=raw --encoding="$encoding" "$use_commit" | + sed -e '1,/^$/d' -e 's/^ //' +elif test -f "$GIT_DIR/MERGE_MSG" +then + cat "$GIT_DIR/MERGE_MSG" +elif test -f "$GIT_DIR/SQUASH_MSG" +then + cat "$GIT_DIR/SQUASH_MSG" +elif test "$templatefile" != "" +then + cat "$templatefile" +fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG + +case "$signoff" in +t) + sign=$(git-var GIT_COMMITTER_IDENT | sed -e ' + s/>.*/>/ + s/^/Signed-off-by: / + ') + blank_before_signoff= + tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG | + grep 'Signed-off-by:' >/dev/null || blank_before_signoff=' +' + tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG | + grep "$sign"$ >/dev/null || + printf '%s%s\n' "$blank_before_signoff" "$sign" \ + >>"$GIT_DIR"/COMMIT_EDITMSG + ;; +esac + +if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then + echo "#" + echo "# It looks like you may be committing a MERGE." + echo "# If this is not correct, please remove the file" + printf '%s\n' "# $GIT_DIR/MERGE_HEAD" + echo "# and try again" + echo "#" +fi >>"$GIT_DIR"/COMMIT_EDITMSG + +# Author +if test '' != "$use_commit" +then + eval "$(get_author_ident_from_commit "$use_commit")" + export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE +fi +if test '' != "$force_author" +then + GIT_AUTHOR_NAME=`expr "z$force_author" : 'z\(.*[^ ]\) *<.*'` && + GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` && + test '' != "$GIT_AUTHOR_NAME" && + test '' != "$GIT_AUTHOR_EMAIL" || + die "malformed --author parameter" + export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL +fi + +PARENTS="-p HEAD" +if test -z "$initial_commit" +then + rloga='commit' + if [ -f "$GIT_DIR/MERGE_HEAD" ]; then + rloga='commit (merge)' + PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"` + elif test -n "$amend"; then + rloga='commit (amend)' + PARENTS=$(git cat-file commit HEAD | + sed -n -e '/^$/q' -e 's/^parent /-p /p') + fi + current="$(git rev-parse --verify HEAD)" +else + if [ -z "$(git ls-files)" ]; then + echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)' + exit 1 + fi + PARENTS="" + rloga='commit (initial)' + current='' +fi +set_reflog_action "$rloga" + +if test -z "$no_edit" +then + { + echo "" + echo "# Please enter the commit message for your changes." + echo "# (Comment lines starting with '#' will not be included)" + test -z "$only_include_assumed" || echo "$only_include_assumed" + run_status + } >>"$GIT_DIR"/COMMIT_EDITMSG +else + # we need to check if there is anything to commit + run_status >/dev/null +fi +if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ] +then + rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG" + use_status_color=t + run_status + exit 1 +fi + +case "$no_edit" in +'') + git-var GIT_AUTHOR_IDENT > /dev/null || die + git-var GIT_COMMITTER_IDENT > /dev/null || die + git_editor "$GIT_DIR/COMMIT_EDITMSG" + ;; +esac + +case "$verify" in +t) + if test -x "$GIT_DIR"/hooks/commit-msg + then + "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit + fi +esac + +if test -z "$no_edit" +then + sed -e ' + /^diff --git a\/.*/{ + s/// + q + } + /^#/d + ' "$GIT_DIR"/COMMIT_EDITMSG +else + cat "$GIT_DIR"/COMMIT_EDITMSG +fi | +git stripspace >"$GIT_DIR"/COMMIT_MSG + +# Test whether the commit message has any content we didn't supply. +have_commitmsg= +grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG | + git stripspace > "$GIT_DIR"/COMMIT_BAREMSG + +# Is the commit message totally empty? +if test -s "$GIT_DIR"/COMMIT_BAREMSG +then + if test "$templatefile" != "" + then + # Test whether this is just the unaltered template. + if cnt=`sed -e '/^#/d' < "$templatefile" | + git stripspace | + diff "$GIT_DIR"/COMMIT_BAREMSG - | + wc -l` && + test 0 -lt $cnt + then + have_commitmsg=t + fi + else + # No template, so the content in the commit message must + # have come from the user. + have_commitmsg=t + fi +fi + +rm -f "$GIT_DIR"/COMMIT_BAREMSG + +if test "$have_commitmsg" = "t" +then + if test -z "$TMP_INDEX" + then + tree=$(GIT_INDEX_FILE="$USE_INDEX" git write-tree) + else + tree=$(GIT_INDEX_FILE="$TMP_INDEX" git write-tree) && + rm -f "$TMP_INDEX" + fi && + commit=$(git commit-tree $tree $PARENTS <"$GIT_DIR/COMMIT_MSG") && + rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) && + git update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" && + rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" && + if test -f "$NEXT_INDEX" + then + mv "$NEXT_INDEX" "$THIS_INDEX" + else + : ;# happy + fi +else + echo >&2 "* no commit message? aborting commit." + false +fi +ret="$?" +rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG" + +cd_to_toplevel + +git rerere + +if test "$ret" = 0 +then + git gc --auto + if test -x "$GIT_DIR"/hooks/post-commit + then + "$GIT_DIR"/hooks/post-commit + fi + if test -z "$quiet" + then + commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\ + --summary --root HEAD --` + echo "Created${initial_commit:+ initial} commit $commit" + fi +fi + +exit "$ret" -- cgit v1.2.3 From b3a4f8586b4ffa6c896cf0afb2ea49d64faf81ad Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 23 Nov 2007 01:11:35 +0000 Subject: bash completion: add diff options I use "git diff" (the porcelain) really often, and am almost as often annoyed that the completions do not know how to complete something simple as --cached. Now they do. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 599b2fc571..58e0e53cd6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -551,6 +551,20 @@ _git_describe () _git_diff () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --stat --numstat --shortstat --summary + --patch-with-stat --name-only --name-status --color + --no-color --color-words --no-renames --check + --full-index --binary --abbrev --diff-filter + --find-copies-harder --pickaxe-all --pickaxe-regex + --text --ignore-space-at-eol --ignore-space-change + --ignore-all-space --exit-code --quiet --ext-diff + --no-ext-diff" + return + ;; + esac __git_complete_file } -- cgit v1.2.3 From afa75bc8aa1d453d18cc2486ba8fc53e7df92c4d Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 2 Dec 2007 20:40:43 +0100 Subject: contrib: Make remotes2config.sh script more robust The remotes2config.sh script replaced all 'unsafe' characters in repo name with '.'; include '-' in the 'safe' characters set (the set is probably even larger). Script required also space after "URL:", "Push:" and "Pull:" in remotes file. This for example made the following remote URL: git://git.kernel.org/pub/scm/git/git.git Pull: refs/heads/master:refs/heads/origin Pull:+refs/heads/pu:refs/heads/pu miss 'pu' branch (forced branch) in config file after conversion. Allow for any number of whitespace after "URL:", "Push:", "Pull:". Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- contrib/remotes2config.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) mode change 100644 => 100755 contrib/remotes2config.sh (limited to 'contrib') diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh old mode 100644 new mode 100755 index 5838b3ab05..1cda19f66a --- a/contrib/remotes2config.sh +++ b/contrib/remotes2config.sh @@ -11,11 +11,11 @@ if [ -d "$GIT_DIR"/remotes ]; then { cd "$GIT_DIR"/remotes ls | while read f; do - name=$(printf "$f" | tr -c "A-Za-z0-9" ".") + name=$(printf "$f" | tr -c "A-Za-z0-9-" ".") sed -n \ - -e "s/^URL: \(.*\)$/remote.$name.url \1 ./p" \ - -e "s/^Pull: \(.*\)$/remote.$name.fetch \1 ^$ /p" \ - -e "s/^Push: \(.*\)$/remote.$name.push \1 ^$ /p" \ + -e "s/^URL:[ ]*\(.*\)$/remote.$name.url \1 ./p" \ + -e "s/^Pull:[ ]*\(.*\)$/remote.$name.fetch \1 ^$ /p" \ + -e "s/^Push:[ ]*\(.*\)$/remote.$name.push \1 ^$ /p" \ < "$f" done echo done -- cgit v1.2.3 From 90e0653b1824f27559cbc5c9d1f2a00fdb9400ba Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Dec 2007 07:26:29 -0800 Subject: hg-to-git: handle an empty dir in hg. Mark Drago had a subversion repository which was then converted to hg and now is moving in to git. The first commit in the svn repo was just the creation of the empty directory. This made its way in to the hg repository fine, but converting from hg to git would cause an error. The problem was that hg-to-git.py tries to commit the change, git-commit fails, and then hg-to-git.py tries to checkout the new revision and that fails (because it was not created). This may have only caused an error because it was the first commit in the repository. If an empty directory was added in the middle of the repo somewhere things might have worked out fine. This patch will use the new --allow-empty option to git-commit to record such an "empty" commit, to reproduce the history recorded in hg more faithfully. Tested-by: Mark Drago Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 7a1c3e497f..9befb92c41 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -211,7 +211,7 @@ for cset in range(int(tip) + 1): os.system('git-ls-files -x .hg --deleted | git-update-index --remove --stdin') # commit - os.system(getgitenv(user, date) + 'git-commit -a -F %s' % filecomment) + os.system(getgitenv(user, date) + 'git commit --allow-empty -a -F %s' % filecomment) os.unlink(filecomment) # tag -- cgit v1.2.3 From 18ff365fcb3b16d6802c04da03ceda3a260c0ab9 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 11 Dec 2007 13:56:09 +0100 Subject: git.el: Added a menu for git-status-mode. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally written by Rémi Vanicat, I just changed the layout a little. Signed-off-by: Rémi Vanicat Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index e147da0596..28a4899a0a 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -49,6 +49,7 @@ (eval-when-compile (require 'cl)) (require 'ewoc) (require 'log-edit) +(require 'easymenu) ;;;; Customizations @@ -1297,7 +1298,47 @@ Return the list of files that haven't been handled." (define-key toggle-map "i" 'git-toggle-show-ignored) (define-key toggle-map "k" 'git-toggle-show-unknown) (define-key toggle-map "m" 'git-toggle-all-marks) - (setq git-status-mode-map map))) + (setq git-status-mode-map map)) + (easy-menu-define git-menu git-status-mode-map + "Git Menu" + `("Git" + ["Refresh" git-refresh-status t] + ["Commit" git-commit-file t] + ("Merge" + ["Next Unmerged File" git-next-unmerged-file t] + ["Prev Unmerged File" git-prev-unmerged-file t] + ["Mark as Resolved" git-resolve-file t] + ["Interactive Merge File" git-find-file-imerge t] + ["Diff Against Common Base File" git-diff-file-base t] + ["Diff Combined" git-diff-file-combined t] + ["Diff Against Merge Head" git-diff-file-merge-head t] + ["Diff Against Mine" git-diff-file-mine t] + ["Diff Against Other" git-diff-file-other t]) + "--------" + ["Add File" git-add-file t] + ["Revert File" git-revert-file t] + ["Ignore File" git-ignore-file t] + ["Remove File" git-remove-file t] + "--------" + ["Find File" git-find-file t] + ["View File" git-view-file t] + ["Diff File" git-diff-file t] + ["Interactive Diff File" git-diff-file-idiff t] + ["Log" git-log-file t] + "--------" + ["Mark" git-mark-file t] + ["Mark All" git-mark-all t] + ["Unmark" git-unmark-file t] + ["Unmark All" git-unmark-all t] + ["Toggle All Marks" git-toggle-all-marks t] + ["Hide Handled Files" git-remove-handled t] + "--------" + ["Show Uptodate Files" git-toggle-show-uptodate :style toggle :selected git-show-uptodate] + ["Show Ignored Files" git-toggle-show-ignored :style toggle :selected git-show-ignored] + ["Show Unknown Files" git-toggle-show-unknown :style toggle :selected git-show-unknown] + "--------" + ["Quit" git-status-quit t]))) + ;; git mode should only run in the *git status* buffer (put 'git-status-mode 'mode-class 'special) -- cgit v1.2.3 From 718a087a47cc148f74027a3a26d71994ff71bdd8 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 15 Dec 2007 06:11:54 -0500 Subject: teach bash completion to treat commands with "--" as a helper There is a convention that commands containing a double-dash are implementation details and not to be used by mortals. We should automatically remove them from the completion suggestions as such. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 58e0e53cd6..343364de04 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -291,7 +291,7 @@ __git_commands () for i in $(git help -a|egrep '^ ') do case $i in - add--interactive) : plumbing;; + *--*) : helper pattern;; applymbox) : ask gittus;; applypatch) : ask gittus;; archimport) : import;; @@ -308,7 +308,6 @@ __git_commands () diff-tree) : plumbing;; fast-import) : import;; fsck-objects) : plumbing;; - fetch--tool) : plumbing;; fetch-pack) : plumbing;; fmt-merge-msg) : plumbing;; for-each-ref) : plumbing;; -- cgit v1.2.3 From e25cfae6677423d05a81c0862f58d3319141794c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Dec 2007 17:38:00 -0800 Subject: contrib: resurrect scripted git-revert. Signed-off-by: Junio C Hamano --- contrib/examples/git-revert.sh | 197 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100755 contrib/examples/git-revert.sh (limited to 'contrib') diff --git a/contrib/examples/git-revert.sh b/contrib/examples/git-revert.sh new file mode 100755 index 0000000000..49f00321b2 --- /dev/null +++ b/contrib/examples/git-revert.sh @@ -0,0 +1,197 @@ +#!/bin/sh +# +# Copyright (c) 2005 Linus Torvalds +# Copyright (c) 2005 Junio C Hamano +# + +case "$0" in +*-revert* ) + test -t 0 && edit=-e + replay= + me=revert + USAGE='[--edit | --no-edit] [-n] ' ;; +*-cherry-pick* ) + replay=t + edit= + me=cherry-pick + USAGE='[--edit] [-n] [-r] [-x] ' ;; +* ) + echo >&2 "What are you talking about?" + exit 1 ;; +esac + +SUBDIRECTORY_OK=Yes ;# we will cd up +. git-sh-setup +require_work_tree +cd_to_toplevel + +no_commit= +while case "$#" in 0) break ;; esac +do + case "$1" in + -n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\ + --no-commi|--no-commit) + no_commit=t + ;; + -e|--e|--ed|--edi|--edit) + edit=-e + ;; + --n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit) + edit= + ;; + -r) + : no-op ;; + -x|--i-really-want-to-expose-my-private-commit-object-name) + replay= + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift +done + +set_reflog_action "$me" + +test "$me,$replay" = "revert,t" && usage + +case "$no_commit" in +t) + # We do not intend to commit immediately. We just want to + # merge the differences in. + head=$(git-write-tree) || + die "Your index file is unmerged." + ;; +*) + head=$(git-rev-parse --verify HEAD) || + die "You do not have a valid HEAD" + files=$(git-diff-index --cached --name-only $head) || exit + if [ "$files" ]; then + die "Dirty index: cannot $me (dirty: $files)" + fi + ;; +esac + +rev=$(git-rev-parse --verify "$@") && +commit=$(git-rev-parse --verify "$rev^0") || + die "Not a single commit $@" +prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) || + die "Cannot run $me a root commit" +git-rev-parse --verify "$commit^2" >/dev/null 2>&1 && + die "Cannot run $me a multi-parent commit." + +encoding=$(git config i18n.commitencoding || echo UTF-8) + +# "commit" is an existing commit. We would want to apply +# the difference it introduces since its first parent "prev" +# on top of the current HEAD if we are cherry-pick. Or the +# reverse of it if we are revert. + +case "$me" in +revert) + git show -s --pretty=oneline --encoding="$encoding" $commit | + sed -e ' + s/^[^ ]* /Revert "/ + s/$/"/ + ' + echo + echo "This reverts commit $commit." + test "$rev" = "$commit" || + echo "(original 'git revert' arguments: $@)" + base=$commit next=$prev + ;; + +cherry-pick) + pick_author_script=' + /^author /{ + s/'\''/'\''\\'\'\''/g + h + s/^author \([^<]*\) <[^>]*> .*$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/GIT_AUTHOR_NAME='\''&'\''/p + + g + s/^author [^<]* <\([^>]*\)> .*$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p + + g + s/^author [^<]* <[^>]*> \(.*\)$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/GIT_AUTHOR_DATE='\''&'\''/p + + q + }' + + logmsg=`git show -s --pretty=raw --encoding="$encoding" "$commit"` + set_author_env=`echo "$logmsg" | + LANG=C LC_ALL=C sed -ne "$pick_author_script"` + eval "$set_author_env" + export GIT_AUTHOR_NAME + export GIT_AUTHOR_EMAIL + export GIT_AUTHOR_DATE + + echo "$logmsg" | + sed -e '1,/^$/d' -e 's/^ //' + case "$replay" in + '') + echo "(cherry picked from commit $commit)" + test "$rev" = "$commit" || + echo "(original 'git cherry-pick' arguments: $@)" + ;; + esac + base=$prev next=$commit + ;; + +esac >.msg + +eval GITHEAD_$head=HEAD +eval GITHEAD_$next='`git show -s \ + --pretty=oneline --encoding="$encoding" "$commit" | + sed -e "s/^[^ ]* //"`' +export GITHEAD_$head GITHEAD_$next + +# This three way merge is an interesting one. We are at +# $head, and would want to apply the change between $commit +# and $prev on top of us (when reverting), or the change between +# $prev and $commit on top of us (when cherry-picking or replaying). + +git-merge-recursive $base -- $head $next && +result=$(git-write-tree 2>/dev/null) || { + mv -f .msg "$GIT_DIR/MERGE_MSG" + { + echo ' +Conflicts: +' + git ls-files --unmerged | + sed -e 's/^[^ ]* / /' | + uniq + } >>"$GIT_DIR/MERGE_MSG" + echo >&2 "Automatic $me failed. After resolving the conflicts," + echo >&2 "mark the corrected paths with 'git-add '" + echo >&2 "and commit the result." + case "$me" in + cherry-pick) + echo >&2 "You may choose to use the following when making" + echo >&2 "the commit:" + echo >&2 "$set_author_env" + esac + exit 1 +} +echo >&2 "Finished one $me." + +# If we are cherry-pick, and if the merge did not result in +# hand-editing, we will hit this commit and inherit the original +# author date and name. +# If we are revert, or if our cherry-pick results in a hand merge, +# we had better say that the current user is responsible for that. + +case "$no_commit" in +'') + git-commit -n -F .msg $edit + rm -f .msg + ;; +esac -- cgit v1.2.3 From 8b30aa50593b159791fe1478cb5725caaf219d06 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 6 Jan 2008 12:12:24 +0100 Subject: git.el: Support for getting diffs from inside the log-edit buffer. Take advantage of the new log-edit feature that allows to show a diff with C-c C-d while editing the log message. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 28a4899a0a..cadb992336 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1024,7 +1024,9 @@ Return the list of files that haven't been handled." (setq default-directory dir) (setq buffer-read-only t))) (display-buffer buffer) - (shrink-window-if-larger-than-buffer)) + ; shrink window only if it displays the status buffer + (when (eq (window-buffer) (current-buffer)) + (shrink-window-if-larger-than-buffer))) (defun git-diff-file () "Diff the marked file(s) against HEAD." @@ -1097,6 +1099,11 @@ Return the list of files that haven't been handled." (with-current-buffer log-edit-parent-buffer (git-get-filenames (git-marked-files-state 'added 'deleted 'modified)))) +(defun git-log-edit-diff () + "Run a diff of the current files being committed from a log-edit buffer." + (with-current-buffer log-edit-parent-buffer + (git-diff-file))) + (defun git-append-sign-off (name email) "Append a Signed-off-by entry to the current buffer, avoiding duplicates." (let ((sign-off (format "Signed-off-by: %s <%s>" name email)) @@ -1169,7 +1176,10 @@ Return the list of files that haven't been handled." (when (re-search-forward "^Date: \\(.*\\)$" nil t) (setq date (match-string 1))))) (git-setup-log-buffer buffer author-name author-email subject date)) - (log-edit #'git-do-commit nil #'git-log-edit-files buffer) + (if (boundp 'log-edit-diff-function) + (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files) + (log-edit-diff-function . git-log-edit-diff)) buffer) + (log-edit 'git-do-commit nil 'git-log-edit-files buffer)) (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) (setq buffer-file-coding-system coding-system) (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) -- cgit v1.2.3 From 5e3cb7e5038f99d442c484d5839bd6bf02adb58c Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 6 Jan 2008 12:13:01 +0100 Subject: git.el: Retrieve the permissions for up-to-date files. This allows displaying correctly the executable flag for the initial commit, and will make it possible to show the file type for up-to-date symlinks and subprojects. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index cadb992336..df3699b3a6 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -642,6 +642,22 @@ Return the list of files that haven't been handled." (git-insert-info-list status infolist) files)) +(defun git-run-ls-files-cached (status files default-state) + "Run git-ls-files -c on FILES and parse the results into STATUS. +Return the list of files that haven't been handled." + (let (infolist) + (with-temp-buffer + (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files) + (goto-char (point-min)) + (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-3]\t\\([^\0]+\\)\0" nil t) + (let* ((new-perm (string-to-number (match-string 1) 8)) + (old-perm (if (eq default-state 'added) 0 new-perm)) + (name (match-string 2))) + (push (git-create-fileinfo default-state name old-perm new-perm) infolist) + (setq files (delete name files))))) + (git-insert-info-list status infolist) + files)) + (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." (with-temp-buffer @@ -673,10 +689,10 @@ Return the list of files that haven't been handled." "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) (unless files - (when git-show-uptodate (git-run-ls-files git-status nil 'uptodate "-c"))) + (when git-show-uptodate (git-run-ls-files-cached git-status nil 'uptodate))) (let* ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db - (git-run-ls-files git-status files 'added "-c") + (git-run-ls-files-cached git-status files 'added) (git-run-diff-index git-status files)))) (git-run-ls-unmerged git-status files) (when (or remaining-files (and git-show-unknown (not files))) -- cgit v1.2.3 From 40f162b04b3d5155a7e41d27c9f52383a5fa5a9f Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 6 Jan 2008 12:13:36 +0100 Subject: git.el: Display file types and type changes. Handle the T status from git-diff-index to display type changes between file/symlink/subproject. Also always show the file type for symlink and subprojects to indicate that they are not normal files. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index df3699b3a6..9a0f03f242 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -489,8 +489,7 @@ and returns the process output as a string." "Set the state of a file info." (unless (eq (git-fileinfo->state info) state) (setf (git-fileinfo->state info) state - (git-fileinfo->old-perm info) 0 - (git-fileinfo->new-perm info) 0 + (git-fileinfo->new-perm info) (git-fileinfo->old-perm info) (git-fileinfo->rename-state info) nil (git-fileinfo->orig-name info) nil (git-fileinfo->needs-refresh info) t))) @@ -524,6 +523,7 @@ and returns the process output as a string." (?A 'added) (?D 'deleted) (?U 'unmerged) + (?T 'modified) (t nil))) (defun git-status-code-as-string (code) @@ -538,6 +538,33 @@ and returns the process output as a string." ('ignored (propertize "Ignored " 'face 'git-ignored-face)) (t "? "))) +(defun git-file-type-as-string (info) + "Return a string describing the file type of INFO." + (let* ((old-type (lsh (or (git-fileinfo->old-perm info) 0) -9)) + (new-type (lsh (or (git-fileinfo->new-perm info) 0) -9)) + (str (case new-type + (?\100 ;; file + (case old-type + (?\100 nil) + (?\120 " (type change symlink -> file)") + (?\160 " (type change subproject -> file)"))) + (?\120 ;; symlink + (case old-type + (?\100 " (type change file -> symlink)") + (?\160 " (type change subproject -> symlink)") + (t " (symlink)"))) + (?\160 ;; subproject + (case old-type + (?\100 " (type change file -> subproject)") + (?\120 " (type change symlink -> subproject)") + (t " (subproject)"))) + (?\000 ;; deleted or unknown + (case old-type + (?\120 " (symlink)") + (?\160 " (subproject)"))) + (t (format " (unknown type %o)" new-type))))) + (if str (propertize str 'face 'git-status-face) ""))) + (defun git-rename-as-string (info) "Return a string describing the copy or rename associated with INFO, or an empty string if none." (let ((state (git-fileinfo->rename-state info))) @@ -567,6 +594,7 @@ and returns the process output as a string." " " (git-status-code-as-string (git-fileinfo->state info)) " " (git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info)) " " (git-escape-file-name (git-fileinfo->name info)) + (git-file-type-as-string info) (git-rename-as-string info)))) (defun git-insert-info-list (status infolist) @@ -603,7 +631,7 @@ Return the list of files that haven't been handled." (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) (while (re-search-forward - ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" + ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" nil t 1) (let ((old-perm (string-to-number (match-string 1) 8)) (new-perm (string-to-number (match-string 2) 8)) -- cgit v1.2.3 From 87e3d812943f3d9c5f9464b0aee83398dd9d028e Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 8 Jan 2008 14:45:46 +0100 Subject: git.el: Make sure we never insert the same file twice. Skip non-zero stage files during git-ls-files -c, they are handled later. Also fix git-insert-info-list to merge duplicate file names. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 9a0f03f242..c826017580 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -608,7 +608,7 @@ and returns the process output as a string." (while info (setf (git-fileinfo->needs-refresh info) t) (cond ((not node) - (ewoc-enter-last status info) + (setq node (ewoc-enter-last status info)) (setq info (pop infolist))) ((string-lessp (git-fileinfo->name (ewoc-data node)) (git-fileinfo->name info)) @@ -620,7 +620,7 @@ and returns the process output as a string." (setf (ewoc-data node) info) (setq info (pop infolist))) (t - (ewoc-enter-before status node info) + (setq node (ewoc-enter-before status node info)) (setq info (pop infolist))))))) (defun git-run-diff-index (status files) @@ -677,7 +677,7 @@ Return the list of files that haven't been handled." (with-temp-buffer (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files) (goto-char (point-min)) - (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-3]\t\\([^\0]+\\)\0" nil t) + (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t) (let* ((new-perm (string-to-number (match-string 1) 8)) (old-perm (if (eq default-state 'added) 0 new-perm)) (name (match-string 2))) -- cgit v1.2.3 From 58152a02d99b499c338b633e7fe021b337313bd8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 8 Jan 2008 14:46:22 +0100 Subject: git.el: Refresh files from their real state upon commit. Instead of just setting the state to up-to-date, retrieve the full state again, so that the file type can be displayed properly. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c826017580..342c78348e 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -626,7 +626,8 @@ and returns the process output as a string." (defun git-run-diff-index (status files) "Run git-diff-index on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let (infolist) + (let ((remaining (copy-sequence files)) + infolist) (with-temp-buffer (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) @@ -644,10 +645,10 @@ Return the list of files that haven't been handled." (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist) (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist)) (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)) - (setq files (delete name files)) - (when new-name (setq files (delete new-name files)))))) + (setq remaining (delete name remaining)) + (when new-name (setq remaining (delete new-name remaining)))))) (git-insert-info-list status infolist) - files)) + remaining)) (defun git-find-status-file (status file) "Find a given file in the status ewoc and return its node." @@ -673,7 +674,8 @@ Return the list of files that haven't been handled." (defun git-run-ls-files-cached (status files default-state) "Run git-ls-files -c on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let (infolist) + (let ((remaining (copy-sequence files)) + infolist) (with-temp-buffer (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files) (goto-char (point-min)) @@ -682,9 +684,9 @@ Return the list of files that haven't been handled." (old-perm (if (eq default-state 'added) 0 new-perm)) (name (match-string 2))) (push (git-create-fileinfo default-state name old-perm new-perm) infolist) - (setq files (delete name files))))) + (setq remaining (delete name remaining))))) (git-insert-info-list status infolist) - files)) + remaining)) (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." @@ -716,8 +718,8 @@ Return the list of files that haven't been handled." (defun git-update-status-files (files &optional default-state) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) - (unless files - (when git-show-uptodate (git-run-ls-files-cached git-status nil 'uptodate))) + (when (or git-show-uptodate files) + (git-run-ls-files-cached git-status files 'uptodate)) (let* ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db (git-run-ls-files-cached git-status files 'added) @@ -839,7 +841,7 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) - (dolist (info files) (git-set-fileinfo-state info 'uptodate)) + (git-update-status-files (git-get-filenames files) 'uptodate) (git-call-process-env nil nil "rerere") (git-call-process-env nil nil "gc" "--auto") (git-refresh-files) -- cgit v1.2.3 From ef40b3efe0764f7b56c2745601690e9cc12428d8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 8 Jan 2008 14:49:09 +0100 Subject: git.el: Make status refresh faster. Don't set the needs-refresh flag when inserting a new file info, since ewoc refreshes it upon insert already; this makes a full refresh twice as fast. Also make git-fileinfo-prettyprint a little faster by not retrieving permission values twice. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 342c78348e..d8a06381f4 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -538,10 +538,10 @@ and returns the process output as a string." ('ignored (propertize "Ignored " 'face 'git-ignored-face)) (t "? "))) -(defun git-file-type-as-string (info) - "Return a string describing the file type of INFO." - (let* ((old-type (lsh (or (git-fileinfo->old-perm info) 0) -9)) - (new-type (lsh (or (git-fileinfo->new-perm info) 0) -9)) +(defun git-file-type-as-string (old-perm new-perm) + "Return a string describing the file type based on its permissions." + (let* ((old-type (lsh (or old-perm 0) -9)) + (new-type (lsh (or new-perm 0) -9)) (str (case new-type (?\100 ;; file (case old-type @@ -590,12 +590,14 @@ and returns the process output as a string." (defun git-fileinfo-prettyprint (info) "Pretty-printer for the git-fileinfo structure." - (insert (concat " " (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ") - " " (git-status-code-as-string (git-fileinfo->state info)) - " " (git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info)) - " " (git-escape-file-name (git-fileinfo->name info)) - (git-file-type-as-string info) - (git-rename-as-string info)))) + (let ((old-perm (git-fileinfo->old-perm info)) + (new-perm (git-fileinfo->new-perm info))) + (insert (concat " " (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ") + " " (git-status-code-as-string (git-fileinfo->state info)) + " " (git-permissions-as-string old-perm new-perm) + " " (git-escape-file-name (git-fileinfo->name info)) + (git-file-type-as-string old-perm new-perm) + (git-rename-as-string info))))) (defun git-insert-info-list (status infolist) "Insert a list of file infos in the status buffer, replacing existing ones if any." @@ -606,7 +608,6 @@ and returns the process output as a string." (let ((info (pop infolist)) (node (ewoc-nth status 0))) (while info - (setf (git-fileinfo->needs-refresh info) t) (cond ((not node) (setq node (ewoc-enter-last status info)) (setq info (pop infolist))) @@ -617,6 +618,7 @@ and returns the process output as a string." (git-fileinfo->name info)) ;; preserve the marked flag (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))) + (setf (git-fileinfo->needs-refresh info) t) (setf (ewoc-data node) info) (setq info (pop infolist))) (t -- cgit v1.2.3 From 22fa97d4e011de82b44fdc83c27e919a996ce1be Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 13 Jan 2008 22:51:01 -0600 Subject: Remove usage of git- (dash) commands from email hook Switch all git command calls to use the git (space) command format, and remove the use of git-repo-config in place of git config. Signed-off-by: Dan McGee Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 7511ea0797..77c88ebf1f 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -248,24 +248,24 @@ generate_update_branch_email() # In this case we want to issue an email containing only revisions # 3, 4, and N. Given (almost) by # - # git-rev-list N ^O --not --all + # git rev-list N ^O --not --all # # The reason for the "almost", is that the "--not --all" will take # precedence over the "N", and effectively will translate to # - # git-rev-list N ^O ^X ^N + # git rev-list N ^O ^X ^N # - # So, we need to build up the list more carefully. git-rev-parse - # will generate a list of revs that may be fed into git-rev-list. + # So, we need to build up the list more carefully. git rev-parse + # will generate a list of revs that may be fed into git rev-list. # We can get it to make the "--not --all" part and then filter out # the "^N" with: # - # git-rev-parse --not --all | grep -v N + # git rev-parse --not --all | grep -v N # - # Then, using the --stdin switch to git-rev-list we have effectively + # Then, using the --stdin switch to git rev-list we have effectively # manufactured # - # git-rev-list N ^O ^X + # git rev-list N ^O ^X # # This leaves a problem when someone else updates the repository # while this script is running. Their new value of the ref we're @@ -274,10 +274,10 @@ generate_update_branch_email() # all of our commits. What we really want is to exclude the current # value of $refname from the --not list, rather than N itself. So: # - # git-rev-parse --not --all | grep -v $(git-rev-parse $refname) + # git rev-parse --not --all | grep -v $(git rev-parse $refname) # # Get's us to something pretty safe (apart from the small time - # between refname being read, and git-rev-parse running - for that, + # between refname being read, and git rev-parse running - for that, # I give up) # # @@ -295,7 +295,7 @@ generate_update_branch_email() # As above, we need to take into account the presence of X; if # another branch is already in the repository and points at some of # the revisions that we are about to output - we don't want them. - # The solution is as before: git-rev-parse output filtered. + # The solution is as before: git rev-parse output filtered. # # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N # @@ -305,7 +305,7 @@ generate_update_branch_email() # for a branch update. Therefore we still want to output revisions # that have been output on a tag email. # - # Luckily, git-rev-parse includes just the tool. Instead of using + # Luckily, git rev-parse includes just the tool. Instead of using # "--all" we use "--branches"; this has the added benefit that # "remotes/" will be ignored as well. @@ -454,7 +454,7 @@ generate_update_atag_email() # generate_atag_email() { - # Use git-for-each-ref to pull out the individual fields from the + # Use git for-each-ref to pull out the individual fields from the # tag eval $(git for-each-ref --shell --format=' tagobject=%(*objectname) @@ -572,7 +572,7 @@ generate_general_email() else # What can we do here? The tag marks an object that is not # a commit, so there is no log for us to display. It's - # probably not wise to output git-cat-file as it could be a + # probably not wise to output git cat-file as it could be a # binary blob. We'll just say how big it is echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." fi @@ -622,10 +622,10 @@ then projectdesc="UNNAMED PROJECT" fi -recipients=$(git repo-config hooks.mailinglist) -announcerecipients=$(git repo-config hooks.announcelist) -envelopesender=$(git-repo-config hooks.envelopesender) -emailprefix=$(git-repo-config hooks.emailprefix || echo '[SCM] ') +recipients=$(git config hooks.mailinglist) +announcerecipients=$(git config hooks.announcelist) +envelopesender=$(git config hooks.envelopesender) +emailprefix=$(git config hooks.emailprefix || echo '[SCM] ') # --- Main loop # Allow dual mode: run from the command line just like the update hook, or -- cgit v1.2.3 From 1bc7c13af9f936aa80893100120b542338a10bf4 Mon Sep 17 00:00:00 2001 From: Mark Drago Date: Mon, 14 Jan 2008 20:11:19 -0500 Subject: hg-to-git: improve popen calls This patch improves all of the popen calls in hg-to-git.py by specifying the template 'hg log' should use instead of calling 'hg log' and grepping for the desired data. Signed-off-by: Mark Drago Acked-by: Stelian Pop Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 46 ++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 24 deletions(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 9befb92c41..c35b15860d 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -1,6 +1,6 @@ #! /usr/bin/python -""" hg-to-svn.py - A Mercurial to GIT converter +""" hg-to-git.py - A Mercurial to GIT converter Copyright (C)2007 Stelian Pop @@ -27,6 +27,8 @@ import re hgvers = {} # List of children for each hg revision hgchildren = {} +# List of parents for each hg revision +hgparents = {} # Current branch for each hg revision hgbranch = {} # Number of new changesets converted from hg @@ -99,17 +101,19 @@ if state: else: print 'State does not exist, first run' -tip = os.popen('hg tip | head -1 | cut -f 2 -d :').read().strip() +tip = os.popen('hg tip --template "{rev}"').read() print 'tip is', tip # Calculate the branches print 'analysing the branches...' hgchildren["0"] = () +hgparents["0"] = (None, None) hgbranch["0"] = "master" for cset in range(1, int(tip) + 1): hgchildren[str(cset)] = () - prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines() - if len(prnts) > 0: + prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().split(' ') + prnts = map(lambda x: x[:x.find(':')], prnts) + if prnts[0] != '': parent = prnts[0].strip() else: parent = str(cset - 1) @@ -120,6 +124,8 @@ for cset in range(1, int(tip) + 1): else: mparent = None + hgparents[str(cset)] = (parent, mparent) + if mparent: # For merge changesets, take either one, preferably the 'master' branch if hgbranch[mparent] == 'master': @@ -147,34 +153,27 @@ for cset in range(int(tip) + 1): hgnewcsets += 1 # get info - prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines() - if len(prnts) > 0: - parent = prnts[0].strip() - else: - parent = str(cset - 1) - if len(prnts) > 1: - mparent = prnts[1].strip() - else: - mparent = None - + log_data = os.popen('hg log -r %d --template "{tags}\n{date|date}\n{author}\n"' % cset).readlines() + tag = log_data[0].strip() + date = log_data[1].strip() + user = log_data[2].strip() + parent = hgparents[str(cset)][0] + mparent = hgparents[str(cset)][1] + + #get comment (fdcomment, filecomment) = tempfile.mkstemp() - csetcomment = os.popen('hg log -r %d -v | grep -v ^changeset: | grep -v ^parent: | grep -v ^user: | grep -v ^date | grep -v ^files: | grep -v ^description: | grep -v ^tag:' % cset).read().strip() + csetcomment = os.popen('hg log -r %d --template "{desc}"' % cset).read().strip() os.write(fdcomment, csetcomment) os.close(fdcomment) - date = os.popen('hg log -r %d | grep ^date: | cut -f 2- -d :' % cset).read().strip() - - tag = os.popen('hg log -r %d | grep ^tag: | cut -f 2- -d :' % cset).read().strip() - - user = os.popen('hg log -r %d | grep ^user: | cut -f 2- -d :' % cset).read().strip() - print '-----------------------------------------' print 'cset:', cset print 'branch:', hgbranch[str(cset)] print 'user:', user print 'date:', date print 'comment:', csetcomment - print 'parent:', parent + if parent: + print 'parent:', parent if mparent: print 'mparent:', mparent if tag: @@ -224,8 +223,7 @@ for cset in range(int(tip) + 1): os.system('git-branch -d %s' % otherbranch) # retrieve and record the version - vvv = os.popen('git-show | head -1').read() - vvv = vvv[vvv.index(' ') + 1 : ].strip() + vvv = os.popen('git-show --quiet --pretty=format:%H').read() print 'record', cset, '->', vvv hgvers[str(cset)] = vvv -- cgit v1.2.3 From 5c66d0d4580196094e80c552f141525759a8e249 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 17 Jan 2008 22:52:40 -0800 Subject: Officially deprecate repo-config. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- contrib/examples/git-tag.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 343364de04..9b0033d17c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -333,7 +333,7 @@ __git_commands () read-tree) : plumbing;; receive-pack) : plumbing;; reflog) : plumbing;; - repo-config) : plumbing;; + repo-config) : deprecated;; rerere) : plumbing;; rev-list) : plumbing;; rev-parse) : plumbing;; diff --git a/contrib/examples/git-tag.sh b/contrib/examples/git-tag.sh index ae7c531666..e9f3a228af 100755 --- a/contrib/examples/git-tag.sh +++ b/contrib/examples/git-tag.sh @@ -167,7 +167,7 @@ type=$(git cat-file -t $object) || exit 1 tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1 test -n "$username" || - username=$(git repo-config user.signingkey) || + username=$(git config user.signingkey) || username=$(expr "z$tagger" : 'z\(.*>\)') trap 'rm -f "$GIT_DIR"/TAG_TMP* "$GIT_DIR"/TAG_FINALMSG "$GIT_DIR"/TAG_EDITMSG' 0 -- cgit v1.2.3 From a3b811a4914cf02bb25662e330a067c1b0ddbc75 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 20 Jan 2008 00:54:57 -0600 Subject: Update git-completion for new 'remote rm' option Signed-off-by: Dan McGee Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9b0033d17c..0d33f9a3dc 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -970,18 +970,18 @@ _git_remote () while [ $c -lt $COMP_CWORD ]; do i="${COMP_WORDS[c]}" case "$i" in - add|show|prune|update) command="$i"; break ;; + add|rm|show|prune|update) command="$i"; break ;; esac c=$((++c)) done if [ $c -eq $COMP_CWORD -a -z "$command" ]; then - __gitcomp "add show prune update" + __gitcomp "add rm show prune update" return fi case "$command" in - show|prune) + rm|show|prune) __gitcomp "$(__git_remotes)" ;; update) -- cgit v1.2.3 From f3e9512be1c7935b8ca496f3687c6850b32a06f3 Mon Sep 17 00:00:00 2001 From: Jason McMullan Date: Wed, 5 Dec 2007 12:16:56 -0500 Subject: Remove $Id: ..$ $Header: ..$ etc from +ko and +k files during import This patch removes the '$Keyword: ...$' '...' data, so that files don't have spurious megre conflicts between branches. Handles both +ko and +k styles, and leaves the '$Foo$' in the original file. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c80a6da252..31c5501d11 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -964,9 +964,13 @@ class P4Sync(Command): stat = filedata[j] j += 1 text = '' - while j < len(filedata) and filedata[j]['code'] in ('text', - 'binary'): - text += filedata[j]['data'] + while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'): + tmp = filedata[j]['data'] + if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): + tmp = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', tmp) + elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): + tmp = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', tmp) + text += tmp j += 1 -- cgit v1.2.3 From e96e400f67b8478a99f9d2b5d82f08cd21f51a88 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2008 14:27:55 +0100 Subject: git-p4: Fix submit user-interface. Don't ask any questions when submitting, behave similar to git-svn dcommit. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 85 ++++++++++++++-------------------------------- 1 file changed, 26 insertions(+), 59 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 31c5501d11..a74aabdb28 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -469,9 +469,7 @@ class P4Submit(Command): optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), optparse.make_option("--log-substitutions", dest="substFile"), - optparse.make_option("--dry-run", action="store_true"), optparse.make_option("--direct", dest="directSubmit", action="store_true"), - optparse.make_option("--trust-me-like-a-fool", dest="trustMeLikeAFool", action="store_true"), optparse.make_option("-M", dest="detectRename", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." @@ -479,12 +477,10 @@ class P4Submit(Command): self.firstTime = True self.reset = False self.interactive = True - self.dryRun = False self.substFile = "" self.firstTime = True self.origin = "" self.directSubmit = False - self.trustMeLikeAFool = False self.detectRename = False self.verbose = False self.isWindows = (platform.system() == "Windows") @@ -681,57 +677,30 @@ class P4Submit(Command): separatorLine += "\r" separatorLine += "\n" - response = "e" - if self.trustMeLikeAFool: - response = "y" - - firstIteration = True - while response == "e": - if not firstIteration: - response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o/[s]kip ") - firstIteration = False - if response == "e": - [handle, fileName] = tempfile.mkstemp() - tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate + separatorLine + diff) - tmpFile.close() - defaultEditor = "vi" - if platform.system() == "Windows": - defaultEditor = "notepad" - editor = os.environ.get("EDITOR", defaultEditor); - system(editor + " " + fileName) - tmpFile = open(fileName, "rb") - message = tmpFile.read() - tmpFile.close() - os.remove(fileName) - submitTemplate = message[:message.index(separatorLine)] - if self.isWindows: - submitTemplate = submitTemplate.replace("\r\n", "\n") - - if response == "y" or response == "yes": - if self.dryRun: - print submitTemplate - raw_input("Press return to continue...") - else: - if self.directSubmit: - print "Submitting to git first" - os.chdir(self.oldWorkingDirectory) - write_pipe("git commit -a -F -", submitTemplate) - os.chdir(self.clientPath) - - write_pipe("p4 submit -i", submitTemplate) - elif response == "s": - for f in editedFiles: - system("p4 revert \"%s\"" % f); - for f in filesToAdd: - system("p4 revert \"%s\"" % f); - system("rm %s" %f) - for f in filesToDelete: - system("p4 delete \"%s\"" % f); - return - else: - print "Not submitting!" - self.interactive = False + [handle, fileName] = tempfile.mkstemp() + tmpFile = os.fdopen(handle, "w+") + tmpFile.write(submitTemplate + separatorLine + diff) + tmpFile.close() + defaultEditor = "vi" + if platform.system() == "Windows": + defaultEditor = "notepad" + editor = os.environ.get("EDITOR", defaultEditor); + system(editor + " " + fileName) + tmpFile = open(fileName, "rb") + message = tmpFile.read() + tmpFile.close() + os.remove(fileName) + submitTemplate = message[:message.index(separatorLine)] + if self.isWindows: + submitTemplate = submitTemplate.replace("\r\n", "\n") + + if self.directSubmit: + print "Submitting to git first" + os.chdir(self.oldWorkingDirectory) + write_pipe("git commit -a -F -", submitTemplate) + os.chdir(self.clientPath) + + write_pipe("p4 submit -i", submitTemplate) else: fileName = "submit.txt" file = open(fileName, "w+") @@ -828,10 +797,8 @@ class P4Submit(Command): sync = P4Sync() sync.run([]) - response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ") - if response == "y" or response == "yes": - rebase = P4Rebase() - rebase.rebase() + rebase = P4Rebase() + rebase.rebase() os.remove(self.configFile) return True -- cgit v1.2.3 From 36ee4ee40e1851442b0b075870cd5de5df6ee2b6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 Jan 2008 14:21:45 +0100 Subject: git-p4: Ensure the working directory and the index are clean before "git-p4 rebase" Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a74aabdb28..e5fe5f6d3d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1611,6 +1611,11 @@ class P4Rebase(Command): return self.rebase() def rebase(self): + if os.system("git update-index --refresh") != 0: + die("Some files in your working directory are modified and different than what is in your index. You can use git update-index to bring the index up-to-date or stash away all your changes with git stash."); + if len(read_pipe("git diff-index HEAD --")) > 0: + die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash."); + [upstream, settings] = findUpstreamBranchPoint() if len(upstream) == 0: die("Cannot find upstream branchpoint for rebase") -- cgit v1.2.3 From 147402a2e9e29efe3fe75b5409cc2dd88cef04c7 Mon Sep 17 00:00:00 2001 From: Tommy Thorn Date: Sat, 2 Feb 2008 00:11:44 -0800 Subject: git-p4: Fix an obvious typo The regexp "$," can't match anything. Clearly not intended. This was introduced in ce6f33c8 which is quite a while ago. Signed-off-by: Tommy Thorn Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e5fe5f6d3d..c17afae94a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1646,7 +1646,7 @@ class P4Clone(P4Sync): depotPath = args[0] depotDir = re.sub("(@[^@]*)$", "", depotPath) depotDir = re.sub("(#[^#]*)$", "", depotDir) - depotDir = re.sub(r"\.\.\.$,", "", depotDir) + depotDir = re.sub(r"\.\.\.$", "", depotDir) depotDir = re.sub(r"/$", "", depotDir) return os.path.split(depotDir)[1] -- cgit v1.2.3 From e75201963f6722ad84491c8f17dff8406ec0a198 Mon Sep 17 00:00:00 2001 From: Robin Rosenberg Date: Sun, 30 Sep 2007 02:20:45 +0200 Subject: Improve bash prompt to detect various states like an unfinished merge This patch makes the git prompt (when enabled) show if a merge or a rebase is unfinished. It also detects if a bisect is being done as well as detached checkouts. An uncompleted git-am cannot be distinguised from a rebase (the non-interactive version). Instead of having an even longer prompt we simply ignore that and hope the power users that use git-am knows the difference. Signed-off-by: Robin Rosenberg Acked-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 37 ++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0d33f9a3dc..4ea727b143 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -64,12 +64,41 @@ __gitdir () __git_ps1 () { - local b="$(git symbolic-ref HEAD 2>/dev/null)" - if [ -n "$b" ]; then + local g="$(git rev-parse --git-dir 2>/dev/null)" + if [ -n "$g" ]; then + local r + local b + if [ -d "$g/../.dotest" ] + then + r="|AM/REBASE" + b="$(git symbolic-ref HEAD 2>/dev/null)" + elif [ -f "$g/.dotest-merge/interactive" ] + then + r="|REBASE-i" + b="$(cat $g/.dotest-merge/head-name)" + elif [ -d "$g/.dotest-merge" ] + then + r="|REBASE-m" + b="$(cat $g/.dotest-merge/head-name)" + elif [ -f "$g/MERGE_HEAD" ] + then + r="|MERGING" + b="$(git symbolic-ref HEAD 2>/dev/null)" + else + if [ -f $g/BISECT_LOG ] + then + r="|BISECTING" + fi + if ! b="$(git symbolic-ref HEAD 2>/dev/null)" + then + b="$(cut -c1-7 $g/HEAD)..." + fi + fi + if [ -n "$1" ]; then - printf "$1" "${b##refs/heads/}" + printf "$1" "${b##refs/heads/}$r" else - printf " (%s)" "${b##refs/heads/}" + printf " (%s)" "${b##refs/heads/}$r" fi fi } -- cgit v1.2.3 From 053d9e432be246a389fb8adaa0c88e7c791f8b21 Mon Sep 17 00:00:00 2001 From: Toby Allsopp Date: Tue, 5 Feb 2008 09:41:43 +1300 Subject: git-p4: Fix indentation from tab to spaces Signed-off-by: Toby Allsopp --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c17afae94a..781a0cbbbc 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1646,7 +1646,7 @@ class P4Clone(P4Sync): depotPath = args[0] depotDir = re.sub("(@[^@]*)$", "", depotPath) depotDir = re.sub("(#[^#]*)$", "", depotDir) - depotDir = re.sub(r"\.\.\.$", "", depotDir) + depotDir = re.sub(r"\.\.\.$", "", depotDir) depotDir = re.sub(r"/$", "", depotDir) return os.path.split(depotDir)[1] -- cgit v1.2.3 From 3f3d564aa74e80cb30ead37e4581ee21220b4ac4 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 7 Feb 2008 13:50:19 +0100 Subject: git.el: Support for showing unknown/ignored directories. Instead of recursing into directories that only contain unknown files, display only the directory itself. Its contents can be expanded with git-find-file (bound to C-m). Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d8a06381f4..58d72a55c1 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -558,12 +558,15 @@ and returns the process output as a string." (?\100 " (type change file -> subproject)") (?\120 " (type change symlink -> subproject)") (t " (subproject)"))) + (?\110 nil) ;; directory (internal, not a real git state) (?\000 ;; deleted or unknown (case old-type (?\120 " (symlink)") (?\160 " (subproject)"))) (t (format " (unknown type %o)" new-type))))) - (if str (propertize str 'face 'git-status-face) ""))) + (cond (str (propertize str 'face 'git-status-face)) + ((eq new-type ?\110) "/") + (t "")))) (defun git-rename-as-string (info) "Return a string describing the copy or rename associated with INFO, or an empty string if none." @@ -666,9 +669,11 @@ Return the list of files that haven't been handled." (with-temp-buffer (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) - (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) + (while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1) (let ((name (match-string 1))) - (push (git-create-fileinfo default-state name) infolist) + (push (git-create-fileinfo default-state name 0 + (if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0)) + infolist) (setq files (delete name files))))) (git-insert-info-list status infolist) files)) @@ -713,7 +718,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-files-with-excludes (status files default-state &rest options) "Run git-ls-files on FILES with appropriate --exclude-from options." (let ((exclude-files (git-get-exclude-files))) - (apply #'git-run-ls-files status files default-state + (apply #'git-run-ls-files status files default-state "--directory" (concat "--exclude-per-directory=" git-per-dir-ignore-file) (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) @@ -957,6 +962,7 @@ Return the list of files that haven't been handled." "Add marked file(s) to the index cache." (interactive) (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored)))) + ;; FIXME: add support for directories (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) (apply #'git-call-process-env nil nil "update-index" "--add" "--" files) @@ -983,7 +989,10 @@ Return the list of files that haven't been handled." (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" ""))) (progn (dolist (name files) - (when (file-exists-p name) (delete-file name))) + (ignore-errors + (if (file-directory-p name) + (delete-directory name) + (delete-file name)))) (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files) (git-update-status-files files nil) (git-success-message "Removed" files)) @@ -992,7 +1001,7 @@ Return the list of files that haven't been handled." (defun git-revert-file () "Revert changes to the marked file(s)." (interactive) - (let ((files (git-marked-files)) + (let ((files (git-marked-files-state 'added 'deleted 'modified 'unmerged)) added modified) (when (and files (yes-or-no-p @@ -1063,6 +1072,16 @@ Return the list of files that haven't been handled." (message "Inserting unknown files...done")) (git-remove-handled))) +(defun git-expand-directory (info) + "Expand the directory represented by INFO to list its files." + (when (eq (lsh (git-fileinfo->new-perm info) -9) ?\110) + (let ((dir (git-fileinfo->name info))) + (git-set-filenames-state git-status (list dir) nil) + (git-run-ls-files-with-excludes git-status (list (concat dir "/")) 'unknown "-o") + (git-refresh-files) + (git-refresh-ewoc-hf git-status) + t))) + (defun git-setup-diff-buffer (buffer) "Setup a buffer for displaying a diff." (let ((dir default-directory)) @@ -1237,9 +1256,10 @@ Return the list of files that haven't been handled." (interactive) (unless git-status (error "Not in git-status buffer.")) (let ((info (ewoc-data (ewoc-locate git-status)))) - (find-file (git-fileinfo->name info)) - (when (eq 'unmerged (git-fileinfo->state info)) - (smerge-mode 1)))) + (unless (git-expand-directory info) + (find-file (git-fileinfo->name info)) + (when (eq 'unmerged (git-fileinfo->state info)) + (smerge-mode 1))))) (defun git-find-file-other-window () "Visit the current file in its own buffer in another window." -- cgit v1.2.3 From 76127b3a0d7cf359b12be80971f023b6ee07f7f9 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 7 Feb 2008 13:50:39 +0100 Subject: git.el: Added a command to amend a commit. It reverts the commit and sets up the status and edit log buffer to allow making changes and recommitting it. Bound to C-c C-a. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 58d72a55c1..5519ed107a 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -740,6 +740,27 @@ Return the list of files that haven't been handled." (git-refresh-files) (git-refresh-ewoc-hf git-status))) +(defun git-mark-files (status files) + "Mark all the specified FILES, and unmark the others." + (setq files (sort files #'string-lessp)) + (let ((file (and files (pop files))) + (node (ewoc-nth status 0))) + (while node + (let ((info (ewoc-data node))) + (if (and file (string-equal (git-fileinfo->name info) file)) + (progn + (unless (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) t) + (setf (git-fileinfo->needs-refresh info) t)) + (setq file (pop files)) + (setq node (ewoc-next status node))) + (when (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) nil) + (setf (git-fileinfo->needs-refresh info) t)) + (if (and file (string-lessp file (git-fileinfo->name info))) + (setq file (pop files)) + (setq node (ewoc-next status node)))))))) + (defun git-marked-files () "Return a list of all marked files, or if none a list containing just the file at cursor position." (unless git-status (error "Not in git-status buffer.")) @@ -1218,7 +1239,8 @@ Return the list of files that haven't been handled." (goto-char (point-min)) (when (re-search-forward "\n+\\'" nil t) (replace-match "\n" t t)) - (when sign-off (git-append-sign-off committer-name committer-email))))) + (when sign-off (git-append-sign-off committer-name committer-email))) + buffer)) (defun git-commit-file () "Commit the marked file(s), asking for a commit message." @@ -1251,6 +1273,52 @@ Return the list of files that haven't been handled." (setq buffer-file-coding-system coding-system) (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) +(defun git-setup-commit-buffer (commit) + "Setup the commit buffer with the contents of COMMIT." + (let (author-name author-email subject date msg) + (with-temp-buffer + (let ((coding-system (git-get-logoutput-coding-system))) + (git-call-process-env t nil "log" "-1" commit) + (goto-char (point-min)) + (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) + (setq author-name (match-string 1)) + (setq author-email (match-string 2))) + (when (re-search-forward "^Date: *\\(.*\\)$" nil t) + (setq date (match-string 1))) + (while (re-search-forward "^ \\(.*\\)$" nil t) + (push (match-string 1) msg)) + (setq msg (nreverse msg)) + (setq subject (pop msg)) + (while (and msg (zerop (length (car msg))) (pop msg))))) + (git-setup-log-buffer (get-buffer-create "*git-commit*") + author-name author-email subject date + (mapconcat #'identity msg "\n")))) + +(defun git-get-commit-files (commit) + "Retrieve the list of files modified by COMMIT." + (let (files) + (with-temp-buffer + (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) + (goto-char (point-min)) + (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) + (push (match-string 1) files))) + files)) + +(defun git-amend-commit () + "Undo the last commit on HEAD, and set things up to commit an +amended version of it." + (interactive) + (unless git-status (error "Not in git-status buffer.")) + (when (git-empty-db-p) (error "No commit to amend.")) + (let* ((commit (git-rev-parse "HEAD")) + (files (git-get-commit-files commit))) + (git-call-process-env nil nil "reset" "--soft" "HEAD^") + (git-update-status-files (copy-sequence files) 'uptodate) + (git-mark-files git-status files) + (git-refresh-files) + (git-setup-commit-buffer commit) + (git-commit-file))) + (defun git-find-file () "Visit the current file in its own buffer." (interactive) @@ -1329,6 +1397,7 @@ Return the list of files that haven't been handled." (unless git-status-mode-map (let ((map (make-keymap)) + (commit-map (make-sparse-keymap)) (diff-map (make-sparse-keymap)) (toggle-map (make-sparse-keymap))) (suppress-keymap map) @@ -1337,6 +1406,7 @@ Return the list of files that haven't been handled." (define-key map " " 'git-next-file) (define-key map "a" 'git-add-file) (define-key map "c" 'git-commit-file) + (define-key map "\C-c" commit-map) (define-key map "d" diff-map) (define-key map "=" 'git-diff-file) (define-key map "f" 'git-find-file) @@ -1362,6 +1432,8 @@ Return the list of files that haven't been handled." (define-key map "x" 'git-remove-handled) (define-key map "\C-?" 'git-unmark-file-up) (define-key map "\M-\C-?" 'git-unmark-all) + ; the commit submap + (define-key commit-map "\C-a" 'git-amend-commit) ; the diff submap (define-key diff-map "b" 'git-diff-file-base) (define-key diff-map "c" 'git-diff-file-combined) -- cgit v1.2.3 From 928323af6b761e8b1c33ce98e67c2f56fd1b7997 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 7 Feb 2008 13:51:20 +0100 Subject: git.el: Check for existing buffers on revert. Refuse to revert a file if it is modified in an existing buffer but not saved. On success, revert the buffers that contains the files that have been reverted. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 5519ed107a..e1058b9a99 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1033,11 +1033,19 @@ Return the list of files that haven't been handled." ('deleted (push (git-fileinfo->name info) modified)) ('unmerged (push (git-fileinfo->name info) modified)) ('modified (push (git-fileinfo->name info) modified)))) + ;; check if a buffer contains one of the files and isn't saved + (dolist (file (append added modified)) + (let ((buffer (get-file-buffer file))) + (when (and buffer (buffer-modified-p buffer)) + (error "Buffer %s is modified. Please kill or save modified buffers before reverting." (buffer-name buffer))))) (when added (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added)) (when modified (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) (git-update-status-files (append added modified) 'uptodate) + (dolist (file (append added modified)) + (let ((buffer (get-file-buffer file))) + (when buffer (with-current-buffer buffer (revert-buffer t t t))))) (git-success-message "Reverted" (git-get-filenames files))))) (defun git-resolve-file () -- cgit v1.2.3 From 0520e2154fb6a7418663bdee839c3448d51b9ae4 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 7 Feb 2008 13:51:34 +0100 Subject: git.el: Better handling of subprocess errors. Where possible, capture the output of the git command and display it if the command fails. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 88 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 37 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index e1058b9a99..a8bf0ef883 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -35,7 +35,6 @@ ;; ;; TODO ;; - portability to XEmacs -;; - better handling of subprocess errors ;; - diff against other branch ;; - renaming files from the status buffer ;; - creating tags @@ -191,6 +190,18 @@ if there is already one that displays the same directory." (append (git-get-env-strings env) (list "git") args)) (apply #'call-process "git" nil buffer nil args))) +(defun git-call-process-display-error (&rest args) + "Wrapper for call-process that displays error messages." + (let* ((dir default-directory) + (buffer (get-buffer-create "*Git Command Output*")) + (ok (with-current-buffer buffer + (let ((default-directory dir) + (buffer-read-only nil)) + (erase-buffer) + (eq 0 (apply 'call-process "git" nil (list buffer t) nil args)))))) + (unless ok (display-message-or-buffer buffer)) + ok)) + (defun git-call-process-env-string (env &rest args) "Wrapper for call-process that sets environment strings, and returns the process output as a string." @@ -377,7 +388,7 @@ and returns the process output as a string." (when reason (push reason args) (push "-m" args)) - (eq 0 (apply #'git-call-process-env nil nil "update-ref" args)))) + (apply 'git-call-process-display-error "update-ref" args))) (defun git-read-tree (tree &optional index-file) "Read a tree into the index file." @@ -866,16 +877,17 @@ Return the list of files that haven't been handled." (if (or (not (string-equal tree head-tree)) (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? ")) (let ((commit (git-commit-tree buffer tree head))) - (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) - (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) - (with-current-buffer buffer (erase-buffer)) - (git-update-status-files (git-get-filenames files) 'uptodate) - (git-call-process-env nil nil "rerere") - (git-call-process-env nil nil "gc" "--auto") - (git-refresh-files) - (git-refresh-ewoc-hf git-status) - (message "Committed %s." commit) - (git-run-hook "post-commit" nil)) + (when commit + (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) + (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) + (with-current-buffer buffer (erase-buffer)) + (git-update-status-files (git-get-filenames files) 'uptodate) + (git-call-process-env nil nil "rerere") + (git-call-process-env nil nil "gc" "--auto") + (git-refresh-files) + (git-refresh-ewoc-hf git-status) + (message "Committed %s." commit) + (git-run-hook "post-commit" nil))) (message "Commit aborted.")))) (message "No files to commit."))) (delete-file index-file)))))) @@ -986,9 +998,9 @@ Return the list of files that haven't been handled." ;; FIXME: add support for directories (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) - (apply #'git-call-process-env nil nil "update-index" "--add" "--" files) - (git-update-status-files files 'uptodate) - (git-success-message "Added" files))) + (when (apply 'git-call-process-display-error "update-index" "--add" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Added" files)))) (defun git-ignore-file () "Add marked file(s) to the ignore list." @@ -1014,9 +1026,9 @@ Return the list of files that haven't been handled." (if (file-directory-p name) (delete-directory name) (delete-file name)))) - (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files) - (git-update-status-files files nil) - (git-success-message "Removed" files)) + (when (apply 'git-call-process-display-error "update-index" "--remove" "--" files) + (git-update-status-files files nil) + (git-success-message "Removed" files))) (message "Aborting")))) (defun git-revert-file () @@ -1034,28 +1046,30 @@ Return the list of files that haven't been handled." ('unmerged (push (git-fileinfo->name info) modified)) ('modified (push (git-fileinfo->name info) modified)))) ;; check if a buffer contains one of the files and isn't saved - (dolist (file (append added modified)) + (dolist (file modified) (let ((buffer (get-file-buffer file))) (when (and buffer (buffer-modified-p buffer)) (error "Buffer %s is modified. Please kill or save modified buffers before reverting." (buffer-name buffer))))) - (when added - (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added)) - (when modified - (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) - (git-update-status-files (append added modified) 'uptodate) - (dolist (file (append added modified)) - (let ((buffer (get-file-buffer file))) - (when buffer (with-current-buffer buffer (revert-buffer t t t))))) - (git-success-message "Reverted" (git-get-filenames files))))) + (let ((ok (and + (or (not added) + (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added)) + (or (not modified) + (apply 'git-call-process-display-error "checkout" "HEAD" modified))))) + (git-update-status-files (append added modified) 'uptodate) + (when ok + (dolist (file modified) + (let ((buffer (get-file-buffer file))) + (when buffer (with-current-buffer buffer (revert-buffer t t t))))) + (git-success-message "Reverted" (git-get-filenames files))))))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." (interactive) (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files - (apply #'git-call-process-env nil nil "update-index" "--" files) - (git-update-status-files files 'uptodate) - (git-success-message "Resolved" files)))) + (when (apply 'git-call-process-display-error "update-index" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Resolved" files))))) (defun git-remove-handled () "Remove handled files from the status list." @@ -1320,12 +1334,12 @@ amended version of it." (when (git-empty-db-p) (error "No commit to amend.")) (let* ((commit (git-rev-parse "HEAD")) (files (git-get-commit-files commit))) - (git-call-process-env nil nil "reset" "--soft" "HEAD^") - (git-update-status-files (copy-sequence files) 'uptodate) - (git-mark-files git-status files) - (git-refresh-files) - (git-setup-commit-buffer commit) - (git-commit-file))) + (when (git-call-process-display-error "reset" "--soft" "HEAD^") + (git-update-status-files (copy-sequence files) 'uptodate) + (git-mark-files git-status files) + (git-refresh-files) + (git-setup-commit-buffer commit) + (git-commit-file)))) (defun git-find-file () "Visit the current file in its own buffer." -- cgit v1.2.3 From 24a2293ad35d567530048f0d2b0d11e0012af26d Mon Sep 17 00:00:00 2001 From: Junichi Uekawa Date: Tue, 12 Feb 2008 00:00:07 +0900 Subject: git-blame.el: show the when, who and what in the minibuffer. Change the default operation to show 'when (day the commit was made), who (who made the commit), what (what the commit log was)' in the minibuffer instead of SHA1 and title of the commit log. Since the user may prefer other displaying options, it is made as a user-configurable option. Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index bb671d561e..9f92cd250b 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -105,6 +105,13 @@ selected element from l." (setq ,l (remove e ,l)) e)) +(defvar git-blame-log-oneline-format + "format:[%cr] %cn: %s" + "*Formatting option used for describing current line in the minibuffer. + +This option is used to pass to git log --pretty= command-line option, +and describe which commit the current line was made.") + (defvar git-blame-dark-colors (git-blame-color-scale "0c" "04" "24" "1c" "2c" "34" "14" "3c") "*List of colors (format #RGB) to use in a dark environment. @@ -371,7 +378,8 @@ See also function `git-blame-mode'." (defun git-describe-commit (hash) (with-temp-buffer (call-process "git" nil t nil - "log" "-1" "--pretty=oneline" + "log" "-1" + (concat "--pretty=" git-blame-log-oneline-format) hash) (buffer-substring (point-min) (1- (point-max))))) -- cgit v1.2.3 From 13bf1a99764ea751f6fa75502309d8b91529623a Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Fri, 15 Feb 2008 22:20:44 +0100 Subject: hg-to-git: fix parent analysis Fix a bug in the hg-to-git convertor introduced by commit 1bc7c13af9f936aa80893100120b542338a10bf4: when searching the changeset parents, 'hg log' returns an extra space at the end of the line, which confuses the .split(' ') based tokenizer: Traceback (most recent call last): File "hg-to-git.py", line 123, in hgchildren[mparent] += ( str(cset), ) KeyError: '' Signed-off-by: Stelian Pop Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index c35b15860d..d72ffbb777 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -111,7 +111,7 @@ hgparents["0"] = (None, None) hgbranch["0"] = "master" for cset in range(1, int(tip) + 1): hgchildren[str(cset)] = () - prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().split(' ') + prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().strip().split(' ') prnts = map(lambda x: x[:x.find(':')], prnts) if prnts[0] != '': parent = prnts[0].strip() -- cgit v1.2.3 From 782c2d65c24066a5d83453efb52763bc34c10f81 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Thu, 7 Feb 2008 11:40:23 -0500 Subject: Build in checkout The only differences in behavior should be: - git checkout -m with non-trivial merging won't print out merge-recursive messages (see the change in t7201-co.sh) - git checkout -- paths... will give a sensible error message if HEAD is invalid as a commit. - some intermediate states which were written to disk in the shell version (in particular, index states) are only kept in memory in this version, and therefore these can no longer be revealed by later write operations becoming impossible. - when we change branches, we discard MERGE_MSG, SQUASH_MSG, and rr-cache/MERGE_RR, like reset always has. I'm not 100% sure I got the merge recursive setup exactly right; the base for a non-trivial merge in the shell code doesn't seem theoretically justified to me, but I tried to match it anyway, and the tests all pass this way. Other than these items, the results should be identical to the shell version, so far as I can tell. [jc: squashed lock-file fix from Dscho in] Signed-off-by: Daniel Barkalow Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/examples/git-checkout.sh | 298 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100755 contrib/examples/git-checkout.sh (limited to 'contrib') diff --git a/contrib/examples/git-checkout.sh b/contrib/examples/git-checkout.sh new file mode 100755 index 0000000000..5621c69d86 --- /dev/null +++ b/contrib/examples/git-checkout.sh @@ -0,0 +1,298 @@ +#!/bin/sh + +OPTIONS_KEEPDASHDASH=t +OPTIONS_SPEC="\ +git-checkout [options] [] [...] +-- +b= create a new branch started at +l create the new branch's reflog +track arrange that the new branch tracks the remote branch +f proceed even if the index or working tree is not HEAD +m merge local modifications into the new branch +q,quiet be quiet +" +SUBDIRECTORY_OK=Sometimes +. git-sh-setup +require_work_tree + +old_name=HEAD +old=$(git rev-parse --verify $old_name 2>/dev/null) +oldbranch=$(git symbolic-ref $old_name 2>/dev/null) +new= +new_name= +force= +branch= +track= +newbranch= +newbranch_log= +merge= +quiet= +v=-v +LF=' +' + +while test $# != 0; do + case "$1" in + -b) + shift + newbranch="$1" + [ -z "$newbranch" ] && + die "git checkout: -b needs a branch name" + git show-ref --verify --quiet -- "refs/heads/$newbranch" && + die "git checkout: branch $newbranch already exists" + git check-ref-format "heads/$newbranch" || + die "git checkout: we do not like '$newbranch' as a branch name." + ;; + -l) + newbranch_log=-l + ;; + --track|--no-track) + track="$1" + ;; + -f) + force=1 + ;; + -m) + merge=1 + ;; + -q|--quiet) + quiet=1 + v= + ;; + --) + shift + break + ;; + *) + usage + ;; + esac + shift +done + +arg="$1" +if rev=$(git rev-parse --verify "$arg^0" 2>/dev/null) +then + [ -z "$rev" ] && die "unknown flag $arg" + new_name="$arg" + if git show-ref --verify --quiet -- "refs/heads/$arg" + then + rev=$(git rev-parse --verify "refs/heads/$arg^0") + branch="$arg" + fi + new="$rev" + shift +elif rev=$(git rev-parse --verify "$arg^{tree}" 2>/dev/null) +then + # checking out selected paths from a tree-ish. + new="$rev" + new_name="$arg^{tree}" + shift +fi +[ "$1" = "--" ] && shift + +case "$newbranch,$track" in +,--*) + die "git checkout: --track and --no-track require -b" +esac + +case "$force$merge" in +11) + die "git checkout: -f and -m are incompatible" +esac + +# The behaviour of the command with and without explicit path +# parameters is quite different. +# +# Without paths, we are checking out everything in the work tree, +# possibly switching branches. This is the traditional behaviour. +# +# With paths, we are _never_ switching branch, but checking out +# the named paths from either index (when no rev is given), +# or the named tree-ish (when rev is given). + +if test "$#" -ge 1 +then + hint= + if test "$#" -eq 1 + then + hint=" +Did you intend to checkout '$@' which can not be resolved as commit?" + fi + if test '' != "$newbranch$force$merge" + then + die "git checkout: updating paths is incompatible with switching branches/forcing$hint" + fi + if test '' != "$new" + then + # from a specific tree-ish; note that this is for + # rescuing paths and is never meant to remove what + # is not in the named tree-ish. + git ls-tree --full-name -r "$new" "$@" | + git update-index --index-info || exit $? + fi + + # Make sure the request is about existing paths. + git ls-files --full-name --error-unmatch -- "$@" >/dev/null || exit + git ls-files --full-name -- "$@" | + (cd_to_toplevel && git checkout-index -f -u --stdin) + + # Run a post-checkout hook -- the HEAD does not change so the + # current HEAD is passed in for both args + if test -x "$GIT_DIR"/hooks/post-checkout; then + "$GIT_DIR"/hooks/post-checkout $old $old 0 + fi + + exit $? +else + # Make sure we did not fall back on $arg^{tree} codepath + # since we are not checking out from an arbitrary tree-ish, + # but switching branches. + if test '' != "$new" + then + git rev-parse --verify "$new^{commit}" >/dev/null 2>&1 || + die "Cannot switch branch to a non-commit." + fi +fi + +# We are switching branches and checking out trees, so +# we *NEED* to be at the toplevel. +cd_to_toplevel + +[ -z "$new" ] && new=$old && new_name="$old_name" + +# If we don't have an existing branch that we're switching to, +# and we don't have a new branch name for the target we +# are switching to, then we are detaching our HEAD from any +# branch. However, if "git checkout HEAD" detaches the HEAD +# from the current branch, even though that may be logically +# correct, it feels somewhat funny. More importantly, we do not +# want "git checkout" nor "git checkout -f" to detach HEAD. + +detached= +detach_warn= + +describe_detached_head () { + test -n "$quiet" || { + printf >&2 "$1 " + GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" -- + } +} + +if test -z "$branch$newbranch" && test "$new_name" != "$old_name" +then + detached="$new" + if test -n "$oldbranch" && test -z "$quiet" + then + detach_warn="Note: moving to \"$new_name\" which isn't a local branch +If you want to create a new branch from this checkout, you may do so +(now or later) by using -b with the checkout command again. Example: + git checkout -b " + fi +elif test -z "$oldbranch" && test "$new" != "$old" +then + describe_detached_head 'Previous HEAD position was' "$old" +fi + +if [ "X$old" = X ] +then + if test -z "$quiet" + then + echo >&2 "warning: You appear to be on a branch yet to be born." + echo >&2 "warning: Forcing checkout of $new_name." + fi + force=1 +fi + +if [ "$force" ] +then + git read-tree $v --reset -u $new +else + git update-index --refresh >/dev/null + merge_error=$(git read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || ( + case "$merge" in + '') + echo >&2 "$merge_error" + exit 1 ;; + esac + + # Match the index to the working tree, and do a three-way. + git diff-files --name-only | git update-index --remove --stdin && + work=`git write-tree` && + git read-tree $v --reset -u $new || exit + + eval GITHEAD_$new='${new_name:-${branch:-$new}}' && + eval GITHEAD_$work=local && + export GITHEAD_$new GITHEAD_$work && + git merge-recursive $old -- $new $work + + # Do not register the cleanly merged paths in the index yet. + # this is not a real merge before committing, but just carrying + # the working tree changes along. + unmerged=`git ls-files -u` + git read-tree $v --reset $new + case "$unmerged" in + '') ;; + *) + ( + z40=0000000000000000000000000000000000000000 + echo "$unmerged" | + sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /" + echo "$unmerged" + ) | git update-index --index-info + ;; + esac + exit 0 + ) + saved_err=$? + if test "$saved_err" = 0 && test -z "$quiet" + then + git diff-index --name-status "$new" + fi + (exit $saved_err) +fi + +# +# Switch the HEAD pointer to the new branch if we +# checked out a branch head, and remove any potential +# old MERGE_HEAD's (subsequent commits will clearly not +# be based on them, since we re-set the index) +# +if [ "$?" -eq 0 ]; then + if [ "$newbranch" ]; then + git branch $track $newbranch_log "$newbranch" "$new_name" || exit + branch="$newbranch" + fi + if test -n "$branch" + then + old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'` + GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from ${old_branch_name:-$old} to $branch" HEAD "refs/heads/$branch" + if test -n "$quiet" + then + true # nothing + elif test "refs/heads/$branch" = "$oldbranch" + then + echo >&2 "Already on branch \"$branch\"" + else + echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\"" + fi + elif test -n "$detached" + then + old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'` + git update-ref --no-deref -m "checkout: moving from ${old_branch_name:-$old} to $arg" HEAD "$detached" || + die "Cannot detach HEAD" + if test -n "$detach_warn" + then + echo >&2 "$detach_warn" + fi + describe_detached_head 'HEAD is now at' HEAD + fi + rm -f "$GIT_DIR/MERGE_HEAD" +else + exit 1 +fi + +# Run a post-checkout hook +if test -x "$GIT_DIR"/hooks/post-checkout; then + "$GIT_DIR"/hooks/post-checkout $old $new 1 +fi -- cgit v1.2.3 From f27e55864317611385be4d33b3c53ca787379df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Tue, 19 Feb 2008 15:01:53 +0100 Subject: git.el: Set process-environment instead of invoking env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will make it a little less posix-dependent, and more efficient. Included is also a minor doc improvement. Signed-off-by: David Kågedal Acked-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index a8bf0ef883..f69b697f8d 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -185,9 +185,8 @@ if there is already one that displays the same directory." (defun git-call-process-env (buffer env &rest args) "Wrapper for call-process that sets environment strings." - (if env - (apply #'call-process "env" nil buffer nil - (append (git-get-env-strings env) (list "git") args)) + (let ((process-environment (append (git-get-env-strings env) + process-environment))) (apply #'call-process "git" nil buffer nil args))) (defun git-call-process-display-error (&rest args) @@ -204,7 +203,7 @@ if there is already one that displays the same directory." (defun git-call-process-env-string (env &rest args) "Wrapper for call-process that sets environment strings, -and returns the process output as a string." +and returns the process output as a string, or nil if the git failed." (with-temp-buffer (and (eq 0 (apply #' git-call-process-env t env args)) (buffer-string)))) -- cgit v1.2.3 From 27c578885a0b8f56430c5a24f558e2b45cf04a38 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 24 Feb 2008 03:07:33 -0500 Subject: Use git-describe --exact-match in bash prompt on detached HEAD Most of the time when I am on a detached HEAD and I am not doing a rebase or bisect operation the working directory is sitting on a tagged release of the repository. Showing the tag name instead of the commit SHA-1 is much more descriptive and a much better reminder of the state of this working directory. Now that git-describe --exact-match is available as a cheap means of obtaining the exact annotated tag or nothing at all, we can favor the annotated tag name over the abbreviated commit SHA-1. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 4ea727b143..8722a68795 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -91,7 +91,10 @@ __git_ps1 () fi if ! b="$(git symbolic-ref HEAD 2>/dev/null)" then - b="$(cut -c1-7 $g/HEAD)..." + if ! b="$(git describe --exact-match HEAD 2>/dev/null)" + then + b="$(cut -c1-7 $g/HEAD)..." + fi fi fi -- cgit v1.2.3 From 354081d5a0392a015731a637f026dc885a4ddb0f Mon Sep 17 00:00:00 2001 From: Tommy Thorn Date: Sun, 3 Feb 2008 10:38:51 -0800 Subject: git-p4: support exclude paths Teach git-p4 about the -/ option which adds depot paths to the exclude list, used when cloning. The option is chosen such that the natural Perforce syntax works, eg: git p4 clone //branch/path/... -//branch/path/{large,old}/... Trailing ... on exclude paths are optional. This is a generalization of a change by Dmitry Kakurin (thanks). Signed-off-by: Tommy Thorn Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 781a0cbbbc..e4238538a1 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -843,18 +843,25 @@ class P4Sync(Command): self.keepRepoPath = False self.depotPaths = None self.p4BranchesInGit = [] + self.cloneExclude = [] if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False def extractFilesFromCommit(self, commit): + self.cloneExclude = [re.sub(r"\.\.\.$", "", path) + for path in self.cloneExclude] files = [] fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] - found = [p for p in self.depotPaths - if path.startswith (p)] + if [p for p in self.cloneExclude + if path.startswith (p)]: + found = False + else: + found = [p for p in self.depotPaths + if path.startswith (p)] if not found: fnum = fnum + 1 continue @@ -1634,13 +1641,23 @@ class P4Clone(P4Sync): P4Sync.__init__(self) self.description = "Creates a new git repository and imports from Perforce into it" self.usage = "usage: %prog [options] //depot/path[@revRange]" - self.options.append( + self.options += [ optparse.make_option("--destination", dest="cloneDestination", action='store', default=None, - help="where to leave result of the clone")) + help="where to leave result of the clone"), + optparse.make_option("-/", dest="cloneExclude", + action="append", type="string", + help="exclude depot path") + ] self.cloneDestination = None self.needsGit = False + # This is required for the "append" cloneExclude action + def ensure_value(self, attr, value): + if not hasattr(self, attr) or getattr(self, attr) is None: + setattr(self, attr, value) + return getattr(self, attr) + def defaultDestination(self, args): ## TODO: use common prefix of args? depotPath = args[0] @@ -1664,6 +1681,7 @@ class P4Clone(P4Sync): self.cloneDestination = depotPaths[-1] depotPaths = depotPaths[:-1] + self.cloneExclude = ["/"+p for p in self.cloneExclude] for p in depotPaths: if not p.startswith("//"): return False -- cgit v1.2.3 From 4b61b5c963ad1fd59e858c2bc21b1b48836f04f8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 19 Feb 2008 09:12:29 +0100 Subject: git-p4: Remove --log-substitutions feature. This turns out to be rarely useful and is already covered by git's commit.template configuration variable. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 ------- 1 file changed, 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e4238538a1..f2a6059a7e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -468,7 +468,6 @@ class P4Submit(Command): optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), - optparse.make_option("--log-substitutions", dest="substFile"), optparse.make_option("--direct", dest="directSubmit", action="store_true"), optparse.make_option("-M", dest="detectRename", action="store_true"), ] @@ -477,7 +476,6 @@ class P4Submit(Command): self.firstTime = True self.reset = False self.interactive = True - self.substFile = "" self.firstTime = True self.origin = "" self.directSubmit = False @@ -759,11 +757,6 @@ class P4Submit(Command): if self.reset: self.firstTime = True - if len(self.substFile) > 0: - for line in open(self.substFile, "r").readlines(): - tokens = line.strip().split("=") - self.logSubstitutions[tokens[0]] = tokens[1] - self.check() self.configFile = self.gitdir + "/p4-git-sync.cfg" self.config = shelve.open(self.configFile, writeback=True) -- cgit v1.2.3 From edae1e2f407d0e9c6c92333047bc31b27bfdd58f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 19 Feb 2008 09:29:06 +0100 Subject: git-p4: Clean up git-p4 submit's log message handling. Instead of trying to substitute fields in the p4 submit template we now simply replace the description of the submit with the log message of the git commit. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f2a6059a7e..e55a41b10e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -483,10 +483,6 @@ class P4Submit(Command): self.verbose = False self.isWindows = (platform.system() == "Windows") - self.logSubstitutions = {} - self.logSubstitutions[""] = "%log%" - self.logSubstitutions["\tDetails:"] = "\tDetails: %log%" - def check(self): if len(p4CmdList("opened ...")) > 0: die("You have files opened with perforce! Close them before starting the sync.") @@ -507,26 +503,31 @@ class P4Submit(Command): self.config["commits"] = commits + # replaces everything between 'Description:' and the next P4 submit template field with the + # commit message def prepareLogMessage(self, template, message): result = "" + inDescriptionSection = False + for line in template.split("\n"): if line.startswith("#"): result += line + "\n" continue - substituted = False - for key in self.logSubstitutions.keys(): - if line.find(key) != -1: - value = self.logSubstitutions[key] - value = value.replace("%log%", message) - if value != "@remove@": - result += line.replace(key, value) + "\n" - substituted = True - break + if inDescriptionSection: + if line.startswith("Files:"): + inDescriptionSection = False + else: + continue + else: + if line.startswith("Description:"): + inDescriptionSection = True + line += "\n" + for messageLine in message.split("\n"): + line += "\t" + messageLine + "\n" - if not substituted: - result += line + "\n" + result += line + "\n" return result @@ -650,7 +651,6 @@ class P4Submit(Command): logMessage = "" if not self.directSubmit: logMessage = extractLogMessageFromGitCommit(id) - logMessage = logMessage.replace("\n", "\n\t") if self.isWindows: logMessage = logMessage.replace("\n", "\r\n") logMessage = logMessage.strip() -- cgit v1.2.3 From 0e36f2d726d4ce50c3f128bcc168d59ad03e804f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 19 Feb 2008 09:33:08 +0100 Subject: git-p4: Removed git-p4 submit --direct. This feature was originally meant to allow for quicker direct submits into perforce, but it turns out that it is not actually quicker than doing a git commit and then running git-p4 submit. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 56 +++++++++------------------------------------- 1 file changed, 11 insertions(+), 45 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e55a41b10e..087f4229a0 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -468,7 +468,6 @@ class P4Submit(Command): optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), - optparse.make_option("--direct", dest="directSubmit", action="store_true"), optparse.make_option("-M", dest="detectRename", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." @@ -478,7 +477,6 @@ class P4Submit(Command): self.interactive = True self.firstTime = True self.origin = "" - self.directSubmit = False self.detectRename = False self.verbose = False self.isWindows = (platform.system() == "Windows") @@ -494,12 +492,9 @@ class P4Submit(Command): "maybe you want to call git-p4 submit --reset" % self.configFile) commits = [] - if self.directSubmit: - commits.append("0") - else: - for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): - commits.append(line.strip()) - commits.reverse() + for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): + commits.append(line.strip()) + commits.reverse() self.config["commits"] = commits @@ -556,13 +551,9 @@ class P4Submit(Command): return template def applyCommit(self, id): - if self.directSubmit: - print "Applying local change in working directory/index" - diff = self.diffStatus - else: - print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) - diffOpts = ("", "-M")[self.detectRename] - diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id)) + print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) + diffOpts = ("", "-M")[self.detectRename] + diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id)) filesToAdd = set() filesToDelete = set() editedFiles = set() @@ -597,10 +588,7 @@ class P4Submit(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - if self.directSubmit: - diffcmd = "cat \"%s\"" % self.diffFile - else: - diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) + diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) patchcmd = diffcmd + " | git apply " tryPatchCmd = patchcmd + "--check -" applyPatchCmd = patchcmd + "--check --apply -" @@ -648,12 +636,10 @@ class P4Submit(Command): mode = filesToChangeExecBit[f] setP4ExecBit(f, mode) - logMessage = "" - if not self.directSubmit: - logMessage = extractLogMessageFromGitCommit(id) - if self.isWindows: - logMessage = logMessage.replace("\n", "\r\n") - logMessage = logMessage.strip() + logMessage = extractLogMessageFromGitCommit(id) + if self.isWindows: + logMessage = logMessage.replace("\n", "\r\n") + logMessage = logMessage.strip() template = self.prepareSubmitTemplate() @@ -692,12 +678,6 @@ class P4Submit(Command): if self.isWindows: submitTemplate = submitTemplate.replace("\r\n", "\n") - if self.directSubmit: - print "Submitting to git first" - os.chdir(self.oldWorkingDirectory) - write_pipe("git commit -a -F -", submitTemplate) - os.chdir(self.clientPath) - write_pipe("p4 submit -i", submitTemplate) else: fileName = "submit.txt" @@ -739,17 +719,6 @@ class P4Submit(Command): print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath) self.oldWorkingDirectory = os.getcwd() - if self.directSubmit: - self.diffStatus = read_pipe_lines("git diff -r --name-status HEAD") - if len(self.diffStatus) == 0: - print "No changes in working directory to submit." - return True - patch = read_pipe("git diff -p --binary --diff-filter=ACMRTUXB HEAD") - self.diffFile = self.gitdir + "/p4-git-diff" - f = open(self.diffFile, "wb") - f.write(patch) - f.close(); - os.chdir(self.clientPath) print "Syncronizing p4 checkout..." system("p4 sync ...") @@ -777,9 +746,6 @@ class P4Submit(Command): self.config.close() - if self.directSubmit: - os.remove(self.diffFile) - if len(commits) == 0: if self.firstTime: print "No changes found to apply between %s and current HEAD" % self.origin -- cgit v1.2.3 From 4c750c0d8bfd7e75b86d660def012bff17d2bf8e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 19 Feb 2008 09:37:16 +0100 Subject: git-p4: git-p4 submit cleanups. Removed storing the list of commits in a configuration file. We only need the list of commits at run-time. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 51 +++++++++------------------------------------- 1 file changed, 10 insertions(+), 41 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 087f4229a0..d59ed945de 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -464,18 +464,13 @@ class P4Submit(Command): def __init__(self): Command.__init__(self) self.options = [ - optparse.make_option("--continue", action="store_false", dest="firstTime"), optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--origin", dest="origin"), - optparse.make_option("--reset", action="store_true", dest="reset"), optparse.make_option("-M", dest="detectRename", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" - self.firstTime = True - self.reset = False self.interactive = True - self.firstTime = True self.origin = "" self.detectRename = False self.verbose = False @@ -485,19 +480,6 @@ class P4Submit(Command): if len(p4CmdList("opened ...")) > 0: die("You have files opened with perforce! Close them before starting the sync.") - def start(self): - if len(self.config) > 0 and not self.reset: - die("Cannot start sync. Previous sync config found at %s\n" - "If you want to start submitting again from scratch " - "maybe you want to call git-p4 submit --reset" % self.configFile) - - commits = [] - for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): - commits.append(line.strip()) - commits.reverse() - - self.config["commits"] = commits - # replaces everything between 'Description:' and the next P4 submit template field with the # commit message def prepareLogMessage(self, template, message): @@ -723,42 +705,29 @@ class P4Submit(Command): print "Syncronizing p4 checkout..." system("p4 sync ...") - if self.reset: - self.firstTime = True - self.check() - self.configFile = self.gitdir + "/p4-git-sync.cfg" - self.config = shelve.open(self.configFile, writeback=True) - if self.firstTime: - self.start() - - commits = self.config.get("commits", []) + commits = [] + for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): + commits.append(line.strip()) + commits.reverse() while len(commits) > 0: - self.firstTime = False commit = commits[0] commits = commits[1:] - self.config["commits"] = commits self.applyCommit(commit) if not self.interactive: break - self.config.close() - if len(commits) == 0: - if self.firstTime: - print "No changes found to apply between %s and current HEAD" % self.origin - else: - print "All changes applied!" - os.chdir(self.oldWorkingDirectory) + print "All changes applied!" + os.chdir(self.oldWorkingDirectory) - sync = P4Sync() - sync.run([]) + sync = P4Sync() + sync.run([]) - rebase = P4Rebase() - rebase.rebase() - os.remove(self.configFile) + rebase = P4Rebase() + rebase.rebase() return True -- cgit v1.2.3 From 3a70cdfa42199e16d2d047c286431c4274d65b1a Mon Sep 17 00:00:00 2001 From: Tor Arvid Lund Date: Mon, 18 Feb 2008 15:22:08 +0100 Subject: git-p4: Support usage of perforce client spec When syncing, git-p4 will only download files that are included in the active perforce client spec. This does not change the default behaviour - it requires that the user either supplies the command line argument --use-client-spec, or sets the git config option p4.useclientspec to "true". Signed-off-by: Tor Arvid Lund Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 50 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d59ed945de..be96600753 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -745,7 +745,9 @@ class P4Sync(Command): help="Import into refs/heads/ , not refs/remotes"), optparse.make_option("--max-changes", dest="maxChanges"), optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true', - help="Keep entire BRANCH/DIR/SUBDIR prefix during import") + help="Keep entire BRANCH/DIR/SUBDIR prefix during import"), + optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true', + help="Only sync files that are included in the Perforce Client Spec") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -772,6 +774,8 @@ class P4Sync(Command): self.depotPaths = None self.p4BranchesInGit = [] self.cloneExclude = [] + self.useClientSpec = False + self.clientSpecDirs = [] if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False @@ -846,11 +850,21 @@ class P4Sync(Command): ## Should move this out, doesn't use SELF. def readP4Files(self, files): + for f in files: + for val in self.clientSpecDirs: + if f['path'].startswith(val[0]): + if val[1] > 0: + f['include'] = True + else: + f['include'] = False + break + files = [f for f in files - if f['action'] != 'delete'] + if f['action'] != 'delete' and + (f.has_key('include') == False or f['include'] == True)] if not files: - return + return [] filedata = p4CmdList('-x - print', stdin='\n'.join(['%s#%s' % (f['path'], f['rev']) @@ -885,6 +899,7 @@ class P4Sync(Command): for f in files: assert not f.has_key('data') f['data'] = contents[f['path']] + return files def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] @@ -901,11 +916,7 @@ class P4Sync(Command): new_files.append (f) else: sys.stderr.write("Ignoring file outside of prefix: %s\n" % path) - files = new_files - self.readP4Files(files) - - - + files = self.readP4Files(new_files) self.gitStream.write("commit %s\n" % branch) # gitStream.write("mark :%s\n" % details["change"]) @@ -1320,6 +1331,26 @@ class P4Sync(Command): print self.gitError.read() + def getClientSpec(self): + specList = p4CmdList( "client -o" ) + temp = {} + for entry in specList: + for k,v in entry.iteritems(): + if k.startswith("View"): + if v.startswith('"'): + start = 1 + else: + start = 0 + index = v.find("...") + v = v[start:index] + if v.startswith("-"): + v = v[1:] + temp[v] = -len(v) + else: + temp[v] = len(v) + self.clientSpecDirs = temp.items() + self.clientSpecDirs.sort( lambda x, y: abs( y[1] ) - abs( x[1] ) ) + def run(self, args): self.depotPaths = [] self.changeRange = "" @@ -1352,6 +1383,9 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch): system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) + if self.useClientSpec or gitConfig("p4.useclientspec") == "true": + self.getClientSpec() + # TODO: should always look at previous commits, # merge with previous imports, if possible. if args == []: -- cgit v1.2.3 From 21a2d69b2a14f1b42b18cfd18ff0477bb97eb11f Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 22 Feb 2008 16:48:53 +0100 Subject: git.el: Do not display empty directories. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexandre Julliard Tested-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index f69b697f8d..cc21e9c682 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -728,7 +728,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-files-with-excludes (status files default-state &rest options) "Run git-ls-files on FILES with appropriate --exclude-from options." (let ((exclude-files (git-get-exclude-files))) - (apply #'git-run-ls-files status files default-state "--directory" + (apply #'git-run-ls-files status files default-state "--directory" "--no-empty-directory" (concat "--exclude-per-directory=" git-per-dir-ignore-file) (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) -- cgit v1.2.3 From be5f5bf02706357794cdf093ebe603aa82ae52eb Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 21 Feb 2008 16:21:49 +0000 Subject: completion: support format-patch's --cover-letter option Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8722a68795..8f70e1efc1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -648,6 +648,7 @@ _git_format_patch () --in-reply-to= --full-index --binary --not --all + --cover-letter " return ;; -- cgit v1.2.3 From a1eebfb3a90b6c240afd1a32cfebe6ee5dbd72c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vanicat?= Date: Fri, 29 Feb 2008 19:28:19 +0100 Subject: git.el: find the git-status buffer whatever its name is MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-status used the buffer name to find git-status buffers, and that can fail if the buffer has another name, for example when multiple working directories is tracked. Signed-off-by: Rémi Vanicat Acked-by: Alexandre Julliard Tested-by: Xavier Maillard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d8a06381f4..0312d891fd 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1432,7 +1432,7 @@ Commands: (with-current-buffer buffer (when (and list-buffers-directory (string-equal fulldir (expand-file-name list-buffers-directory)) - (string-match "\\*git-status\\*$" (buffer-name buffer))) + (eq major-mode 'git-status-mode)) (setq found buffer)))) (setq list (cdr list))) found)) -- cgit v1.2.3 From 5f4347bba39ddb147b06913ac263fc46954d2d0b Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 28 Feb 2008 00:25:20 -0500 Subject: add storage size output to 'git verify-pack -v' This can possibly break external scripts that depend on the previous output, but those script can't possibly be critical to Git usage, and fixing them should be trivial. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- contrib/stats/packinfo.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl index aab501ea08..f4a7b62cd9 100755 --- a/contrib/stats/packinfo.pl +++ b/contrib/stats/packinfo.pl @@ -93,7 +93,7 @@ my %depths; my @depths; while () { - my ($sha1, $type, $size, $offset, $depth, $parent) = split(/\s+/, $_); + my ($sha1, $type, $size, $space, $offset, $depth, $parent) = split(/\s+/, $_); next unless ($sha1 =~ /^[0-9a-f]{40}$/); $depths{$sha1} = $depth || 0; push(@depths, $depth || 0); -- cgit v1.2.3 From 211c89682eeef310f39022b91e88d07cd5784953 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 29 Feb 2008 01:45:45 +0000 Subject: Make git-remote a builtin Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/examples/git-remote.perl | 477 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 477 insertions(+) create mode 100755 contrib/examples/git-remote.perl (limited to 'contrib') diff --git a/contrib/examples/git-remote.perl b/contrib/examples/git-remote.perl new file mode 100755 index 0000000000..5cd69513cf --- /dev/null +++ b/contrib/examples/git-remote.perl @@ -0,0 +1,477 @@ +#!/usr/bin/perl -w + +use strict; +use Git; +my $git = Git->repository(); + +sub add_remote_config { + my ($hash, $name, $what, $value) = @_; + if ($what eq 'url') { + if (exists $hash->{$name}{'URL'}) { + print STDERR "Warning: more than one remote.$name.url\n"; + } + $hash->{$name}{'URL'} = $value; + } + elsif ($what eq 'fetch') { + $hash->{$name}{'FETCH'} ||= []; + push @{$hash->{$name}{'FETCH'}}, $value; + } + elsif ($what eq 'push') { + $hash->{$name}{'PUSH'} ||= []; + push @{$hash->{$name}{'PUSH'}}, $value; + } + if (!exists $hash->{$name}{'SOURCE'}) { + $hash->{$name}{'SOURCE'} = 'config'; + } +} + +sub add_remote_remotes { + my ($hash, $file, $name) = @_; + + if (exists $hash->{$name}) { + $hash->{$name}{'WARNING'} = 'ignored due to config'; + return; + } + + my $fh; + if (!open($fh, '<', $file)) { + print STDERR "Warning: cannot open $file\n"; + return; + } + my $it = { 'SOURCE' => 'remotes' }; + $hash->{$name} = $it; + while (<$fh>) { + chomp; + if (/^URL:\s*(.*)$/) { + # Having more than one is Ok -- it is used for push. + if (! exists $it->{'URL'}) { + $it->{'URL'} = $1; + } + } + elsif (/^Push:\s*(.*)$/) { + $it->{'PUSH'} ||= []; + push @{$it->{'PUSH'}}, $1; + } + elsif (/^Pull:\s*(.*)$/) { + $it->{'FETCH'} ||= []; + push @{$it->{'FETCH'}}, $1; + } + elsif (/^\#/) { + ; # ignore + } + else { + print STDERR "Warning: funny line in $file: $_\n"; + } + } + close($fh); +} + +sub list_remote { + my ($git) = @_; + my %seen = (); + my @remotes = eval { + $git->command(qw(config --get-regexp), '^remote\.'); + }; + for (@remotes) { + if (/^remote\.(\S+?)\.([^.\s]+)\s+(.*)$/) { + add_remote_config(\%seen, $1, $2, $3); + } + } + + my $dir = $git->repo_path() . "/remotes"; + if (opendir(my $dh, $dir)) { + local $_; + while ($_ = readdir($dh)) { + chomp; + next if (! -f "$dir/$_" || ! -r _); + add_remote_remotes(\%seen, "$dir/$_", $_); + } + } + + return \%seen; +} + +sub add_branch_config { + my ($hash, $name, $what, $value) = @_; + if ($what eq 'remote') { + if (exists $hash->{$name}{'REMOTE'}) { + print STDERR "Warning: more than one branch.$name.remote\n"; + } + $hash->{$name}{'REMOTE'} = $value; + } + elsif ($what eq 'merge') { + $hash->{$name}{'MERGE'} ||= []; + push @{$hash->{$name}{'MERGE'}}, $value; + } +} + +sub list_branch { + my ($git) = @_; + my %seen = (); + my @branches = eval { + $git->command(qw(config --get-regexp), '^branch\.'); + }; + for (@branches) { + if (/^branch\.([^.]*)\.(\S*)\s+(.*)$/) { + add_branch_config(\%seen, $1, $2, $3); + } + } + + return \%seen; +} + +my $remote = list_remote($git); +my $branch = list_branch($git); + +sub update_ls_remote { + my ($harder, $info) = @_; + + return if (($harder == 0) || + (($harder == 1) && exists $info->{'LS_REMOTE'})); + + my @ref = map { + s|^[0-9a-f]{40}\s+refs/heads/||; + $_; + } $git->command(qw(ls-remote --heads), $info->{'URL'}); + $info->{'LS_REMOTE'} = \@ref; +} + +sub list_wildcard_mapping { + my ($forced, $ours, $ls) = @_; + my %refs; + for (@$ls) { + $refs{$_} = 01; # bit #0 to say "they have" + } + for ($git->command('for-each-ref', "refs/remotes/$ours")) { + chomp; + next unless (s|^[0-9a-f]{40}\s[a-z]+\srefs/remotes/$ours/||); + next if ($_ eq 'HEAD'); + $refs{$_} ||= 0; + $refs{$_} |= 02; # bit #1 to say "we have" + } + my (@new, @stale, @tracked); + for (sort keys %refs) { + my $have = $refs{$_}; + if ($have == 1) { + push @new, $_; + } + elsif ($have == 2) { + push @stale, $_; + } + elsif ($have == 3) { + push @tracked, $_; + } + } + return \@new, \@stale, \@tracked; +} + +sub list_mapping { + my ($name, $info) = @_; + my $fetch = $info->{'FETCH'}; + my $ls = $info->{'LS_REMOTE'}; + my (@new, @stale, @tracked); + + for (@$fetch) { + next unless (/(\+)?([^:]+):(.*)/); + my ($forced, $theirs, $ours) = ($1, $2, $3); + if ($theirs eq 'refs/heads/*' && + $ours =~ /^refs\/remotes\/(.*)\/\*$/) { + # wildcard mapping + my ($w_new, $w_stale, $w_tracked) + = list_wildcard_mapping($forced, $1, $ls); + push @new, @$w_new; + push @stale, @$w_stale; + push @tracked, @$w_tracked; + } + elsif ($theirs =~ /\*/ || $ours =~ /\*/) { + print STDERR "Warning: unrecognized mapping in remotes.$name.fetch: $_\n"; + } + elsif ($theirs =~ s|^refs/heads/||) { + if (!grep { $_ eq $theirs } @$ls) { + push @stale, $theirs; + } + elsif ($ours ne '') { + push @tracked, $theirs; + } + } + } + return \@new, \@stale, \@tracked; +} + +sub show_mapping { + my ($name, $info) = @_; + my ($new, $stale, $tracked) = list_mapping($name, $info); + if (@$new) { + print " New remote branches (next fetch will store in remotes/$name)\n"; + print " @$new\n"; + } + if (@$stale) { + print " Stale tracking branches in remotes/$name (use 'git remote prune')\n"; + print " @$stale\n"; + } + if (@$tracked) { + print " Tracked remote branches\n"; + print " @$tracked\n"; + } +} + +sub prune_remote { + my ($name, $ls_remote) = @_; + if (!exists $remote->{$name}) { + print STDERR "No such remote $name\n"; + return 1; + } + my $info = $remote->{$name}; + update_ls_remote($ls_remote, $info); + + my ($new, $stale, $tracked) = list_mapping($name, $info); + my $prefix = "refs/remotes/$name"; + foreach my $to_prune (@$stale) { + my @v = $git->command(qw(rev-parse --verify), "$prefix/$to_prune"); + $git->command(qw(update-ref -d), "$prefix/$to_prune", $v[0]); + } + return 0; +} + +sub show_remote { + my ($name, $ls_remote) = @_; + if (!exists $remote->{$name}) { + print STDERR "No such remote $name\n"; + return 1; + } + my $info = $remote->{$name}; + update_ls_remote($ls_remote, $info); + + print "* remote $name\n"; + print " URL: $info->{'URL'}\n"; + for my $branchname (sort keys %$branch) { + next unless (defined $branch->{$branchname}{'REMOTE'} && + $branch->{$branchname}{'REMOTE'} eq $name); + my @merged = map { + s|^refs/heads/||; + $_; + } split(' ',"@{$branch->{$branchname}{'MERGE'}}"); + next unless (@merged); + print " Remote branch(es) merged with 'git pull' while on branch $branchname\n"; + print " @merged\n"; + } + if ($info->{'LS_REMOTE'}) { + show_mapping($name, $info); + } + if ($info->{'PUSH'}) { + my @pushed = map { + s|^refs/heads/||; + s|^\+refs/heads/|+|; + s|:refs/heads/|:|; + $_; + } @{$info->{'PUSH'}}; + print " Local branch(es) pushed with 'git push'\n"; + print " @pushed\n"; + } + return 0; +} + +sub add_remote { + my ($name, $url, $opts) = @_; + if (exists $remote->{$name}) { + print STDERR "remote $name already exists.\n"; + exit(1); + } + $git->command('config', "remote.$name.url", $url); + my $track = $opts->{'track'} || ["*"]; + + for (@$track) { + $git->command('config', '--add', "remote.$name.fetch", + $opts->{'mirror'} ? + "+refs/$_:refs/$_" : + "+refs/heads/$_:refs/remotes/$name/$_"); + } + if ($opts->{'fetch'}) { + $git->command('fetch', $name); + } + if (exists $opts->{'master'}) { + $git->command('symbolic-ref', "refs/remotes/$name/HEAD", + "refs/remotes/$name/$opts->{'master'}"); + } +} + +sub update_remote { + my ($name) = @_; + my @remotes; + + my $conf = $git->config("remotes." . $name); + if (defined($conf)) { + @remotes = split(' ', $conf); + } elsif ($name eq 'default') { + @remotes = (); + for (sort keys %$remote) { + my $do_fetch = $git->config_bool("remote." . $_ . + ".skipDefaultUpdate"); + unless ($do_fetch) { + push @remotes, $_; + } + } + } else { + print STDERR "Remote group $name does not exists.\n"; + exit(1); + } + for (@remotes) { + print "Updating $_\n"; + $git->command('fetch', "$_"); + } +} + +sub rm_remote { + my ($name) = @_; + if (!exists $remote->{$name}) { + print STDERR "No such remote $name\n"; + return 1; + } + + $git->command('config', '--remove-section', "remote.$name"); + + eval { + my @trackers = $git->command('config', '--get-regexp', + 'branch.*.remote', $name); + for (@trackers) { + /^branch\.(.*)?\.remote/; + $git->config('--unset', "branch.$1.remote"); + $git->config('--unset', "branch.$1.merge"); + } + }; + + my @refs = $git->command('for-each-ref', + '--format=%(refname) %(objectname)', "refs/remotes/$name"); + for (@refs) { + my ($ref, $object) = split; + $git->command(qw(update-ref -d), $ref, $object); + } + return 0; +} + +sub add_usage { + print STDERR "Usage: git remote add [-f] [-t track]* [-m master] \n"; + exit(1); +} + +my $VERBOSE = 0; +@ARGV = grep { + if ($_ eq '-v' or $_ eq '--verbose') { + $VERBOSE=1; + 0 + } else { + 1 + } +} @ARGV; + +if (!@ARGV) { + for (sort keys %$remote) { + print "$_"; + print "\t$remote->{$_}->{URL}" if $VERBOSE; + print "\n"; + } +} +elsif ($ARGV[0] eq 'show') { + my $ls_remote = 1; + my $i; + for ($i = 1; $i < @ARGV; $i++) { + if ($ARGV[$i] eq '-n') { + $ls_remote = 0; + } + else { + last; + } + } + if ($i >= @ARGV) { + print STDERR "Usage: git remote show \n"; + exit(1); + } + my $status = 0; + for (; $i < @ARGV; $i++) { + $status |= show_remote($ARGV[$i], $ls_remote); + } + exit($status); +} +elsif ($ARGV[0] eq 'update') { + if (@ARGV <= 1) { + update_remote("default"); + exit(1); + } + for (my $i = 1; $i < @ARGV; $i++) { + update_remote($ARGV[$i]); + } +} +elsif ($ARGV[0] eq 'prune') { + my $ls_remote = 1; + my $i; + for ($i = 1; $i < @ARGV; $i++) { + if ($ARGV[$i] eq '-n') { + $ls_remote = 0; + } + else { + last; + } + } + if ($i >= @ARGV) { + print STDERR "Usage: git remote prune \n"; + exit(1); + } + my $status = 0; + for (; $i < @ARGV; $i++) { + $status |= prune_remote($ARGV[$i], $ls_remote); + } + exit($status); +} +elsif ($ARGV[0] eq 'add') { + my %opts = (); + while (1 < @ARGV && $ARGV[1] =~ /^-/) { + my $opt = $ARGV[1]; + shift @ARGV; + if ($opt eq '-f' || $opt eq '--fetch') { + $opts{'fetch'} = 1; + next; + } + if ($opt eq '-t' || $opt eq '--track') { + if (@ARGV < 1) { + add_usage(); + } + $opts{'track'} ||= []; + push @{$opts{'track'}}, $ARGV[1]; + shift @ARGV; + next; + } + if ($opt eq '-m' || $opt eq '--master') { + if ((@ARGV < 1) || exists $opts{'master'}) { + add_usage(); + } + $opts{'master'} = $ARGV[1]; + shift @ARGV; + next; + } + if ($opt eq '--mirror') { + $opts{'mirror'} = 1; + next; + } + add_usage(); + } + if (@ARGV != 3) { + add_usage(); + } + add_remote($ARGV[1], $ARGV[2], \%opts); +} +elsif ($ARGV[0] eq 'rm') { + if (@ARGV <= 1) { + print STDERR "Usage: git remote rm \n"; + exit(1); + } + exit(rm_remote($ARGV[1])); +} +else { + print STDERR "Usage: git remote\n"; + print STDERR " git remote add \n"; + print STDERR " git remote rm \n"; + print STDERR " git remote show \n"; + print STDERR " git remote prune \n"; + print STDERR " git remote update [group]\n"; + exit(1); +} -- cgit v1.2.3 From 30b5940bcd3ed7392795f6a27563013f6f806de4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 3 Mar 2008 11:55:48 +0100 Subject: git-p4: Fix import of changesets with file deletions Commit 3a70cdfa42199e16d2d047c286431c4274d65b1a made readP4Files abort quickly when the changeset only contains files that are marked for deletion with an empty return value, which caused the commit to not do anything. This commit changes readP4Files to distinguish between files that need to be passed to p4 print and files that have no content ("deleted") and merge them in the returned list. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index be96600753..650ea34176 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -850,29 +850,32 @@ class P4Sync(Command): ## Should move this out, doesn't use SELF. def readP4Files(self, files): + filesForCommit = [] + filesToRead = [] + for f in files: + includeFile = True for val in self.clientSpecDirs: if f['path'].startswith(val[0]): - if val[1] > 0: - f['include'] = True - else: - f['include'] = False + if val[1] <= 0: + includeFile = False break - files = [f for f in files - if f['action'] != 'delete' and - (f.has_key('include') == False or f['include'] == True)] + if includeFile: + filesForCommit.append(f) + if f['action'] != 'delete': + filesToRead.append(f) - if not files: - return [] + filedata = [] + if len(filesToRead) > 0: + filedata = p4CmdList('-x - print', + stdin='\n'.join(['%s#%s' % (f['path'], f['rev']) + for f in filesToRead]), + stdin_mode='w+') - filedata = p4CmdList('-x - print', - stdin='\n'.join(['%s#%s' % (f['path'], f['rev']) - for f in files]), - stdin_mode='w+') - if "p4ExitCode" in filedata[0]: - die("Problems executing p4. Error: [%d]." - % (filedata[0]['p4ExitCode'])); + if "p4ExitCode" in filedata[0]: + die("Problems executing p4. Error: [%d]." + % (filedata[0]['p4ExitCode'])); j = 0; contents = {} @@ -896,10 +899,12 @@ class P4Sync(Command): contents[stat['depotFile']] = text - for f in files: - assert not f.has_key('data') - f['data'] = contents[f['path']] - return files + for f in filesForCommit: + path = f['path'] + if contents.has_key(path): + f['data'] = contents[path] + + return filesForCommit def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] -- cgit v1.2.3 From 3041c324305e2bad59d7372336940846646dd46a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 4 Mar 2008 00:25:06 -0800 Subject: am: --rebasing The new option --rebasing is used internally for rebase to tell am that it is being used for its purpose. This would leave .dotest/rebasing to help "completion" scripts tell if the ongoing operation is am or rebase. Also the option at the same time stands for --binary, -3 and -k which are always given when rebase drives am as its backend. Using the information "am" leaves, git-completion.bash tells ongoing rebase and am apart. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8f70e1efc1..5ae87998e6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -70,7 +70,15 @@ __git_ps1 () local b if [ -d "$g/../.dotest" ] then - r="|AM/REBASE" + if test -f "$g/../.dotest/rebasing" + then + r="|REBASE" + elif test -f "$g/../.dotest/applying" + then + r="|AM" + else + r="|AM/REBASE" + fi b="$(git symbolic-ref HEAD 2>/dev/null)" elif [ -f "$g/.dotest-merge/interactive" ] then -- cgit v1.2.3 From 9225d7be0abab0c3b1325a733aea127b45a18625 Mon Sep 17 00:00:00 2001 From: Denis Cheng Date: Sun, 2 Mar 2008 17:05:52 +0800 Subject: specify explicit "--pretty=medium" with `git log/show/whatchanged` The following patch will introduce a new configuration variable, "format.pretty", from then on the pretty format without specifying "--pretty" might not be the default "--pretty=medium", it depends on the user's config. So all kinds of Shell/Perl/Emacs scripts that needs the default medium pretty format must specify it explicitly. Signed-off-by: Denis Cheng Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- contrib/hooks/post-receive-email | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c9268234a5..4fa853fae7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1299,7 +1299,7 @@ Return the list of files that haven't been handled." (let (author-name author-email subject date msg) (with-temp-buffer (let ((coding-system (git-get-logoutput-coding-system))) - (git-call-process-env t nil "log" "-1" commit) + (git-call-process-env t nil "log" "-1" "--pretty=medium" commit) (goto-char (point-min)) (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) (setq author-name (match-string 1)) diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 77c88ebf1f..62a740c482 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -567,7 +567,7 @@ generate_general_email() echo "" if [ "$newrev_type" = "commit" ]; then echo $LOGBEGIN - git show --no-color --root -s $newrev + git show --no-color --root -s --pretty=medium $newrev echo $LOGEND else # What can we do here? The tag marks an object that is not -- cgit v1.2.3 From 3b376b0cb8b5d6b7b0d3f3acff6a31f350a7ab04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 4 Mar 2008 19:00:58 +0100 Subject: bash: add git-branch options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8f70e1efc1..8d6733abe4 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -506,7 +506,16 @@ _git_bisect () _git_branch () { - __gitcomp "$(__git_refs)" + case "${COMP_WORDS[COMP_CWORD]}" in + --*=*) COMPREPLY=() ;; + --*) + __gitcomp " + --color --no-color --verbose --abbrev= --no-abbrev + --track --no-track + " + ;; + *) __gitcomp "$(__git_refs)" ;; + esac } _git_bundle () -- cgit v1.2.3 From b9217642ef2db34e2cbeaef8d4439b07a03027cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 4 Mar 2008 19:00:59 +0100 Subject: bash: git-branch -d and -m lists only local branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit But still all branches are listed, if -r is present Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8d6733abe4..49e6df0965 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -506,6 +506,17 @@ _git_bisect () _git_branch () { + local i c=1 only_local_ref="n" has_r="n" + + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + -d|-m) only_local_ref="y" ;; + -r) has_r="y" ;; + esac + c=$((++c)) + done + case "${COMP_WORDS[COMP_CWORD]}" in --*=*) COMPREPLY=() ;; --*) @@ -514,7 +525,13 @@ _git_branch () --track --no-track " ;; - *) __gitcomp "$(__git_refs)" ;; + *) + if [ $only_local_ref = "y" -a $has_r = "n" ]; then + __gitcomp "$(__git_heads)" + else + __gitcomp "$(__git_refs)" + fi + ;; esac } -- cgit v1.2.3 From a5c4f85b1685aeb01f4dac88075f465c71a6c82d Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sat, 8 Mar 2008 22:10:48 -0500 Subject: bash: Properly quote the GIT_DIR at all times to fix subdirectory paths with spaces Signed-off-by: Kevin Ballard Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 848c067b57..c29569c624 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -83,17 +83,17 @@ __git_ps1 () elif [ -f "$g/.dotest-merge/interactive" ] then r="|REBASE-i" - b="$(cat $g/.dotest-merge/head-name)" + b="$(cat "$g/.dotest-merge/head-name")" elif [ -d "$g/.dotest-merge" ] then r="|REBASE-m" - b="$(cat $g/.dotest-merge/head-name)" + b="$(cat "$g/.dotest-merge/head-name")" elif [ -f "$g/MERGE_HEAD" ] then r="|MERGING" b="$(git symbolic-ref HEAD 2>/dev/null)" else - if [ -f $g/BISECT_LOG ] + if [ -f "$g/BISECT_LOG" ] then r="|BISECTING" fi @@ -101,7 +101,7 @@ __git_ps1 () then if ! b="$(git describe --exact-match HEAD 2>/dev/null)" then - b="$(cut -c1-7 $g/HEAD)..." + b="$(cut -c1-7 "$g/HEAD")..." fi fi fi -- cgit v1.2.3 From 1d17b22ebf9d894cef393e3e062f383aad8817e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 10 Mar 2008 16:02:22 +0100 Subject: bash: remove unnecessary conditions when checking for subcommands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checking emptyness of $command is sufficient. Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c29569c624..a94dc88fdc 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -497,7 +497,7 @@ _git_bisect () c=$((++c)) done - if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + if [ -z "$command" ]; then __gitcomp "start bad good reset visualize replay log" return fi @@ -1042,7 +1042,7 @@ _git_remote () c=$((++c)) done - if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + if [ -z "$command" ]; then __gitcomp "add rm show prune update" return fi @@ -1135,7 +1135,7 @@ _git_submodule () c=$((++c)) done - if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + if [ -z "$command" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -1198,7 +1198,7 @@ _git () c=$((++c)) done - if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + if [ -z "$command" ]; then case "${COMP_WORDS[COMP_CWORD]}" in --*=*) COMPREPLY=() ;; --*) __gitcomp " -- cgit v1.2.3 From 3ff1320d4be164e35a685d8d00b6f44084be76e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 10 Mar 2008 16:02:23 +0100 Subject: bash: refactor searching for subcommands on the command line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the __git_find_subcommand function, which takes one argument: a string containing all subcommands separated by spaces. The function searches through the command line whether a subcommand is already present. The first found subcommand will be printed to standard output. This enables us to remove code duplications from completion functions for commands having subcommands. Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 71 +++++++++++++++------------------- 1 file changed, 32 insertions(+), 39 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a94dc88fdc..438e193bd0 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -428,6 +428,22 @@ __git_aliased_command () done } +__git_find_subcommand () +{ + local word subcommand c=1 + + while [ $c -lt $COMP_CWORD ]; do + word="${COMP_WORDS[c]}" + for subcommand in $1; do + if [ "$subcommand" = "$word" ]; then + echo "$subcommand" + return + fi + done + c=$((++c)) + done +} + __git_whitespacelist="nowarn warn error error-all strip" _git_am () @@ -485,24 +501,14 @@ _git_add () _git_bisect () { - local i c=1 command - while [ $c -lt $COMP_CWORD ]; do - i="${COMP_WORDS[c]}" - case "$i" in - start|bad|good|reset|visualize|replay|log) - command="$i" - break - ;; - esac - c=$((++c)) - done - - if [ -z "$command" ]; then - __gitcomp "start bad good reset visualize replay log" + local subcommands="start bad good reset visualize replay log" + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" return fi - case "$command" in + case "$subcommand" in bad|good|reset) __gitcomp "$(__git_refs)" ;; @@ -1033,21 +1039,13 @@ _git_config () _git_remote () { - local i c=1 command - while [ $c -lt $COMP_CWORD ]; do - i="${COMP_WORDS[c]}" - case "$i" in - add|rm|show|prune|update) command="$i"; break ;; - esac - c=$((++c)) - done - - if [ -z "$command" ]; then - __gitcomp "add rm show prune update" + local subcommands="add rm show prune update" + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then return fi - case "$command" in + case "$subcommand" in rm|show|prune) __gitcomp "$(__git_remotes)" ;; @@ -1121,28 +1119,23 @@ _git_show () _git_stash () { - __gitcomp 'list show apply clear' + local subcommands='list show apply clear' + if [ -z "$(__git_find_subcommand "$subcommands")" ]; then + __gitcomp "$subcommands" + fi } _git_submodule () { - local i c=1 command - while [ $c -lt $COMP_CWORD ]; do - i="${COMP_WORDS[c]}" - case "$i" in - add|status|init|update) command="$i"; break ;; - esac - c=$((++c)) - done - - if [ -z "$command" ]; then + local subcommands="add status init update" + if [ -z "$(__git_find_subcommand "$subcommands")" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) __gitcomp "--quiet --cached" ;; *) - __gitcomp "add status init update" + __gitcomp "$subcommands" ;; esac return -- cgit v1.2.3 From 88b302f5e2e0a6f6610912df49c473967fe59c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 10 Mar 2008 16:02:24 +0100 Subject: bash: add new 'git stash' subcommands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Namely 'save', 'drop', 'pop' and 'create' Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 438e193bd0..c62b877aeb 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1119,7 +1119,7 @@ _git_show () _git_stash () { - local subcommands='list show apply clear' + local subcommands='save list show apply clear drop pop create' if [ -z "$(__git_find_subcommand "$subcommands")" ]; then __gitcomp "$subcommands" fi -- cgit v1.2.3 From 47f6ee283891b757069c82922a88d1393be25dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 10 Mar 2008 16:02:25 +0100 Subject: bash: add 'git svn' subcommands and options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 81 +++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c62b877aeb..2c4bf4e1d2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -384,7 +384,6 @@ __git_commands () show-index) : plumbing;; ssh-*) : transport;; stripspace) : plumbing;; - svn) : import export;; symbolic-ref) : plumbing;; tar-tree) : deprecated;; unpack-file) : plumbing;; @@ -1142,6 +1141,84 @@ _git_submodule () fi } +_git_svn () +{ + local subcommands=" + init fetch clone rebase dcommit log find-rev + set-tree commit-diff info create-ignore propget + proplist show-ignore show-externals + " + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" + else + local remote_opts="--username= --config-dir= --no-auth-cache" + local fc_opts=" + --follow-parent --authors-file= --repack= + --no-metadata --use-svm-props --use-svnsync-props + --log-window-size= --no-checkout --quiet + --repack-flags --user-log-author $remote_opts + " + local init_opts=" + --template= --shared= --trunk= --tags= + --branches= --stdlayout --minimize-url + --no-metadata --use-svm-props --use-svnsync-props + --rewrite-root= $remote_opts + " + local cmt_opts=" + --edit --rmdir --find-copies-harder --copy-similarity= + " + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$subcommand,$cur" in + fetch,--*) + __gitcomp "--revision= --fetch-all $fc_opts" + ;; + clone,--*) + __gitcomp "--revision= $fc_opts $init_opts" + ;; + init,--*) + __gitcomp "$init_opts" + ;; + dcommit,--*) + __gitcomp " + --merge --strategy= --verbose --dry-run + --fetch-all --no-rebase $cmt_opts $fc_opts + " + ;; + set-tree,--*) + __gitcomp "--stdin $cmt_opts $fc_opts" + ;; + create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\ + show-externals,--*) + __gitcomp "--revision=" + ;; + log,--*) + __gitcomp " + --limit= --revision= --verbose --incremental + --oneline --show-commit --non-recursive + --authors-file= + " + ;; + rebase,--*) + __gitcomp " + --merge --verbose --strategy= --local + --fetch-all $fc_opts + " + ;; + commit-diff,--*) + __gitcomp "--message= --file= --revision= $cmt_opts" + ;; + info,--*) + __gitcomp "--url" + ;; + *) + COMPREPLY=() + ;; + esac + fi +} + _git_tag () { local i c=1 f=0 @@ -1243,6 +1320,7 @@ _git () show-branch) _git_log ;; stash) _git_stash ;; submodule) _git_submodule ;; + svn) _git_svn ;; tag) _git_tag ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; @@ -1293,6 +1371,7 @@ complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_stash git-stash complete -o default -o nospace -F _git_submodule git-submodule +complete -o default -o nospace -F _git_svn git-svn complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_tag git-tag complete -o default -o nospace -F _git_log git-whatchanged -- cgit v1.2.3 From 6753f2aa55280a0fef1cbdcee71c1b529cb0c910 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 10 Mar 2008 19:49:19 -0400 Subject: bash: Remove completion of core.legacyheaders option This option is no longer recognized by git. Completing it is not worthwhile. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2c4bf4e1d2..73ed095487 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -961,7 +961,6 @@ _git_config () core.sharedRepository core.warnAmbiguousRefs core.compression - core.legacyHeaders core.packedGitWindowSize core.packedGitLimit clean.requireForce -- cgit v1.2.3 From 51fe120903370ca8bf384c839c8cfb70ee563eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 6 Mar 2008 22:37:36 +0100 Subject: bash: use __gitdir when completing 'git rebase' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When doing completion of rebase options in a subdirectory of the work tree during an ongoing rebase, wrong options were offered because of the hardcoded .git/.dotest-merge path. Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 73ed095487..fc108e4828 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -841,8 +841,8 @@ _git_push () _git_rebase () { - local cur="${COMP_WORDS[COMP_CWORD]}" - if [ -d .dotest ] || [ -d .git/.dotest-merge ]; then + local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" + if [ -d .dotest ] || [ -d "$dir"/.dotest-merge ]; then __gitcomp "--continue --skip --abort" return fi -- cgit v1.2.3 From ce5a2c956f10f84d8403fc4e94106a6b33a5024a Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Thu, 6 Mar 2008 18:52:37 +0200 Subject: bash: Add more long options to be completed with "git --" Add the following long options to be completed with command "git": --paginate --work-tree= --help Signed-off-by: Teemu Likonen Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index fc108e4828..2d11d0a97f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1271,11 +1271,14 @@ _git () case "${COMP_WORDS[COMP_CWORD]}" in --*=*) COMPREPLY=() ;; --*) __gitcomp " + --paginate --no-pager --git-dir= --bare --version --exec-path + --work-tree= + --help " ;; *) __gitcomp "$(__git_commands) $(__git_aliases)" ;; -- cgit v1.2.3 From 5447aac755e5cfb948c627762b4665801811b026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Wed, 5 Mar 2008 20:07:49 +0100 Subject: bash: fix long option with argument double completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pressing TAB right after 'git command --long-option=' results in 'git command --long-option=--long-option=' when the long option requires an argument, but we don't provide completion for its arguments (e.g. commit --author=, apply --exclude=). This patch detects these long options and provides empty completion array for them. Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2d11d0a97f..5046f69934 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -121,13 +121,21 @@ __gitcomp () if [ $# -gt 2 ]; then cur="$3" fi - for c in $1; do - case "$c$4" in - --*=*) all="$all$c$4$s" ;; - *.) all="$all$c$4$s" ;; - *) all="$all$c$4 $s" ;; - esac - done + case "$cur" in + --*=) + COMPREPLY=() + return + ;; + *) + for c in $1; do + case "$c$4" in + --*=*) all="$all$c$4$s" ;; + *.) all="$all$c$4$s" ;; + *) all="$all$c$4 $s" ;; + esac + done + ;; + esac IFS=$s COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur")) return -- cgit v1.2.3 From 8ff45f2af5b25b7581072ee7896f4285dfc034ea Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Mon, 3 Mar 2008 13:42:47 +0100 Subject: git-p4: Optimize the fetching of data from perforce. Use shallow copies in loop, and join content at the end. Then do the substitution, if needed. Signed-off-by: Marius Storm-Olsen Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 650ea34176..539c5cda07 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -882,21 +882,21 @@ class P4Sync(Command): while j < len(filedata): stat = filedata[j] j += 1 - text = '' + text = []; while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'): - tmp = filedata[j]['data'] - if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): - tmp = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', tmp) - elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): - tmp = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', tmp) - text += tmp + text.append(filedata[j]['data']) j += 1 - + text = ''.join(text) if not stat.has_key('depotFile'): sys.stderr.write("p4 print fails with: %s\n" % repr(stat)) continue + if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): + text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text) + elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): + text = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text) + contents[stat['depotFile']] = text for f in filesForCommit: -- cgit v1.2.3 From 67abd417165d1e7716d947949f5e5e27318c8a29 Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Wed, 12 Mar 2008 19:03:23 -0500 Subject: git-p4: Unset P4DIFF environment variable when using 'p4 -du diff' A custom diffing utility can be specified for the 'p4 diff' command by setting the P4DIFF environment variable. However when using a custom diffing utility such as 'vimdiff' passing options like -du can cause unexpected behavior. Since the goal is to generate a unified diff of the changes and attach them to the bottom of the p4 submit log we should unset P4DIFF if it has been set in order to generate the diff properly. Signed-off-by: Shawn Bohrer Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 539c5cda07..28b9c3c3cb 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -627,6 +627,8 @@ class P4Submit(Command): if self.interactive: submitTemplate = self.prepareLogMessage(template, logMessage) + if os.environ.has_key("P4DIFF"): + del(os.environ["P4DIFF"]) diff = read_pipe("p4 diff -du ...") for newFile in filesToAdd: -- cgit v1.2.3 From 82cea9ffb1c4677155e3e2996d76542502611370 Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Wed, 12 Mar 2008 19:03:24 -0500 Subject: git-p4: Use P4EDITOR environment variable when set Perforce allows you to set the P4EDITOR environment variable to your preferred editor for use in perforce. Since we are displaying a perforce changelog to the user we should use it when it is defined. Signed-off-by: Shawn Bohrer Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 28b9c3c3cb..3cb0330ec2 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -652,7 +652,10 @@ class P4Submit(Command): defaultEditor = "vi" if platform.system() == "Windows": defaultEditor = "notepad" - editor = os.environ.get("EDITOR", defaultEditor); + if os.environ.has_key("P4EDITOR"): + editor = os.environ.get("P4EDITOR") + else: + editor = os.environ.get("EDITOR", defaultEditor); system(editor + " " + fileName) tmpFile = open(fileName, "rb") message = tmpFile.read() -- cgit v1.2.3 From ac378633f3cae46d2c88255e53ee38a6206d3777 Mon Sep 17 00:00:00 2001 From: Bernt Hansen Date: Fri, 14 Mar 2008 22:44:13 -0400 Subject: git-new-workdir: Share SVN meta data between work dirs and the repository Multiple work dirs with git svn caused each work dir to have its own stale copy of the SVN meta data in .git/svn git svn rebase updates commits with git-svn-id: in the repository and stores the SVN meta data information only in that work dir. Attempting to git svn rebase in other work dirs for the same branch would fail because the last revision fetched according to the git-svn-id is greater than the revision in the SVN meta data for that work directory. Signed-off-by: Bernt Hansen Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 2838546d16..7959eab902 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -63,7 +63,7 @@ mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!" # create the links to the original repo. explictly exclude index, HEAD and # logs/HEAD from the list since they are purely related to the current working # directory, and should not be shared. -for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache +for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn do case $x in */*) -- cgit v1.2.3 From c817faabd7b0d7c0779ad5b34a4411ba3cdccf2d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 15 Mar 2008 23:58:10 -0700 Subject: Resurrect git-rerere to contrib/examples It is handy to have a copy readily available for checking regressions. Signed-off-by: Junio C Hamano --- contrib/examples/git-rerere.perl | 284 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100755 contrib/examples/git-rerere.perl (limited to 'contrib') diff --git a/contrib/examples/git-rerere.perl b/contrib/examples/git-rerere.perl new file mode 100755 index 0000000000..4f692091e7 --- /dev/null +++ b/contrib/examples/git-rerere.perl @@ -0,0 +1,284 @@ +#!/usr/bin/perl +# +# REuse REcorded REsolve. This tool records a conflicted automerge +# result and its hand resolution, and helps to resolve future +# automerge that results in the same conflict. +# +# To enable this feature, create a directory 'rr-cache' under your +# .git/ directory. + +use Digest; +use File::Path; +use File::Copy; + +my $git_dir = $::ENV{GIT_DIR} || ".git"; +my $rr_dir = "$git_dir/rr-cache"; +my $merge_rr = "$git_dir/rr-cache/MERGE_RR"; + +my %merge_rr = (); + +sub read_rr { + if (!-f $merge_rr) { + %merge_rr = (); + return; + } + my $in; + local $/ = "\0"; + open $in, "<$merge_rr" or die "$!: $merge_rr"; + while (<$in>) { + chomp; + my ($name, $path) = /^([0-9a-f]{40})\t(.*)$/s; + $merge_rr{$path} = $name; + } + close $in; +} + +sub write_rr { + my $out; + open $out, ">$merge_rr" or die "$!: $merge_rr"; + for my $path (sort keys %merge_rr) { + my $name = $merge_rr{$path}; + print $out "$name\t$path\0"; + } + close $out; +} + +sub compute_conflict_name { + my ($path) = @_; + my @side = (); + my $in; + open $in, "<$path" or die "$!: $path"; + + my $sha1 = Digest->new("SHA-1"); + my $hunk = 0; + while (<$in>) { + if (/^<<<<<<< .*/) { + $hunk++; + @side = ([], undef); + } + elsif (/^=======$/) { + $side[1] = []; + } + elsif (/^>>>>>>> .*/) { + my ($one, $two); + $one = join('', @{$side[0]}); + $two = join('', @{$side[1]}); + if ($two le $one) { + ($one, $two) = ($two, $one); + } + $sha1->add($one); + $sha1->add("\0"); + $sha1->add($two); + $sha1->add("\0"); + @side = (); + } + elsif (@side == 0) { + next; + } + elsif (defined $side[1]) { + push @{$side[1]}, $_; + } + else { + push @{$side[0]}, $_; + } + } + close $in; + return ($sha1->hexdigest, $hunk); +} + +sub record_preimage { + my ($path, $name) = @_; + my @side = (); + my ($in, $out); + open $in, "<$path" or die "$!: $path"; + open $out, ">$name" or die "$!: $name"; + + while (<$in>) { + if (/^<<<<<<< .*/) { + @side = ([], undef); + } + elsif (/^=======$/) { + $side[1] = []; + } + elsif (/^>>>>>>> .*/) { + my ($one, $two); + $one = join('', @{$side[0]}); + $two = join('', @{$side[1]}); + if ($two le $one) { + ($one, $two) = ($two, $one); + } + print $out "<<<<<<<\n"; + print $out $one; + print $out "=======\n"; + print $out $two; + print $out ">>>>>>>\n"; + @side = (); + } + elsif (@side == 0) { + print $out $_; + } + elsif (defined $side[1]) { + push @{$side[1]}, $_; + } + else { + push @{$side[0]}, $_; + } + } + close $out; + close $in; +} + +sub find_conflict { + my $in; + local $/ = "\0"; + my $pid = open($in, '-|'); + die "$!" unless defined $pid; + if (!$pid) { + exec(qw(git ls-files -z -u)) or die "$!: ls-files"; + } + my %path = (); + my @path = (); + while (<$in>) { + chomp; + my ($mode, $sha1, $stage, $path) = + /^([0-7]+) ([0-9a-f]{40}) ([123])\t(.*)$/s; + $path{$path} |= (1 << $stage); + } + close $in; + while (my ($path, $status) = each %path) { + if ($status == 14) { push @path, $path; } + } + return @path; +} + +sub merge { + my ($name, $path) = @_; + record_preimage($path, "$rr_dir/$name/thisimage"); + unless (system('git', 'merge-file', map { "$rr_dir/$name/${_}image" } + qw(this pre post))) { + my $in; + open $in, "<$rr_dir/$name/thisimage" or + die "$!: $name/thisimage"; + my $out; + open $out, ">$path" or die "$!: $path"; + while (<$in>) { print $out $_; } + close $in; + close $out; + return 1; + } + return 0; +} + +sub garbage_collect_rerere { + # We should allow specifying these from the command line and + # that is why the caller gives @ARGV to us, but I am lazy. + + my $cutoff_noresolve = 15; # two weeks + my $cutoff_resolve = 60; # two months + my @to_remove; + while (<$rr_dir/*/preimage>) { + my ($dir) = /^(.*)\/preimage$/; + my $cutoff = ((-f "$dir/postimage") + ? $cutoff_resolve + : $cutoff_noresolve); + my $age = -M "$_"; + if ($cutoff <= $age) { + push @to_remove, $dir; + } + } + if (@to_remove) { + rmtree(\@to_remove); + } +} + +-d "$rr_dir" || exit(0); + +read_rr(); + +if (@ARGV) { + my $arg = shift @ARGV; + if ($arg eq 'clear') { + for my $path (keys %merge_rr) { + my $name = $merge_rr{$path}; + if (-d "$rr_dir/$name" && + ! -f "$rr_dir/$name/postimage") { + rmtree(["$rr_dir/$name"]); + } + } + unlink $merge_rr; + } + elsif ($arg eq 'status') { + for my $path (keys %merge_rr) { + print $path, "\n"; + } + } + elsif ($arg eq 'diff') { + for my $path (keys %merge_rr) { + my $name = $merge_rr{$path}; + system('diff', ((@ARGV == 0) ? ('-u') : @ARGV), + '-L', "a/$path", '-L', "b/$path", + "$rr_dir/$name/preimage", $path); + } + } + elsif ($arg eq 'gc') { + garbage_collect_rerere(@ARGV); + } + else { + die "$0 unknown command: $arg\n"; + } + exit 0; +} + +my %conflict = map { $_ => 1 } find_conflict(); + +# MERGE_RR records paths with conflicts immediately after merge +# failed. Some of the conflicted paths might have been hand resolved +# in the working tree since then, but the initial run would catch all +# and register their preimages. + +for my $path (keys %conflict) { + # This path has conflict. If it is not recorded yet, + # record the pre-image. + if (!exists $merge_rr{$path}) { + my ($name, $hunk) = compute_conflict_name($path); + next unless ($hunk); + $merge_rr{$path} = $name; + if (! -d "$rr_dir/$name") { + mkpath("$rr_dir/$name", 0, 0777); + print STDERR "Recorded preimage for '$path'\n"; + record_preimage($path, "$rr_dir/$name/preimage"); + } + } +} + +# Now some of the paths that had conflicts earlier might have been +# hand resolved. Others may be similar to a conflict already that +# was resolved before. + +for my $path (keys %merge_rr) { + my $name = $merge_rr{$path}; + + # We could resolve this automatically if we have images. + if (-f "$rr_dir/$name/preimage" && + -f "$rr_dir/$name/postimage") { + if (merge($name, $path)) { + print STDERR "Resolved '$path' using previous resolution.\n"; + # Then we do not have to worry about this path + # anymore. + delete $merge_rr{$path}; + next; + } + } + + # Let's see if we have resolved it. + (undef, my $hunk) = compute_conflict_name($path); + next if ($hunk); + + print STDERR "Recorded resolution for '$path'.\n"; + copy($path, "$rr_dir/$name/postimage"); + # And we do not have to worry about this path anymore. + delete $merge_rr{$path}; +} + +# Write out the rest. +write_rr(); -- cgit v1.2.3 From f3e5ae4f06ae968b710d286280a46b38ae3d36e8 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Fri, 28 Mar 2008 15:40:40 +0100 Subject: git-p4: Handle Windows EOLs properly after removal of p4 submit template handling. git-p4s handling of Windows style EOL was broken after the removal of the p4 submit template handling in commit f2a6059. Fix that, and make getP4OpenedType() more robust. Signed-off-by: Marius Storm-Olsen Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3cb0330ec2..d8de9f6c25 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -90,11 +90,11 @@ def getP4OpenedType(file): # Returns the perforce file type for the given file. result = read_pipe("p4 opened %s" % file) - match = re.match(".*\((.+)\)$", result) + match = re.match(".*\((.+)\)\r?$", result) if match: return match.group(1) else: - die("Could not determine file type for %s" % file) + die("Could not determine file type for %s (result: '%s')" % (file, result)) def diffTreePattern(): # This is a simple generator for the diff tree regex pattern. This could be @@ -513,6 +513,8 @@ class P4Submit(Command): template = "" inFilesSection = False for line in read_pipe_lines("p4 change -o"): + if line.endswith("\r\n"): + line = line[:-2] + "\n" if inFilesSection: if line.startswith("\t"): # path starts and ends with a tab @@ -619,8 +621,6 @@ class P4Submit(Command): setP4ExecBit(f, mode) logMessage = extractLogMessageFromGitCommit(id) - if self.isWindows: - logMessage = logMessage.replace("\n", "\r\n") logMessage = logMessage.strip() template = self.prepareSubmitTemplate() @@ -631,23 +631,25 @@ class P4Submit(Command): del(os.environ["P4DIFF"]) diff = read_pipe("p4 diff -du ...") + newdiff = "" for newFile in filesToAdd: - diff += "==== new file ====\n" - diff += "--- /dev/null\n" - diff += "+++ %s\n" % newFile + newdiff += "==== new file ====\n" + newdiff += "--- /dev/null\n" + newdiff += "+++ %s\n" % newFile f = open(newFile, "r") for line in f.readlines(): - diff += "+" + line + newdiff += "+" + line f.close() - separatorLine = "######## everything below this line is just the diff #######" - if platform.system() == "Windows": - separatorLine += "\r" - separatorLine += "\n" + separatorLine = "######## everything below this line is just the diff #######\n" [handle, fileName] = tempfile.mkstemp() tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate + separatorLine + diff) + if self.isWindows: + submitTemplate = submitTemplate.replace("\n", "\r\n") + separatorLine = separatorLine.replace("\n", "\r\n") + newdiff = newdiff.replace("\n", "\r\n") + tmpFile.write(submitTemplate + separatorLine + diff + newdiff) tmpFile.close() defaultEditor = "vi" if platform.system() == "Windows": -- cgit v1.2.3 From 6aeeffd14447348f3ab4a7d34927d6aff8c0a75c Mon Sep 17 00:00:00 2001 From: Josh Elsasser Date: Thu, 27 Mar 2008 14:02:14 -0700 Subject: Allow git-cvsserver database table name prefix to be specified. Adds a gitcvs.dbtablenameprefix config variable, the contents of which are prepended to any database tables names used by git-cvsserver. The same substutions as gitcvs.dbname and gitcvs.dbuser are supported, and any non-alphabetic characters are replaced with underscores. A typo found in contrib/completion/git-completion.bash is also fixed. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 5046f69934..791e30f6d7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -999,7 +999,8 @@ _git_config () gitcvs.enabled gitcvs.logfile gitcvs.allbinary - gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dvpass + gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dbpass + gitcvs.dbtablenameprefix gc.packrefs gc.reflogexpire gc.reflogexpireunreachable -- cgit v1.2.3 From d9e3b7025fdfeb80bb72a10b5d272acfbb5df72a Mon Sep 17 00:00:00 2001 From: Pascal Obry Date: Sun, 6 Apr 2008 19:32:24 +0200 Subject: Add interactive option in rebase command completion list. Signed-off-by: Pascal Obry Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 791e30f6d7..96a712b5ea 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -865,7 +865,7 @@ _git_rebase () return ;; --*) - __gitcomp "--onto --merge --strategy" + __gitcomp "--onto --merge --strategy --interactive" return esac __gitcomp "$(__git_refs)" -- cgit v1.2.3 From aba201c6e8d9934157b9ba39b8e6b54c2fa7b6e1 Mon Sep 17 00:00:00 2001 From: Pascal Obry Date: Sun, 6 Apr 2008 18:56:08 +0200 Subject: Add prefix oriented completions for diff and format-patch commands. Signed-off-by: Pascal Obry Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 96a712b5ea..4d81963b1d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -639,7 +639,9 @@ _git_diff () --find-copies-harder --pickaxe-all --pickaxe-regex --text --ignore-space-at-eol --ignore-space-change --ignore-all-space --exit-code --quiet --ext-diff - --no-ext-diff" + --no-ext-diff + --no-prefix --src-prefix= --dst-prefix= + " return ;; esac @@ -696,6 +698,7 @@ _git_format_patch () --full-index --binary --not --all --cover-letter + --no-prefix --src-prefix= --dst-prefix= " return ;; -- cgit v1.2.3 From f949844263e866774063511c4538b96068dd8d1b Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 2 Apr 2008 21:35:11 +0200 Subject: contrib/hooks: add an example pre-auto-gc hook It disables git-gc --auto when you are running Linux and you are not on AC. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hooks/pre-auto-gc-battery | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 contrib/hooks/pre-auto-gc-battery (limited to 'contrib') diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery new file mode 100644 index 0000000000..0096f57b7e --- /dev/null +++ b/contrib/hooks/pre-auto-gc-battery @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to verify if you are on battery, in case you +# are running Linux. Called by git-gc --auto with no arguments. The hook +# should exit with non-zero status after issuing an appropriate message +# if it wants to stop the auto repacking. +# +# This hook is stored in the contrib/hooks directory. Your distribution +# may have put this somewhere else. If you want to use this hook, you +# should make this script executable then link to it in the repository +# you would like to use it in. +# +# For example, if the hook is stored in +# /usr/share/git-core/contrib/hooks/pre-auto-gc-battery: +# +# chmod a+x pre-auto-gc-battery +# cd /path/to/your/repository.git +# ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \ +# hooks/pre-auto-gc + +if test -x /sbin/on_ac_power && /sbin/on_ac_power +then + exit 0 +elif test "$(cat /sys/class/power_supply/AC/online 2>/dev/null)" = 1 +then + exit 0 +elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null +then + exit 0 +elif grep -q '0x01$' /proc/apm 2>/dev/null +then + exit 0 +fi + +echo "Auto packing deferred; not on AC" +exit 1 -- cgit v1.2.3 From d8abe148bece83f6c3e5ebe6075aef236322ed74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sun, 6 Apr 2008 03:23:43 +0200 Subject: merge, pull: introduce '--(no-)stat' option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This option has the same effect as '--(no-)summary' (i.e. whether to show a diffsat at the end of the merge or not), and it is consistent with the '--stat' option of other git commands. Documentation, tests, and bash completion are updaed accordingly, and the old --summary option is marked as being deprected. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 4d81963b1d..0f54cfd8a3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -779,7 +779,7 @@ _git_merge () ;; --*) __gitcomp " - --no-commit --no-summary --squash --strategy + --no-commit --no-stat --squash --strategy " return esac -- cgit v1.2.3 From efb779f8873e5aa36be29a4e551186c62c1b580c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sun, 6 Apr 2008 03:23:46 +0200 Subject: merge, pull: add '--(no-)log' command line option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are the command line option equivalents of the 'merge.log' config variable. The patch also updates documentation and bash completion accordingly, and adds a test. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0f54cfd8a3..8091d2deb7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -779,7 +779,7 @@ _git_merge () ;; --*) __gitcomp " - --no-commit --no-stat --squash --strategy + --no-commit --no-stat --log --no-log --squash --strategy " return esac -- cgit v1.2.3 From f45741390844778f5b286cf6607cd767636e5c57 Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Sun, 20 Apr 2008 22:32:47 +0300 Subject: bash: Add completion for git diff --base --ours --theirs Signed-off-by: Teemu Likonen Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 4d81963b1d..6949cac45d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -641,6 +641,7 @@ _git_diff () --ignore-all-space --exit-code --quiet --ext-diff --no-ext-diff --no-prefix --src-prefix= --dst-prefix= + --base --ours --theirs " return ;; -- cgit v1.2.3 From dbe48256b41c1e94d81f2458d7e84b1fdcb47026 Mon Sep 17 00:00:00 2001 From: Clifford Caoile Date: Fri, 18 Apr 2008 22:07:12 +0900 Subject: git.el: Set process-environment instead of invoking env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the similar patch from David Kågedal [1], "this will make it a little less posix-dependent and more efficient." However, there are two other areas that need to replaced, namely git-run-command-region and git-run-hooks. This patch implements the changes of [1] onto those Emacs Lisp functions. If unpatched, using the git port "msysgit" on Windows will require defadvice changes as shown at [2] (also explained at 4msysgit.git [3]). I have tested git-run-command-region on msysgit, because this is always called by git-commit (via git-commit-tree <- git-do-commit <- git-commit-file). However, I could not test git-run-hooks because it currently does not work on the Emacs Windows port. The latter reports the hooks files as a+rw and a-x, despite msysgit and cygwin chmod setting on the respective files. References: [1] f27e55864317611385be4d33b3c53ca787379df9 [2] http://groups.google.com/group/msysgit/browse_thread/thread/b852fef689817707 [3] http://repo.or.cz/w/git/mingw/4msysgit.git?a=commit;h=3c30e5e87358eba7b6d7dcd6301ae8438f0c30ea Signed-off-by: Clifford Caoile Acked-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 4fa853fae7..2557a7667f 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -232,10 +232,8 @@ and returns the process output as a string, or nil if the git failed." (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (unless (eq 0 (if env - (git-run-process-region - buffer start end "env" - (append (git-get-env-strings env) (list "git") args)) + (unless (eq 0 (let ((process-environment (append (git-get-env-strings env) + process-environment))) (git-run-process-region buffer start end "git" args))) (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) @@ -250,9 +248,8 @@ and returns the process output as a string, or nil if the git failed." (erase-buffer) (cd dir) (setq status - (if env - (apply #'call-process "env" nil (list buffer t) nil - (append (git-get-env-strings env) (list hook-name) args)) + (let ((process-environment (append (git-get-env-strings env) + process-environment))) (apply #'call-process hook-name nil (list buffer t) nil args)))) (display-message-or-buffer buffer) (eq 0 status))))) -- cgit v1.2.3 From 3903c6189d0d596a0fef47edab437aa047e812fa Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 20 Apr 2008 14:34:07 -0500 Subject: completion: allow 'git remote' subcommand completion After typing 'git remote ', the subcommand options were not shown. Fix it by adding the missing __gitcomp call. Signed-off-by: Dan McGee Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 4d81963b1d..fd654bdc9c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1052,6 +1052,7 @@ _git_remote () local subcommands="add rm show prune update" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" return fi -- cgit v1.2.3 From 799596a5d06f2abddef75940604d00c4bd8ba849 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 20 Apr 2008 12:28:44 -0500 Subject: completion: remove use of dashed git commands Signed-off-by: Dan McGee Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index fd654bdc9c..6012047ee5 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -152,7 +152,7 @@ __git_heads () done return fi - for i in $(git-ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "$1" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -173,7 +173,7 @@ __git_tags () done return fi - for i in $(git-ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "$1" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -200,7 +200,7 @@ __git_refs () done return fi - for i in $(git-ls-remote "$dir" 2>/dev/null); do + for i in $(git ls-remote "$dir" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -223,7 +223,7 @@ __git_refs2 () __git_refs_remotes () { local cmd i is_hash=y - for i in $(git-ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "$1" 2>/dev/null); do case "$is_hash,$i" in n,refs/heads/*) is_hash=y -- cgit v1.2.3 From 71bd81ade2973a304889d94426c922cc096019a2 Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Mon, 21 Apr 2008 14:44:44 +0100 Subject: post-receive-email: fix accidental removal of a trailing space in signature line post-receive-email adds a signature to the end of emails in generate_email_footer(). The signature was separated from the main email body using the standard string "-- ". (see RFC 3676) a6080a0 (War on whitespace, 2007-06-07) removed the trailing whitespace from "-- ", leaving it as "--", which is not a correct signature separator. This patch restores the missing space, but does it in a way that will not set off the trailing whitespace alarms. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 62a740c482..41368950d6 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -202,11 +202,12 @@ generate_email_header() generate_email_footer() { + SPACE=" " cat <<-EOF hooks/post-receive - -- + --${SPACE} $projectdesc EOF } -- cgit v1.2.3 From 07ba53f724b95a817f957b8e943c9e4f545a0949 Mon Sep 17 00:00:00 2001 From: Richard Quirk Date: Sun, 27 Apr 2008 17:35:10 +0200 Subject: bash: Add completion for gitk --merge Option is only completed when .git/MERGE_HEAD is present. Signed-off-by: Richard Quirk Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 665a895f5e..23db664f48 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1346,9 +1346,14 @@ _git () _gitk () { local cur="${COMP_WORDS[COMP_CWORD]}" + local g="$(git rev-parse --git-dir 2>/dev/null)" + local merge="" + if [ -f $g/MERGE_HEAD ]; then + merge="--merge" + fi case "$cur" in --*) - __gitcomp "--not --all" + __gitcomp "--not --all $merge" return ;; esac -- cgit v1.2.3 From 8434c2f1afedb936e0ea8c07ce25733013c2f743 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 27 Apr 2008 13:39:30 -0400 Subject: Build in clone Thanks to Johannes Schindelin for various comments and improvements, including supporting cloning full bundles. Signed-off-by: Junio C Hamano --- contrib/examples/git-clone.sh | 523 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 523 insertions(+) create mode 100755 contrib/examples/git-clone.sh (limited to 'contrib') diff --git a/contrib/examples/git-clone.sh b/contrib/examples/git-clone.sh new file mode 100755 index 0000000000..8c7fc7f631 --- /dev/null +++ b/contrib/examples/git-clone.sh @@ -0,0 +1,523 @@ +#!/bin/sh +# +# Copyright (c) 2005, Linus Torvalds +# Copyright (c) 2005, Junio C Hamano +# +# Clone a repository into a different directory that does not yet exist. + +# See git-sh-setup why. +unset CDPATH + +OPTIONS_SPEC="\ +git-clone [options] [--] [] +-- +n,no-checkout don't create a checkout +bare create a bare repository +naked create a bare repository +l,local to clone from a local repository +no-hardlinks don't use local hardlinks, always copy +s,shared setup as a shared repository +template= path to the template directory +q,quiet be quiet +reference= reference repository +o,origin= use instead of 'origin' to track upstream +u,upload-pack= path to git-upload-pack on the remote +depth= create a shallow clone of that depth + +use-separate-remote compatibility, do not use +no-separate-remote compatibility, do not use" + +die() { + echo >&2 "$@" + exit 1 +} + +usage() { + exec "$0" -h +} + +eval "$(echo "$OPTIONS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)" + +get_repo_base() { + ( + cd "`/bin/pwd`" && + cd "$1" || cd "$1.git" && + { + cd .git + pwd + } + ) 2>/dev/null +} + +if [ -n "$GIT_SSL_NO_VERIFY" -o \ + "`git config --bool http.sslVerify`" = false ]; then + curl_extra_args="-k" +fi + +http_fetch () { + # $1 = Remote, $2 = Local + curl -nsfL $curl_extra_args "$1" >"$2" + curl_exit_status=$? + case $curl_exit_status in + 126|127) exit ;; + *) return $curl_exit_status ;; + esac +} + +clone_dumb_http () { + # $1 - remote, $2 - local + cd "$2" && + clone_tmp="$GIT_DIR/clone-tmp" && + mkdir -p "$clone_tmp" || exit 1 + if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \ + "`git config --bool http.noEPSV`" = true ]; then + curl_extra_args="${curl_extra_args} --disable-epsv" + fi + http_fetch "$1/info/refs" "$clone_tmp/refs" || + die "Cannot get remote repository information. +Perhaps git-update-server-info needs to be run there?" + test "z$quiet" = z && v=-v || v= + while read sha1 refname + do + name=`expr "z$refname" : 'zrefs/\(.*\)'` && + case "$name" in + *^*) continue;; + esac + case "$bare,$name" in + yes,* | ,heads/* | ,tags/*) ;; + *) continue ;; + esac + if test -n "$use_separate_remote" && + branch_name=`expr "z$name" : 'zheads/\(.*\)'` + then + tname="remotes/$origin/$branch_name" + else + tname=$name + fi + git-http-fetch $v -a -w "$tname" "$sha1" "$1" || exit 1 + done <"$clone_tmp/refs" + rm -fr "$clone_tmp" + http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD" || + rm -f "$GIT_DIR/REMOTE_HEAD" + if test -f "$GIT_DIR/REMOTE_HEAD"; then + head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"` + case "$head_sha1" in + 'ref: refs/'*) + ;; + *) + git-http-fetch $v -a "$head_sha1" "$1" || + rm -f "$GIT_DIR/REMOTE_HEAD" + ;; + esac + fi +} + +quiet= +local=no +use_local_hardlink=yes +local_shared=no +unset template +no_checkout= +upload_pack= +bare= +reference= +origin= +origin_override= +use_separate_remote=t +depth= +no_progress= +local_explicitly_asked_for= +test -t 1 || no_progress=--no-progress + +while test $# != 0 +do + case "$1" in + -n|--no-checkout) + no_checkout=yes ;; + --naked|--bare) + bare=yes ;; + -l|--local) + local_explicitly_asked_for=yes + use_local_hardlink=yes + ;; + --no-hardlinks) + use_local_hardlink=no ;; + -s|--shared) + local_shared=yes ;; + --template) + shift; template="--template=$1" ;; + -q|--quiet) + quiet=-q ;; + --use-separate-remote|--no-separate-remote) + die "clones are always made with separate-remote layout" ;; + --reference) + shift; reference="$1" ;; + -o|--origin) + shift; + case "$1" in + '') + usage ;; + */*) + die "'$1' is not suitable for an origin name" + esac + git check-ref-format "heads/$1" || + die "'$1' is not suitable for a branch name" + test -z "$origin_override" || + die "Do not give more than one --origin options." + origin_override=yes + origin="$1" + ;; + -u|--upload-pack) + shift + upload_pack="--upload-pack=$1" ;; + --depth) + shift + depth="--depth=$1" ;; + --) + shift + break ;; + *) + usage ;; + esac + shift +done + +repo="$1" +test -n "$repo" || + die 'you must specify a repository to clone.' + +# --bare implies --no-checkout and --no-separate-remote +if test yes = "$bare" +then + if test yes = "$origin_override" + then + die '--bare and --origin $origin options are incompatible.' + fi + no_checkout=yes + use_separate_remote= +fi + +if test -z "$origin" +then + origin=origin +fi + +# Turn the source into an absolute path if +# it is local +if base=$(get_repo_base "$repo"); then + repo="$base" + if test -z "$depth" + then + local=yes + fi +elif test -f "$repo" +then + case "$repo" in /*) ;; *) repo="$PWD/$repo" ;; esac +fi + +# Decide the directory name of the new repository +if test -n "$2" +then + dir="$2" + test $# = 2 || die "excess parameter to git-clone" +else + # Derive one from the repository name + # Try using "humanish" part of source repo if user didn't specify one + if test -f "$repo" + then + # Cloning from a bundle + dir=$(echo "$repo" | sed -e 's|/*\.bundle$||' -e 's|.*/||g') + else + dir=$(echo "$repo" | + sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g') + fi +fi + +[ -e "$dir" ] && die "destination directory '$dir' already exists." +[ yes = "$bare" ] && unset GIT_WORK_TREE +[ -n "$GIT_WORK_TREE" ] && [ -e "$GIT_WORK_TREE" ] && +die "working tree '$GIT_WORK_TREE' already exists." +D= +W= +cleanup() { + err=$? + test -z "$D" && rm -rf "$dir" + test -z "$W" && test -n "$GIT_WORK_TREE" && rm -rf "$GIT_WORK_TREE" + cd .. + test -n "$D" && rm -rf "$D" + test -n "$W" && rm -rf "$W" + exit $err +} +trap cleanup 0 +mkdir -p "$dir" && D=$(cd "$dir" && pwd) || usage +test -n "$GIT_WORK_TREE" && mkdir -p "$GIT_WORK_TREE" && +W=$(cd "$GIT_WORK_TREE" && pwd) && GIT_WORK_TREE="$W" && export GIT_WORK_TREE +if test yes = "$bare" || test -n "$GIT_WORK_TREE"; then + GIT_DIR="$D" +else + GIT_DIR="$D/.git" +fi && +export GIT_DIR && +GIT_CONFIG="$GIT_DIR/config" git-init $quiet ${template+"$template"} || usage + +if test -n "$bare" +then + GIT_CONFIG="$GIT_DIR/config" git config core.bare true +fi + +if test -n "$reference" +then + ref_git= + if test -d "$reference" + then + if test -d "$reference/.git/objects" + then + ref_git="$reference/.git" + elif test -d "$reference/objects" + then + ref_git="$reference" + fi + fi + if test -n "$ref_git" + then + ref_git=$(cd "$ref_git" && pwd) + echo "$ref_git/objects" >"$GIT_DIR/objects/info/alternates" + ( + GIT_DIR="$ref_git" git for-each-ref \ + --format='%(objectname) %(*objectname)' + ) | + while read a b + do + test -z "$a" || + git update-ref "refs/reference-tmp/$a" "$a" + test -z "$b" || + git update-ref "refs/reference-tmp/$b" "$b" + done + else + die "reference repository '$reference' is not a local directory." + fi +fi + +rm -f "$GIT_DIR/CLONE_HEAD" + +# We do local magic only when the user tells us to. +case "$local" in +yes) + ( cd "$repo/objects" ) || + die "cannot chdir to local '$repo/objects'." + + if test "$local_shared" = yes + then + mkdir -p "$GIT_DIR/objects/info" + echo "$repo/objects" >>"$GIT_DIR/objects/info/alternates" + else + cpio_quiet_flag="" + cpio --help 2>&1 | grep -- --quiet >/dev/null && \ + cpio_quiet_flag=--quiet + l= && + if test "$use_local_hardlink" = yes + then + # See if we can hardlink and drop "l" if not. + sample_file=$(cd "$repo" && \ + find objects -type f -print | sed -e 1q) + # objects directory should not be empty because + # we are cloning! + test -f "$repo/$sample_file" || + die "fatal: cannot clone empty repository" + if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null + then + rm -f "$GIT_DIR/objects/sample" + l=l + elif test -n "$local_explicitly_asked_for" + then + echo >&2 "Warning: -l asked but cannot hardlink to $repo" + fi + fi && + cd "$repo" && + find objects -depth -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \ + exit 1 + fi + git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1 + ;; +*) + case "$repo" in + rsync://*) + case "$depth" in + "") ;; + *) die "shallow over rsync not supported" ;; + esac + rsync $quiet -av --ignore-existing \ + --exclude info "$repo/objects/" "$GIT_DIR/objects/" || + exit + # Look at objects/info/alternates for rsync -- http will + # support it natively and git native ones will do it on the + # remote end. Not having that file is not a crime. + rsync -q "$repo/objects/info/alternates" \ + "$GIT_DIR/TMP_ALT" 2>/dev/null || + rm -f "$GIT_DIR/TMP_ALT" + if test -f "$GIT_DIR/TMP_ALT" + then + ( cd "$D" && + . git-parse-remote && + resolve_alternates "$repo" <"$GIT_DIR/TMP_ALT" ) | + while read alt + do + case "$alt" in 'bad alternate: '*) die "$alt";; esac + case "$quiet" in + '') echo >&2 "Getting alternate: $alt" ;; + esac + rsync $quiet -av --ignore-existing \ + --exclude info "$alt" "$GIT_DIR/objects" || exit + done + rm -f "$GIT_DIR/TMP_ALT" + fi + git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1 + ;; + https://*|http://*|ftp://*) + case "$depth" in + "") ;; + *) die "shallow over http or ftp not supported" ;; + esac + if test -z "@@NO_CURL@@" + then + clone_dumb_http "$repo" "$D" + else + die "http transport not supported, rebuild Git with curl support" + fi + ;; + *) + if [ -f "$repo" ] ; then + git bundle unbundle "$repo" > "$GIT_DIR/CLONE_HEAD" || + die "unbundle from '$repo' failed." + else + case "$upload_pack" in + '') git-fetch-pack --all -k $quiet $depth $no_progress "$repo";; + *) git-fetch-pack --all -k \ + $quiet "$upload_pack" $depth $no_progress "$repo" ;; + esac >"$GIT_DIR/CLONE_HEAD" || + die "fetch-pack from '$repo' failed." + fi + ;; + esac + ;; +esac +test -d "$GIT_DIR/refs/reference-tmp" && rm -fr "$GIT_DIR/refs/reference-tmp" + +if test -f "$GIT_DIR/CLONE_HEAD" +then + # Read git-fetch-pack -k output and store the remote branches. + if [ -n "$use_separate_remote" ] + then + branch_top="remotes/$origin" + else + branch_top="heads" + fi + tag_top="tags" + while read sha1 name + do + case "$name" in + *'^{}') + continue ;; + HEAD) + destname="REMOTE_HEAD" ;; + refs/heads/*) + destname="refs/$branch_top/${name#refs/heads/}" ;; + refs/tags/*) + destname="refs/$tag_top/${name#refs/tags/}" ;; + *) + continue ;; + esac + git update-ref -m "clone: from $repo" "$destname" "$sha1" "" + done < "$GIT_DIR/CLONE_HEAD" +fi + +if test -n "$W"; then + cd "$W" || exit +else + cd "$D" || exit +fi + +if test -z "$bare" +then + # a non-bare repository is always in separate-remote layout + remote_top="refs/remotes/$origin" + head_sha1= + test ! -r "$GIT_DIR/REMOTE_HEAD" || head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"` + case "$head_sha1" in + 'ref: refs/'*) + # Uh-oh, the remote told us (http transport done against + # new style repository with a symref HEAD). + # Ideally we should skip the guesswork but for now + # opt for minimum change. + head_sha1=`expr "z$head_sha1" : 'zref: refs/heads/\(.*\)'` + head_sha1=`cat "$GIT_DIR/$remote_top/$head_sha1"` + ;; + esac + + # The name under $remote_top the remote HEAD seems to point at. + head_points_at=$( + ( + test -f "$GIT_DIR/$remote_top/master" && echo "master" + cd "$GIT_DIR/$remote_top" && + find . -type f -print | sed -e 's/^\.\///' + ) | ( + done=f + while read name + do + test t = $done && continue + branch_tip=`cat "$GIT_DIR/$remote_top/$name"` + if test "$head_sha1" = "$branch_tip" + then + echo "$name" + done=t + fi + done + ) + ) + + # Upstream URL + git config remote."$origin".url "$repo" && + + # Set up the mappings to track the remote branches. + git config remote."$origin".fetch \ + "+refs/heads/*:$remote_top/*" '^$' && + + # Write out remote.$origin config, and update our "$head_points_at". + case "$head_points_at" in + ?*) + # Local default branch + git symbolic-ref HEAD "refs/heads/$head_points_at" && + + # Tracking branch for the primary branch at the remote. + git update-ref HEAD "$head_sha1" && + + rm -f "refs/remotes/$origin/HEAD" + git symbolic-ref "refs/remotes/$origin/HEAD" \ + "refs/remotes/$origin/$head_points_at" && + + git config branch."$head_points_at".remote "$origin" && + git config branch."$head_points_at".merge "refs/heads/$head_points_at" + ;; + '') + if test -z "$head_sha1" + then + # Source had nonexistent ref in HEAD + echo >&2 "Warning: Remote HEAD refers to nonexistent ref, unable to checkout." + no_checkout=t + else + # Source had detached HEAD pointing nowhere + git update-ref --no-deref HEAD "$head_sha1" && + rm -f "refs/remotes/$origin/HEAD" + fi + ;; + esac + + case "$no_checkout" in + '') + test "z$quiet" = z -a "z$no_progress" = z && v=-v || v= + git read-tree -m -u $v HEAD HEAD + esac +fi +rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD" + +trap - 0 -- cgit v1.2.3 From 97561fff3263add59ec25207a0c5a635b28ce9b9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 25 May 2008 22:17:57 -0400 Subject: Don't diff empty tree on branch creation in paranoid update hook Listing all files in a branch during branch creation is silly; the user's file-level ACLs probably don't mean anything at this point. We now treat the base case of 0{40} as an empty diff, as this happens only when the user is creating the branch and there are file level ACLs that diff against the old value of the branch. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 068fa37083..6e0d97c89f 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -225,14 +225,12 @@ sub load_diff ($) { local $/ = "\0"; my %this_diff; if ($base =~ /^0{40}$/) { - open(T,'-|','git','ls-tree', - '-r','--name-only','-z', - $new) or return undef; - while () { - chop; - $this_diff{$_} = 'A'; - } - close T or return undef; + # Don't load the diff at all; we are making the + # branch and have no base to compare to in this + # case. A file level ACL makes no sense in this + # context. Having an empty diff will allow the + # branch creation. + # } else { open(T,'-|','git','diff-tree', '-r','--name-status','-z', -- cgit v1.2.3 From 50b7b2ee99cb98265f847d91159cb3215c6f2379 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 25 May 2008 22:18:01 -0400 Subject: Don't load missing ACL files in paranoid update hook If a user or group ACL file does not exist in the current tip revision of the acl repository we will get an error from cat-file when we ask for that blob as it cannot be resolved. A quick look at the history by rev-list can tell us if there is a path there or not. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 6e0d97c89f..ae94822cd3 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -136,6 +136,7 @@ sub parse_config ($$$$) { local $ENV{GIT_DIR} = shift; my $br = shift; my $fn = shift; + return unless git_value('rev-list','--max-count=1',$br,'--',$fn); info "Loading $br:$fn"; open(I,'-|','git','cat-file','blob',"$br:$fn"); my $section = ''; -- cgit v1.2.3 From fa620f1ac8191fa72e54b8b6acc3e424ecfae26e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 25 May 2008 22:18:05 -0400 Subject: Ignore no-op changes in paranoid update hook If the hook gets invoked with identical old and new ids there is no change taking place. We probably should not have been called, but instead of failing silently allow the no-op. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index ae94822cd3..d18b317b2f 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -259,6 +259,7 @@ deny "Refusing funny ref $ref" unless $ref =~ s,^refs/,,; deny "Bad old value $old" unless $old =~ /^[a-z0-9]{40}$/; deny "Bad new value $new" unless $new =~ /^[a-z0-9]{40}$/; deny "Cannot determine who you are." unless $this_user; +grant "No change requested." if $old eq $new; $repository_name = File::Spec->rel2abs($git_dir); $repository_name =~ m,/([^/]+)(?:\.git|/\.git)$,; -- cgit v1.2.3 From 37a12dda24f7ab250869db7eb00157f78d40c724 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 26 May 2008 14:20:54 +0100 Subject: hg-to-git: add --verbose option This patch adds an option to make hg-to-git quiet by default. Note: it only suppresses those messages that would be printed when everything was up-to-date. Signed-off-by: Johannes Schindelin Acked-by: Stelian Pop Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index d72ffbb777..f68ef725d4 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -46,6 +46,7 @@ options: for incrementals -n, --nrepack=INT: number of changesets that will trigger a repack (default=0, -1 to deactivate) + -v, --verbose: be verbose required: hgprj: name of the HG project to import (directory) @@ -75,15 +76,18 @@ def getgitenv(user, date): state = '' opt_nrepack = 0 +verbose = False try: - opts, args = getopt.getopt(sys.argv[1:], 's:t:n:', ['gitstate=', 'tempdir=', 'nrepack=']) + opts, args = getopt.getopt(sys.argv[1:], 's:t:n:v', ['gitstate=', 'tempdir=', 'nrepack=', 'verbose']) for o, a in opts: if o in ('-s', '--gitstate'): state = a state = os.path.abspath(state) if o in ('-n', '--nrepack'): opt_nrepack = int(a) + if o in ('-v', '--verbose'): + verbose = True if len(args) != 1: raise('params') except: @@ -95,17 +99,20 @@ os.chdir(hgprj) if state: if os.path.exists(state): - print 'State does exist, reading' + if verbose: + print 'State does exist, reading' f = open(state, 'r') hgvers = pickle.load(f) else: print 'State does not exist, first run' tip = os.popen('hg tip --template "{rev}"').read() -print 'tip is', tip +if verbose: + print 'tip is', tip # Calculate the branches -print 'analysing the branches...' +if verbose: + print 'analysing the branches...' hgchildren["0"] = () hgparents["0"] = (None, None) hgbranch["0"] = "master" @@ -232,7 +239,8 @@ if hgnewcsets >= opt_nrepack and opt_nrepack != -1: # write the state for incrementals if state: - print 'Writing state' + if verbose: + print 'Writing state' f = open(state, 'w') pickle.dump(hgvers, f) -- cgit v1.2.3 From 3db4723ead0f141540118f622dedac5106b07a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Hasselstr=C3=B6m?= Date: Tue, 3 Jun 2008 00:41:44 +0200 Subject: Revert "git.el: Set process-environment instead of invoking env" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit dbe48256b41c1e94d81f2458d7e84b1fdcb47026, which caused mis-encoding of non-ASCII author/committer names when the git-status mode is used to create commits. Signed-off-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2557a7667f..4fa853fae7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -232,8 +232,10 @@ and returns the process output as a string, or nil if the git failed." (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (unless (eq 0 (let ((process-environment (append (git-get-env-strings env) - process-environment))) + (unless (eq 0 (if env + (git-run-process-region + buffer start end "env" + (append (git-get-env-strings env) (list "git") args)) (git-run-process-region buffer start end "git" args))) (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) @@ -248,8 +250,9 @@ and returns the process output as a string, or nil if the git failed." (erase-buffer) (cd dir) (setq status - (let ((process-environment (append (git-get-env-strings env) - process-environment))) + (if env + (apply #'call-process "env" nil (list buffer t) nil + (append (git-get-env-strings env) (list hook-name) args)) (apply #'call-process hook-name nil (list buffer t) nil args)))) (display-message-or-buffer buffer) (eq 0 status))))) -- cgit v1.2.3 From 1d284cbae3abd5fb6f58dd5282ba0e93eb68e6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sat, 14 Jun 2008 11:48:01 +0200 Subject: completion: add more 'git add' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 16984632d9..2141b6b6ba 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -500,7 +500,10 @@ _git_add () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--interactive --refresh" + __gitcomp " + --interactive --refresh --patch --update --dry-run + --ignore-errors + " return esac COMPREPLY=() -- cgit v1.2.3 From 20827d99c5ee079d92831474a0b6e66b79757dbd Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 19 Jun 2008 16:15:53 -0500 Subject: completion: add --graph to log command completion Signed-off-by: Dan McGee Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2141b6b6ba..0eb8df020b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -761,6 +761,7 @@ _git_log () --pretty= --name-status --name-only --raw --not --all --left-right --cherry-pick + --graph " return ;; -- cgit v1.2.3 From 0c3d26d24ab913a8cc6f478d176f5896af28c03c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Sandstr=C3=B6m?= Date: Fri, 20 Jun 2008 01:21:33 +0200 Subject: Add a helper script to send patches with Mozilla Thunderbird MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The script appp.sh can be used with the External Editor extension for Mozilla Thunderbird in order to be able to send inline patches in an easy way. Signed-off-by: Lukas Sandström Signed-off-by: Junio C Hamano --- contrib/thunderbird-patch-inline/README | 20 ++++++++++++ contrib/thunderbird-patch-inline/appp.sh | 55 ++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 contrib/thunderbird-patch-inline/README create mode 100755 contrib/thunderbird-patch-inline/appp.sh (limited to 'contrib') diff --git a/contrib/thunderbird-patch-inline/README b/contrib/thunderbird-patch-inline/README new file mode 100644 index 0000000000..39f96aa115 --- /dev/null +++ b/contrib/thunderbird-patch-inline/README @@ -0,0 +1,20 @@ +appp.sh is a script that is supposed to be used together with ExternalEditor +for Mozilla Thundebird. It will let you include patches inline in e-mails +in an easy way. + +Usage: +- Generate the patch with git format-patch. +- Start writing a new e-mail in Thunderbird. +- Press the external editor button (or Ctrl-E) to run appp.sh +- Select the previosly generated patch file. +- Finish editing the e-mail. + +Any text that is entered into the message editor before appp.sh is called +will be moved to the section between the --- and the diffstat. + +All S-O-B:s and Cc:s in the patch will be added to the CC list. + +To set it up, just install External Editor and tell it to use appp.sh as the +editor. + +Zenity is a required dependency. diff --git a/contrib/thunderbird-patch-inline/appp.sh b/contrib/thunderbird-patch-inline/appp.sh new file mode 100755 index 0000000000..cc518f3c89 --- /dev/null +++ b/contrib/thunderbird-patch-inline/appp.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# Copyright 2008 Lukas Sandström +# +# AppendPatch - A script to be used together with ExternalEditor +# for Mozilla Thunderbird to properly include pathes inline i e-mails. + +# ExternalEditor can be downloaded at http://globs.org/articles.php?lng=en&pg=2 + +CONFFILE=~/.appprc + +SEP="-=-=-=-=-=-=-=-=-=# Don't remove this line #=-=-=-=-=-=-=-=-=-" +if [ -e "$CONFFILE" ] ; then + LAST_DIR=`grep -m 1 "^LAST_DIR=" "${CONFFILE}"|sed -e 's/^LAST_DIR=//'` + cd "${LAST_DIR}" +else + cd > /dev/null +fi + +PATCH=$(zenity --file-selection) + +if [ "$?" != "0" ] ; then + #zenity --error --text "No patchfile given." + exit 1 +fi + +cd - > /dev/null + +SUBJECT=`sed -n -e '/^Subject: /p' "${PATCH}"` +HEADERS=`sed -e '/^'"${SEP}"'$/,$d' $1` +BODY=`sed -e "1,/${SEP}/d" $1` +CMT_MSG=`sed -e '1,/^$/d' -e '/^---$/,$d' "${PATCH}"` +DIFF=`sed -e '1,/^---$/d' "${PATCH}"` + +CCS=`echo -e "$CMT_MSG\n$HEADERS" | sed -n -e 's/^Cc: \(.*\)$/\1,/gp' \ + -e 's/^Signed-off-by: \(.*\)/\1,/gp'` + +echo "$SUBJECT" > $1 +echo "Cc: $CCS" >> $1 +echo "$HEADERS" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1 +echo "$SEP" >> $1 + +echo "$CMT_MSG" >> $1 +echo "---" >> $1 +if [ "x${BODY}x" != "xx" ] ; then + echo >> $1 + echo "$BODY" >> $1 + echo >> $1 +fi +echo "$DIFF" >> $1 + +LAST_DIR=`dirname "${PATCH}"` + +grep -v "^LAST_DIR=" "${CONFFILE}" > "${CONFFILE}_" +echo "LAST_DIR=${LAST_DIR}" >> "${CONFFILE}_" +mv "${CONFFILE}_" "${CONFFILE}" -- cgit v1.2.3 From 66aafad5e43815e5f54634e4ef787cd759388880 Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Fri, 20 Jun 2008 16:02:10 +0300 Subject: bash: Add more option completions for 'git log' Options added: --walk-reflogs --stat --numstat --shortstat --decorate --diff-filter= --color-words Signed-off-by: Teemu Likonen Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0eb8df020b..ebf7cde5c0 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -762,6 +762,9 @@ _git_log () --not --all --left-right --cherry-pick --graph + --stat --numstat --shortstat + --decorate --diff-filter= + --color-words --walk-reflogs " return ;; -- cgit v1.2.3 From 4c2d5d722c4775c1efd5e63f41ba5b303ec8fb65 Mon Sep 17 00:00:00 2001 From: Jing Xue Date: Sun, 22 Jun 2008 14:12:39 -0400 Subject: Add 'git-p4.allowSubmit' to git-p4 I'm working with a perforce repo using git-p4. There are some config files which I need to change locally according to my environment. I'm using a 'local' git branch to park these changes. And I want to avoid accidentally checking them into p4 just by doing "git p4 submit" mindlessly without realizing which branch I'm actually on. This patch adds a new git config, 'git-p4.allowSubmit', which is a whitelist of branch names. "git p4 submit" will only allow submissions from local branches on the list. Useful for preventing inadvertently submitting from a strictly local branch. For backward compatibility, if this config is not set at all, submissions from all branches are allowed. Signed-off-by: Jing Xue Acked-By: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d8de9f6c25..87ca51e401 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -687,6 +687,10 @@ class P4Submit(Command): else: return False + allowSubmit = gitConfig("git-p4.allowSubmit") + if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","): + die("%s is not in git-p4.allowSubmit" % self.master) + [upstream, settings] = findUpstreamBranchPoint() self.depotPath = settings['depot-paths'][0] if len(self.origin) == 0: -- cgit v1.2.3 From 8813df9066427ffe40d1d77d18f20643f396f153 Mon Sep 17 00:00:00 2001 From: Olivier Marin Date: Fri, 27 Jun 2008 02:17:55 +0200 Subject: Documentation: remove {show,whatchanged}.difftree config options This removes, from the documentation and the bash completion script, the two config options that were introduced by the git-whatchanged.sh script and lost in the C rewrite. Today, we can use aliases as an alternative. Signed-off-by: Olivier Marin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 -- 1 file changed, 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ebf7cde5c0..3f46149853 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1041,7 +1041,6 @@ _git_config () pull.octopus pull.twohead repack.useDeltaBaseOffset - show.difftree showbranch.default tar.umask transfer.unpackLimit @@ -1050,7 +1049,6 @@ _git_config () user.name user.email user.signingkey - whatchanged.difftree branch. remote. " } -- cgit v1.2.3 From be612c2318e87a9d32a385c7684459e9e0cd6227 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 30 Jun 2008 19:50:44 +0100 Subject: Add another fast-import example, this time for .zip files Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/fast-import/import-zips.py | 72 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 contrib/fast-import/import-zips.py (limited to 'contrib') diff --git a/contrib/fast-import/import-zips.py b/contrib/fast-import/import-zips.py new file mode 100755 index 0000000000..c674fa2d1b --- /dev/null +++ b/contrib/fast-import/import-zips.py @@ -0,0 +1,72 @@ +#!/usr/bin/python + +## zip archive frontend for git-fast-import +## +## For example: +## +## mkdir project; cd project; git init +## python import-zips.py *.zip +## git log --stat import-zips + +from os import popen, path +from sys import argv, exit +from time import mktime +from zipfile import ZipFile + +if len(argv) < 2: + print 'Usage:', argv[0], '...' + exit(1) + +branch_ref = 'refs/heads/import-zips' +committer_name = 'Z Ip Creator' +committer_email = 'zip@example.com' + +fast_import = popen('git fast-import --quiet', 'w') +def printlines(list): + for str in list: + fast_import.write(str + "\n") + +for zipfile in argv[1:]: + commit_time = 0 + next_mark = 1 + common_prefix = None + mark = dict() + + zip = ZipFile(zipfile, 'r') + for name in zip.namelist(): + if name.endswith('/'): + continue + info = zip.getinfo(name) + + if commit_time < info.date_time: + commit_time = info.date_time + if common_prefix == None: + common_prefix = name[:name.rfind('/') + 1] + else: + while not name.startswith(common_prefix): + common_prefix = name[:name.rfind('/') + 1] + + mark[name] = ':' + str(next_mark) + next_mark += 1 + + printlines(('blob', 'mark ' + mark[name], \ + 'data ' + str(info.file_size))) + fast_import.write(zip.read(name) + "\n") + + committer = committer_name + ' <' + committer_email + '> %d +0000' % \ + mktime(commit_time + (0, 0, 0)) + + printlines(('commit ' + branch_ref, 'committer ' + committer, \ + 'data < Date: Fri, 27 Jun 2008 16:37:15 +0200 Subject: stash: introduce 'stash save --keep-index' option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'git stash save' saves local modifications to a new stash, and runs 'git reset --hard' to revert them to a clean index and work tree. When the '--keep-index' option is specified, after that 'git reset --hard' the previous contents of the index is restored and the work tree is updated to match the index. This option is useful if the user wants to commit only parts of his local modifications, but wants to test those parts before committing. Also add support for the completion of the new option, and add an example use case to the documentation. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3f46149853..595de80ea4 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1137,8 +1137,19 @@ _git_show () _git_stash () { local subcommands='save list show apply clear drop pop create' - if [ -z "$(__git_find_subcommand "$subcommands")" ]; then + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then __gitcomp "$subcommands" + else + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$subcommand,$cur" in + save,--*) + __gitcomp "--keep-index" + ;; + *) + COMPREPLY=() + ;; + esac fi } -- cgit v1.2.3 From 6376cffaebe40947eea9afb4ae6df05a6ac59ae8 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 6 Jul 2008 05:15:17 +0200 Subject: hg-to-git: avoid raising a string exception This fixes the following warning: hg-to-git.py:92: DeprecationWarning: raising a string exception is deprecated Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index f68ef725d4..25d99411ca 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -89,7 +89,7 @@ try: if o in ('-v', '--verbose'): verbose = True if len(args) != 1: - raise('params') + raise Exception('params') except: usage() sys.exit(1) -- cgit v1.2.3 From 2553ede5a9b09260be69de72b60e5038f5452c44 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 6 Jul 2008 05:15:18 +0200 Subject: hg-to-git: abort if the project directory is not a hg repo Check the exit code of the first hg command, and abort to avoid a later ValueError exception. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 25d99411ca..130b1c4bcd 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -106,7 +106,10 @@ if state: else: print 'State does not exist, first run' -tip = os.popen('hg tip --template "{rev}"').read() +sock = os.popen('hg tip --template "{rev}"') +tip = sock.read() +if sock.close(): + sys.exit(1) if verbose: print 'tip is', tip -- cgit v1.2.3 From 96f2395951fd81ad49217d49074dfbcf3f0df685 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 6 Jul 2008 05:15:19 +0200 Subject: hg-to-git: rewrite "git-frotz" to "git frotz" This is not just nice but necessary since git-frotz is no longer in PATH. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 130b1c4bcd..61540ef806 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -152,7 +152,7 @@ for cset in range(1, int(tip) + 1): if not hgvers.has_key("0"): print 'creating repository' - os.system('git-init-db') + os.system('git init-db') # loop through every hg changeset for cset in range(int(tip) + 1): @@ -194,10 +194,10 @@ for cset in range(int(tip) + 1): if cset != 0: if hgbranch[str(cset)] == "branch-" + str(cset): print 'creating new branch', hgbranch[str(cset)] - os.system('git-checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent])) + os.system('git checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent])) else: print 'checking out branch', hgbranch[str(cset)] - os.system('git-checkout %s' % hgbranch[str(cset)]) + os.system('git checkout %s' % hgbranch[str(cset)]) # merge if mparent: @@ -206,7 +206,7 @@ for cset in range(int(tip) + 1): else: otherbranch = hgbranch[parent] print 'merging', otherbranch, 'into', hgbranch[str(cset)] - os.system(getgitenv(user, date) + 'git-merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch)) + os.system(getgitenv(user, date) + 'git merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch)) # remove everything except .git and .hg directories os.system('find . \( -path "./.hg" -o -path "./.git" \) -prune -o ! -name "." -print | xargs rm -rf') @@ -215,9 +215,9 @@ for cset in range(int(tip) + 1): os.system('hg update -C %d' % cset) # add new files - os.system('git-ls-files -x .hg --others | git-update-index --add --stdin') + os.system('git ls-files -x .hg --others | git update-index --add --stdin') # delete removed files - os.system('git-ls-files -x .hg --deleted | git-update-index --remove --stdin') + os.system('git ls-files -x .hg --deleted | git update-index --remove --stdin') # commit os.system(getgitenv(user, date) + 'git commit --allow-empty -a -F %s' % filecomment) @@ -225,20 +225,20 @@ for cset in range(int(tip) + 1): # tag if tag and tag != 'tip': - os.system(getgitenv(user, date) + 'git-tag %s' % tag) + os.system(getgitenv(user, date) + 'git tag %s' % tag) # delete branch if not used anymore... if mparent and len(hgchildren[str(cset)]): print "Deleting unused branch:", otherbranch - os.system('git-branch -d %s' % otherbranch) + os.system('git branch -d %s' % otherbranch) # retrieve and record the version - vvv = os.popen('git-show --quiet --pretty=format:%H').read() + vvv = os.popen('git show --quiet --pretty=format:%H').read() print 'record', cset, '->', vvv hgvers[str(cset)] = vvv if hgnewcsets >= opt_nrepack and opt_nrepack != -1: - os.system('git-repack -a -d') + os.system('git repack -a -d') # write the state for incrementals if state: -- cgit v1.2.3 From af9a01e1c2f6a8814b817eb7f3d78814389a3212 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 6 Jul 2008 05:15:20 +0200 Subject: hg-to-git: use git init instead of git init-db Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 61540ef806..7b03204ed1 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -152,7 +152,7 @@ for cset in range(1, int(tip) + 1): if not hgvers.has_key("0"): print 'creating repository' - os.system('git init-db') + os.system('git init') # loop through every hg changeset for cset in range(int(tip) + 1): -- cgit v1.2.3 From bf11d4613c647184bb081fa600f5c0ab8c0be909 Mon Sep 17 00:00:00 2001 From: Dmitry Potapov Date: Wed, 2 Jul 2008 17:29:50 +0400 Subject: completion.bash: add 'skip' and 'run' to git-bisect Signed-off-by: Dmitry Potapov Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3f46149853..d54aa8d62c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -511,7 +511,7 @@ _git_add () _git_bisect () { - local subcommands="start bad good reset visualize replay log" + local subcommands="start bad good skip reset visualize replay log run" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" @@ -519,7 +519,7 @@ _git_bisect () fi case "$subcommand" in - bad|good|reset) + bad|good|reset|skip) __gitcomp "$(__git_refs)" ;; *) -- cgit v1.2.3 From 1c7b76be7d620bbaf2e6b8417f04012326bbb9df Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 7 Jul 2008 19:24:20 +0200 Subject: Build in merge Mentored-by: Johannes Schindelin Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/examples/git-merge.sh | 554 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100755 contrib/examples/git-merge.sh (limited to 'contrib') diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh new file mode 100755 index 0000000000..8026ccff4a --- /dev/null +++ b/contrib/examples/git-merge.sh @@ -0,0 +1,554 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# + +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-merge [options] ... +git-merge [options] HEAD +-- +stat show a diffstat at the end of the merge +n don't show a diffstat at the end of the merge +summary (synonym to --stat) +log add list of one-line log to merge commit message +squash create a single commit instead of doing a merge +commit perform a commit if the merge succeeds (default) +ff allow fast forward (default) +s,strategy= merge strategy to use +m,message= message to be used for the merge commit (if any) +" + +SUBDIRECTORY_OK=Yes +. git-sh-setup +require_work_tree +cd_to_toplevel + +test -z "$(git ls-files -u)" || + die "You are in the middle of a conflicted merge." + +LF=' +' + +all_strategies='recur recursive octopus resolve stupid ours subtree' +default_twohead_strategies='recursive' +default_octopus_strategies='octopus' +no_fast_forward_strategies='subtree ours' +no_trivial_strategies='recursive recur subtree ours' +use_strategies= + +allow_fast_forward=t +allow_trivial_merge=t +squash= no_commit= log_arg= + +dropsave() { + rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \ + "$GIT_DIR/MERGE_STASH" || exit 1 +} + +savestate() { + # Stash away any local modifications. + git stash create >"$GIT_DIR/MERGE_STASH" +} + +restorestate() { + if test -f "$GIT_DIR/MERGE_STASH" + then + git reset --hard $head >/dev/null + git stash apply $(cat "$GIT_DIR/MERGE_STASH") + git update-index --refresh >/dev/null + fi +} + +finish_up_to_date () { + case "$squash" in + t) + echo "$1 (nothing to squash)" ;; + '') + echo "$1" ;; + esac + dropsave +} + +squash_message () { + echo Squashed commit of the following: + echo + git log --no-merges --pretty=medium ^"$head" $remoteheads +} + +finish () { + if test '' = "$2" + then + rlogm="$GIT_REFLOG_ACTION" + else + echo "$2" + rlogm="$GIT_REFLOG_ACTION: $2" + fi + case "$squash" in + t) + echo "Squash commit -- not updating HEAD" + squash_message >"$GIT_DIR/SQUASH_MSG" + ;; + '') + case "$merge_msg" in + '') + echo "No merge message -- not updating HEAD" + ;; + *) + git update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1 + git gc --auto + ;; + esac + ;; + esac + case "$1" in + '') + ;; + ?*) + if test "$show_diffstat" = t + then + # We want color (if set), but no pager + GIT_PAGER='' git diff --stat --summary -M "$head" "$1" + fi + ;; + esac + + # Run a post-merge hook + if test -x "$GIT_DIR"/hooks/post-merge + then + case "$squash" in + t) + "$GIT_DIR"/hooks/post-merge 1 + ;; + '') + "$GIT_DIR"/hooks/post-merge 0 + ;; + esac + fi +} + +merge_name () { + remote="$1" + rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return + bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null) + if test "$rh" = "$bh" + then + echo "$rh branch '$remote' of ." + elif truname=$(expr "$remote" : '\(.*\)~[1-9][0-9]*$') && + git show-ref -q --verify "refs/heads/$truname" 2>/dev/null + then + echo "$rh branch '$truname' (early part) of ." + elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD" + then + sed -e 's/ not-for-merge / /' -e 1q \ + "$GIT_DIR/FETCH_HEAD" + else + echo "$rh commit '$remote'" + fi +} + +parse_config () { + while test $# != 0; do + case "$1" in + -n|--no-stat|--no-summary) + show_diffstat=false ;; + --stat|--summary) + show_diffstat=t ;; + --log|--no-log) + log_arg=$1 ;; + --squash) + test "$allow_fast_forward" = t || + die "You cannot combine --squash with --no-ff." + squash=t no_commit=t ;; + --no-squash) + squash= no_commit= ;; + --commit) + no_commit= ;; + --no-commit) + no_commit=t ;; + --ff) + allow_fast_forward=t ;; + --no-ff) + test "$squash" != t || + die "You cannot combine --squash with --no-ff." + allow_fast_forward=f ;; + -s|--strategy) + shift + case " $all_strategies " in + *" $1 "*) + use_strategies="$use_strategies$1 " ;; + *) + die "available strategies are: $all_strategies" ;; + esac + ;; + -m|--message) + shift + merge_msg="$1" + have_message=t + ;; + --) + shift + break ;; + *) usage ;; + esac + shift + done + args_left=$# +} + +test $# != 0 || usage + +have_message= + +if branch=$(git-symbolic-ref -q HEAD) +then + mergeopts=$(git config "branch.${branch#refs/heads/}.mergeoptions") + if test -n "$mergeopts" + then + parse_config $mergeopts -- + fi +fi + +parse_config "$@" +while test $args_left -lt $#; do shift; done + +if test -z "$show_diffstat"; then + test "$(git config --bool merge.diffstat)" = false && show_diffstat=false + test "$(git config --bool merge.stat)" = false && show_diffstat=false + test -z "$show_diffstat" && show_diffstat=t +fi + +# This could be traditional "merge HEAD ..." and the +# way we can tell it is to see if the second token is HEAD, but some +# people might have misused the interface and used a committish that +# is the same as HEAD there instead. Traditional format never would +# have "-m" so it is an additional safety measure to check for it. + +if test -z "$have_message" && + second_token=$(git rev-parse --verify "$2^0" 2>/dev/null) && + head_commit=$(git rev-parse --verify "HEAD" 2>/dev/null) && + test "$second_token" = "$head_commit" +then + merge_msg="$1" + shift + head_arg="$1" + shift +elif ! git rev-parse --verify HEAD >/dev/null 2>&1 +then + # If the merged head is a valid one there is no reason to + # forbid "git merge" into a branch yet to be born. We do + # the same for "git pull". + if test 1 -ne $# + then + echo >&2 "Can merge only exactly one commit into empty head" + exit 1 + fi + + rh=$(git rev-parse --verify "$1^0") || + die "$1 - not something we can merge" + + git update-ref -m "initial pull" HEAD "$rh" "" && + git read-tree --reset -u HEAD + exit + +else + # We are invoked directly as the first-class UI. + head_arg=HEAD + + # All the rest are the commits being merged; prepare + # the standard merge summary message to be appended to + # the given message. If remote is invalid we will die + # later in the common codepath so we discard the error + # in this loop. + merge_name=$(for remote + do + merge_name "$remote" + done | git fmt-merge-msg $log_arg + ) + merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name" +fi +head=$(git rev-parse --verify "$head_arg"^0) || usage + +# All the rest are remote heads +test "$#" = 0 && usage ;# we need at least one remote head. +set_reflog_action "merge $*" + +remoteheads= +for remote +do + remotehead=$(git rev-parse --verify "$remote"^0 2>/dev/null) || + die "$remote - not something we can merge" + remoteheads="${remoteheads}$remotehead " + eval GITHEAD_$remotehead='"$remote"' + export GITHEAD_$remotehead +done +set x $remoteheads ; shift + +case "$use_strategies" in +'') + case "$#" in + 1) + var="`git config --get pull.twohead`" + if test -n "$var" + then + use_strategies="$var" + else + use_strategies="$default_twohead_strategies" + fi ;; + *) + var="`git config --get pull.octopus`" + if test -n "$var" + then + use_strategies="$var" + else + use_strategies="$default_octopus_strategies" + fi ;; + esac + ;; +esac + +for s in $use_strategies +do + for ss in $no_fast_forward_strategies + do + case " $s " in + *" $ss "*) + allow_fast_forward=f + break + ;; + esac + done + for ss in $no_trivial_strategies + do + case " $s " in + *" $ss "*) + allow_trivial_merge=f + break + ;; + esac + done +done + +case "$#" in +1) + common=$(git merge-base --all $head "$@") + ;; +*) + common=$(git show-branch --merge-base $head "$@") + ;; +esac +echo "$head" >"$GIT_DIR/ORIG_HEAD" + +case "$allow_fast_forward,$#,$common,$no_commit" in +?,*,'',*) + # No common ancestors found. We need a real merge. + ;; +?,1,"$1",*) + # If head can reach all the merge then we are up to date. + # but first the most common case of merging one remote. + finish_up_to_date "Already up-to-date." + exit 0 + ;; +t,1,"$head",*) + # Again the most common case of merging one remote. + echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)" + git update-index --refresh 2>/dev/null + msg="Fast forward" + if test -n "$have_message" + then + msg="$msg (no commit created; -m option ignored)" + fi + new_head=$(git rev-parse --verify "$1^0") && + git read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" && + finish "$new_head" "$msg" || exit + dropsave + exit 0 + ;; +?,1,?*"$LF"?*,*) + # We are not doing octopus and not fast forward. Need a + # real merge. + ;; +?,1,*,) + # We are not doing octopus, not fast forward, and have only + # one common. + git update-index --refresh 2>/dev/null + case "$allow_trivial_merge" in + t) + # See if it is really trivial. + git var GIT_COMMITTER_IDENT >/dev/null || exit + echo "Trying really trivial in-index merge..." + if git read-tree --trivial -m -u -v $common $head "$1" && + result_tree=$(git write-tree) + then + echo "Wonderful." + result_commit=$( + printf '%s\n' "$merge_msg" | + git commit-tree $result_tree -p HEAD -p "$1" + ) || exit + finish "$result_commit" "In-index merge" + dropsave + exit 0 + fi + echo "Nope." + esac + ;; +*) + # An octopus. If we can reach all the remote we are up to date. + up_to_date=t + for remote + do + common_one=$(git merge-base --all $head $remote) + if test "$common_one" != "$remote" + then + up_to_date=f + break + fi + done + if test "$up_to_date" = t + then + finish_up_to_date "Already up-to-date. Yeeah!" + exit 0 + fi + ;; +esac + +# We are going to make a new commit. +git var GIT_COMMITTER_IDENT >/dev/null || exit + +# At this point, we need a real merge. No matter what strategy +# we use, it would operate on the index, possibly affecting the +# working tree, and when resolved cleanly, have the desired tree +# in the index -- this means that the index must be in sync with +# the $head commit. The strategies are responsible to ensure this. + +case "$use_strategies" in +?*' '?*) + # Stash away the local changes so that we can try more than one. + savestate + single_strategy=no + ;; +*) + rm -f "$GIT_DIR/MERGE_STASH" + single_strategy=yes + ;; +esac + +result_tree= best_cnt=-1 best_strategy= wt_strategy= +merge_was_ok= +for strategy in $use_strategies +do + test "$wt_strategy" = '' || { + echo "Rewinding the tree to pristine..." + restorestate + } + case "$single_strategy" in + no) + echo "Trying merge strategy $strategy..." + ;; + esac + + # Remember which strategy left the state in the working tree + wt_strategy=$strategy + + git-merge-$strategy $common -- "$head_arg" "$@" + exit=$? + if test "$no_commit" = t && test "$exit" = 0 + then + merge_was_ok=t + exit=1 ;# pretend it left conflicts. + fi + + test "$exit" = 0 || { + + # The backend exits with 1 when conflicts are left to be resolved, + # with 2 when it does not handle the given merge at all. + + if test "$exit" -eq 1 + then + cnt=`{ + git diff-files --name-only + git ls-files --unmerged + } | wc -l` + if test $best_cnt -le 0 -o $cnt -le $best_cnt + then + best_strategy=$strategy + best_cnt=$cnt + fi + fi + continue + } + + # Automerge succeeded. + result_tree=$(git write-tree) && break +done + +# If we have a resulting tree, that means the strategy module +# auto resolved the merge cleanly. +if test '' != "$result_tree" +then + if test "$allow_fast_forward" = "t" + then + parents=$(git show-branch --independent "$head" "$@") + else + parents=$(git rev-parse "$head" "$@") + fi + parents=$(echo "$parents" | sed -e 's/^/-p /') + result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit + finish "$result_commit" "Merge made by $wt_strategy." + dropsave + exit 0 +fi + +# Pick the result from the best strategy and have the user fix it up. +case "$best_strategy" in +'') + restorestate + case "$use_strategies" in + ?*' '?*) + echo >&2 "No merge strategy handled the merge." + ;; + *) + echo >&2 "Merge with strategy $use_strategies failed." + ;; + esac + exit 2 + ;; +"$wt_strategy") + # We already have its result in the working tree. + ;; +*) + echo "Rewinding the tree to pristine..." + restorestate + echo "Using the $best_strategy to prepare resolving by hand." + git-merge-$best_strategy $common -- "$head_arg" "$@" + ;; +esac + +if test "$squash" = t +then + finish +else + for remote + do + echo $remote + done >"$GIT_DIR/MERGE_HEAD" + printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG" +fi + +if test "$merge_was_ok" = t +then + echo >&2 \ + "Automatic merge went well; stopped before committing as requested" + exit 0 +else + { + echo ' +Conflicts: +' + git ls-files --unmerged | + sed -e 's/^[^ ]* / /' | + uniq + } >>"$GIT_DIR/MERGE_MSG" + git rerere + die "Automatic merge failed; fix conflicts and then commit the result." +fi -- cgit v1.2.3 From d773c6314d5660266313772b3fd8a466c3dbc559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 8 Jul 2008 18:56:14 +0200 Subject: bash: offer only paths after '--' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many git commands use '--' to separate subcommands, options, and refs from paths. However, the programmable completion for several of these commands does not respect the '--', and offer subcommands, options, or refs after a '--', although only paths are permitted. e.g. 'git bisect -- ' offers subcommands, 'git log -- --' offers options and 'git log -- git' offers all gitgui tags. The completion for the following commands share this wrong behaviour: am add bisect commit diff log reset shortlog submodule gitk. To avoid this, we check the presence of a '--' on the command line first and let the shell do filename completion, if one is found. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0eb8df020b..cff28a88af 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -451,6 +451,18 @@ __git_find_subcommand () done } +__git_has_doubledash () +{ + local c=1 + while [ $c -lt $COMP_CWORD ]; do + if [ "--" = "${COMP_WORDS[c]}" ]; then + return 0 + fi + c=$((++c)) + done + return 1 +} + __git_whitespacelist="nowarn warn error error-all strip" _git_am () @@ -497,6 +509,8 @@ _git_apply () _git_add () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -511,6 +525,8 @@ _git_add () _git_bisect () { + __git_has_doubledash && return + local subcommands="start bad good reset visualize replay log" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then @@ -613,6 +629,8 @@ _git_cherry_pick () _git_commit () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -632,6 +650,8 @@ _git_describe () _git_diff () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -734,6 +754,8 @@ _git_ls_tree () _git_log () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) @@ -1085,6 +1107,8 @@ _git_remote () _git_reset () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -1097,6 +1121,8 @@ _git_reset () _git_shortlog () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -1143,6 +1169,8 @@ _git_stash () _git_submodule () { + __git_has_doubledash && return + local subcommands="add status init update" if [ -z "$(__git_find_subcommand "$subcommands")" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1349,6 +1377,8 @@ _git () _gitk () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" local g="$(git rev-parse --git-dir 2>/dev/null)" local merge="" -- cgit v1.2.3 From 50e6102504cb211b6fe6224e67a9ed982efeb02f Mon Sep 17 00:00:00 2001 From: Eric Raible Date: Mon, 7 Jul 2008 13:41:54 -0700 Subject: completion: add branch options --contains --merged --no-merged Signed-off-by: Eric Raible Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d54aa8d62c..cc75ad7ccd 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -546,7 +546,7 @@ _git_branch () --*) __gitcomp " --color --no-color --verbose --abbrev= --no-abbrev - --track --no-track + --track --no-track --contains --merged --no-merged " ;; *) -- cgit v1.2.3 From 31a92f6aa47732e93f6685b8d1fd1ac09c1fec44 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 8 Jul 2008 19:48:04 +0200 Subject: Git.pm: Add remote_refs() git-ls-remote frontend This patch also converts the good ole' git-remote.perl to use it. It is otherwise used in the repo.or.cz machinery and I guess other scripts might find it useful too. Unfortunately, git-ls-remote --heads . is subtly different from git-ls-remote . refs/heads/ (since the second matches anywhere in the string, not just at the beginning) so we have to provide interface for both. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- contrib/examples/git-remote.perl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-remote.perl b/contrib/examples/git-remote.perl index b30ed734e7..36bd54c985 100755 --- a/contrib/examples/git-remote.perl +++ b/contrib/examples/git-remote.perl @@ -129,10 +129,7 @@ sub update_ls_remote { return if (($harder == 0) || (($harder == 1) && exists $info->{'LS_REMOTE'})); - my @ref = map { - s|^[0-9a-f]{40}\s+refs/heads/||; - $_; - } $git->command(qw(ls-remote --heads), $info->{'URL'}); + my @ref = map { s|refs/heads/||; $_; } keys %{$git->remote_refs($info->{'URL'}, [ 'heads' ])}; $info->{'LS_REMOTE'} = \@ref; } -- cgit v1.2.3 From ab02dfe533f55535bdb66e05776a4081020322c6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 13 Jul 2008 02:37:42 +0000 Subject: bash completion: Improve responsiveness of git-log completion Junio noticed the bash completion has been taking a long time lately. Petr Baudis tracked it down to 72e5e989b ("bash: Add space after unique command name is completed."). Tracing the code showed we spent significant time inside of this loop within __gitcomp, due to the string copying overhead. [28.146109654] _git common over [28.164791148] gitrefs in [28.280302268] gitrefs dir out [28.300939737] gitcomp in [28.308378112] gitcomp pre-case * [28.313407453] gitcomp iter in * [28.701270296] gitcomp iter out [28.713370786] out normal Since __git_refs avoids this string copying by forking and using echo we use the same trick here when we need to finish generating the names for the caller. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index cff28a88af..0734ea313c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -114,9 +114,20 @@ __git_ps1 () fi } +__gitcomp_1 () +{ + local c IFS=' '$'\t'$'\n' + for c in $1; do + case "$c$2" in + --*=*) printf %s$'\n' "$c$2" ;; + *.) printf %s$'\n' "$c$2" ;; + *) printf %s$'\n' "$c$2 " ;; + esac + done +} + __gitcomp () { - local all c s=$'\n' IFS=' '$'\t'$'\n' local cur="${COMP_WORDS[COMP_CWORD]}" if [ $# -gt 2 ]; then cur="$3" @@ -124,21 +135,14 @@ __gitcomp () case "$cur" in --*=) COMPREPLY=() - return ;; *) - for c in $1; do - case "$c$4" in - --*=*) all="$all$c$4$s" ;; - *.) all="$all$c$4$s" ;; - *) all="$all$c$4 $s" ;; - esac - done + local IFS=$'\n' + COMPREPLY=($(compgen -P "$2" \ + -W "$(__gitcomp_1 "$1" "$4")" \ + -- "$cur")) ;; esac - IFS=$s - COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur")) - return } __git_heads () -- cgit v1.2.3 From 6c36c9e4eabadecf75f8751b1c1140da2068e2a0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 13 Jul 2008 22:06:31 +0000 Subject: bash completion: Don't offer "a.." as a completion for "a." If the user is trying to complete "v1.5.3." to see all of the available maintenance releases for 1.5.3 we should not give them an extra dot as the completion. Instead if the user wants a ".." or a "..." operator they should key the two dots out on their own. Its the same number of keystrokes either way. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 --- 1 file changed, 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0734ea313c..821c9a7f9f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -324,9 +324,6 @@ __git_complete_revlist () cur="${cur#*..}" __gitcomp "$(__git_refs)" "$pfx" "$cur" ;; - *.) - __gitcomp "$cur." - ;; *) __gitcomp "$(__git_refs)" ;; -- cgit v1.2.3 From 778306e405b416d8073652c535777f2de5fc68ad Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 14 Jul 2008 00:22:03 +0000 Subject: bash completion: Append space after file names have been completed When completing `git show origin/maint:Makef` we should add a space after the filename has been completed, so that the user can immediately begin the next argument. I also added a special case for the symlink variant so we treat it just like a normal blob, as there are no items below it in the Git tree structure. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 821c9a7f9f..0a3bea44f7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -294,9 +294,17 @@ __git_complete_file () ls="$ref" ;; esac + local IFS=$'\n' COMPREPLY=($(compgen -P "$pfx" \ -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \ - | sed '/^100... blob /s,^.* ,, + | sed '/^100... blob /{ + s,^.* ,, + s,$, , + } + /^120000 blob /{ + s,^.* ,, + s,$, , + } /^040000 tree /{ s,^.* ,, s,$,/, -- cgit v1.2.3 From db8a9ff03831a26aa8bfad8bb026b90739d684ec Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 15 Jul 2008 05:52:04 +0000 Subject: bash completion: Resolve git show ref:path losing ref: portion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linus reported that the bash completion for git show often dropped the ref portion of the argument (stuff before the :) when trying to complete a file name of a file in another branch or tag. Björn Steinbrink tracked it down to the gvfs completion script which comes standard on many Fedora Core based systems. That is removing : from COMP_WORDBREAKS, making readline treat the entire argument (including the ref) as the name that must be completed. When the git completion routines supplied a completion of just the filename, readline replaced everything. Since Git users often need to use "ref:path" or "ref:ref" sort of arguments, and expect completion support on both sides of the : we really want the : in COMP_WORDBREAKS to provide a good user experience. This is also the default that ships with bash as it can be useful in other contexts, such as rcp/scp. We now try to add : back to COMP_WORDBREAKS if it has been removed by a script that loaded before us. However if this doesn't work (as the : is stripped after we load) we fallback in the completion routines to include "ref:" as part of the prefix for completions, allowing readine to fully insert the argument the user wanted. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0a3bea44f7..72f02f208f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -45,6 +45,11 @@ # git@vger.kernel.org # +case "$COMP_WORDBREAKS" in +*:*) : great ;; +*) COMP_WORDBREAKS="$COMP_WORDBREAKS:" +esac + __gitdir () { if [ -z "$1" ]; then @@ -294,6 +299,12 @@ __git_complete_file () ls="$ref" ;; esac + + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="$ref:$pfx" ;; + esac + local IFS=$'\n' COMPREPLY=($(compgen -P "$pfx" \ -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \ @@ -700,7 +711,12 @@ _git_fetch () *) case "$cur" in *:*) - __gitcomp "$(__git_refs)" "" "${cur#*:}" + local pfx="" + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="${cur%%:*}:" ;; + esac + __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}" ;; *) local remote @@ -873,7 +889,14 @@ _git_push () git-push) remote="${COMP_WORDS[1]}" ;; git) remote="${COMP_WORDS[2]}" ;; esac - __gitcomp "$(__git_refs "$remote")" "" "${cur#*:}" + + local pfx="" + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="${cur%%:*}:" ;; + esac + + __gitcomp "$(__git_refs "$remote")" "$pfx" "${cur#*:}" ;; +*) __gitcomp "$(__git_refs)" + "${cur#+}" -- cgit v1.2.3 From 25a1f374f0ff23a4d9191436226ab68f3da5e83a Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Mon, 14 Jul 2008 11:21:02 +0300 Subject: bash: Add long option completion for 'git send-email' Add the following long options to be completed with 'git send-email': --bcc --cc --cc-cmd --chain-reply-to --compose --dry-run --envelope-sender --from --identity --in-reply-to --no-chain-reply-to --no-signed-off-by-cc --no-suppress-from --no-thread --quiet --signed-off-by-cc --smtp-pass --smtp-server --smtp-server-port --smtp-ssl --smtp-user --subject --suppress-cc --suppress-from --thread --to Short ones like --to and --cc are not usable for actual completion because of the shortness itself and because there are longer ones which start with same letters (--thread, --compose). It's still useful to have these shorter options _listed_ when user presses TAB key after typing two dashes. It gives user an idea what options are available (and --to and --cc are probably the most commonly used). Signed-off-by: Teemu Likonen Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d268e6f0b3..d48dbf2672 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -905,6 +905,24 @@ _git_rebase () __gitcomp "$(__git_refs)" } +_git_send_email () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--bcc --cc --cc-cmd --chain-reply-to --compose + --dry-run --envelope-sender --from --identity + --in-reply-to --no-chain-reply-to --no-signed-off-by-cc + --no-suppress-from --no-thread --quiet + --signed-off-by-cc --smtp-pass --smtp-server + --smtp-server-port --smtp-ssl --smtp-user --subject + --suppress-cc --suppress-from --thread --to" + return + ;; + esac + COMPREPLY=() +} + _git_config () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1376,6 +1394,7 @@ _git () rebase) _git_rebase ;; remote) _git_remote ;; reset) _git_reset ;; + send-email) _git_send_email ;; shortlog) _git_shortlog ;; show) _git_show ;; show-branch) _git_log ;; @@ -1435,6 +1454,7 @@ complete -o default -o nospace -F _git_rebase git-rebase complete -o default -o nospace -F _git_config git-config complete -o default -o nospace -F _git_remote git-remote complete -o default -o nospace -F _git_reset git-reset +complete -o default -o nospace -F _git_send_email git-send-email complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_stash git-stash -- cgit v1.2.3 From 055767194c15f9b7c39a509daf1a77472c77bed8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 15 Jul 2008 05:58:06 +0000 Subject: bash completion: Remove dashed command completion support Since only 'git' and 'gitk' are in the user's $PATH now we do not expect users to need completion support for git-fetch, and expect they will instead rely upon the completion support for 'git fetch'. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 54 ---------------------------------- 1 file changed, 54 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 37f52d5395..03e4e02785 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1459,65 +1459,11 @@ _gitk () complete -o default -o nospace -F _git git complete -o default -o nospace -F _gitk gitk -complete -o default -o nospace -F _git_am git-am -complete -o default -o nospace -F _git_apply git-apply -complete -o default -o nospace -F _git_bisect git-bisect -complete -o default -o nospace -F _git_branch git-branch -complete -o default -o nospace -F _git_bundle git-bundle -complete -o default -o nospace -F _git_checkout git-checkout -complete -o default -o nospace -F _git_cherry git-cherry -complete -o default -o nospace -F _git_cherry_pick git-cherry-pick -complete -o default -o nospace -F _git_commit git-commit -complete -o default -o nospace -F _git_describe git-describe -complete -o default -o nospace -F _git_diff git-diff -complete -o default -o nospace -F _git_fetch git-fetch -complete -o default -o nospace -F _git_format_patch git-format-patch -complete -o default -o nospace -F _git_gc git-gc -complete -o default -o nospace -F _git_log git-log -complete -o default -o nospace -F _git_ls_remote git-ls-remote -complete -o default -o nospace -F _git_ls_tree git-ls-tree -complete -o default -o nospace -F _git_merge git-merge -complete -o default -o nospace -F _git_merge_base git-merge-base -complete -o default -o nospace -F _git_name_rev git-name-rev -complete -o default -o nospace -F _git_pull git-pull -complete -o default -o nospace -F _git_push git-push -complete -o default -o nospace -F _git_rebase git-rebase -complete -o default -o nospace -F _git_config git-config -complete -o default -o nospace -F _git_remote git-remote -complete -o default -o nospace -F _git_reset git-reset -complete -o default -o nospace -F _git_send_email git-send-email -complete -o default -o nospace -F _git_shortlog git-shortlog -complete -o default -o nospace -F _git_show git-show -complete -o default -o nospace -F _git_stash git-stash -complete -o default -o nospace -F _git_submodule git-submodule -complete -o default -o nospace -F _git_svn git-svn -complete -o default -o nospace -F _git_log git-show-branch -complete -o default -o nospace -F _git_tag git-tag -complete -o default -o nospace -F _git_log git-whatchanged # The following are necessary only for Cygwin, and only are needed # when the user has tab-completed the executable name and consequently # included the '.exe' suffix. # if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then -complete -o default -o nospace -F _git_add git-add.exe -complete -o default -o nospace -F _git_apply git-apply.exe complete -o default -o nospace -F _git git.exe -complete -o default -o nospace -F _git_branch git-branch.exe -complete -o default -o nospace -F _git_bundle git-bundle.exe -complete -o default -o nospace -F _git_cherry git-cherry.exe -complete -o default -o nospace -F _git_describe git-describe.exe -complete -o default -o nospace -F _git_diff git-diff.exe -complete -o default -o nospace -F _git_format_patch git-format-patch.exe -complete -o default -o nospace -F _git_log git-log.exe -complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe -complete -o default -o nospace -F _git_merge_base git-merge-base.exe -complete -o default -o nospace -F _git_name_rev git-name-rev.exe -complete -o default -o nospace -F _git_push git-push.exe -complete -o default -o nospace -F _git_config git-config -complete -o default -o nospace -F _git_shortlog git-shortlog.exe -complete -o default -o nospace -F _git_show git-show.exe -complete -o default -o nospace -F _git_log git-show-branch.exe -complete -o default -o nospace -F _git_tag git-tag.exe -complete -o default -o nospace -F _git_log git-whatchanged.exe fi -- cgit v1.2.3 From 28ed6e7b321bee3dd7e4aa9c0ff7da64844136f6 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 16 Jul 2008 03:33:44 +0200 Subject: Rename ".dotest/" to ".git/rebase" and ".dotest-merge" to "rebase-merge" Since the files generated and used during a rebase are never to be tracked, they should live in $GIT_DIR. While at it, avoid the rather meaningless term "dotest" to "rebase", and unhide ".dotest-merge". This was wished for on the mailing list, but so far unimplemented. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 20 ++++++++++---------- contrib/emacs/git.el | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 03e4e02785..29f6cd4e9e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -73,26 +73,26 @@ __git_ps1 () if [ -n "$g" ]; then local r local b - if [ -d "$g/../.dotest" ] + if [ -d "$g/rebase" ] then - if test -f "$g/../.dotest/rebasing" + if test -f "$g/rebase/rebasing" then r="|REBASE" - elif test -f "$g/../.dotest/applying" + elif test -f "$g/rebase/applying" then r="|AM" else r="|AM/REBASE" fi b="$(git symbolic-ref HEAD 2>/dev/null)" - elif [ -f "$g/.dotest-merge/interactive" ] + elif [ -f "$g/rebase-merge/interactive" ] then r="|REBASE-i" - b="$(cat "$g/.dotest-merge/head-name")" - elif [ -d "$g/.dotest-merge" ] + b="$(cat "$g/rebase-merge/head-name")" + elif [ -d "$g/rebase-merge" ] then r="|REBASE-m" - b="$(cat "$g/.dotest-merge/head-name")" + b="$(cat "$g/rebase-merge/head-name")" elif [ -f "$g/MERGE_HEAD" ] then r="|MERGING" @@ -487,8 +487,8 @@ __git_whitespacelist="nowarn warn error error-all strip" _git_am () { - local cur="${COMP_WORDS[COMP_CWORD]}" - if [ -d .dotest ]; then + local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" + if [ -d "$dir"/rebase ]; then __gitcomp "--skip --resolved" return fi @@ -915,7 +915,7 @@ _git_push () _git_rebase () { local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" - if [ -d .dotest ] || [ -d "$dir"/.dotest-merge ]; then + if [ -d "$dir"/rebase ] || [ -d "$dir"/rebase-merge ]; then __gitcomp "--continue --skip --abort" return fi diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 4fa853fae7..43b059bbaa 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1252,8 +1252,8 @@ Return the list of files that haven't been handled." "\n") (when subject (insert subject "\n\n")) (cond (msg (insert msg "\n")) - ((file-readable-p ".dotest/msg") - (insert-file-contents ".dotest/msg")) + ((file-readable-p ".git/rebase/msg") + (insert-file-contents ".git/rebase/msg")) ((file-readable-p ".git/MERGE_MSG") (insert-file-contents ".git/MERGE_MSG"))) ; delete empty lines at end @@ -1272,9 +1272,9 @@ Return the list of files that haven't been handled." (coding-system (git-get-commits-coding-system)) author-name author-email subject date) (when (eq 0 (buffer-size buffer)) - (when (file-readable-p ".dotest/info") + (when (file-readable-p ".git/rebase/info") (with-temp-buffer - (insert-file-contents ".dotest/info") + (insert-file-contents ".git/rebase/info") (goto-char (point-min)) (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) (setq author-name (match-string 1)) -- cgit v1.2.3 From 51ef1daa4a0dfaa4d777b2fa949ba051cf800554 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 21 Jul 2008 12:51:02 +0200 Subject: Rename .git/rebase to .git/rebase-apply With git-am, it sounds awkward to have the patches in ".git/rebase/", but for technical reasons, we have to keep the same directory name for git-am and git-rebase. ".git/rebase-apply" seems to be a good compromise. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 +++++----- contrib/emacs/git.el | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 29f6cd4e9e..2edb341b57 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -73,12 +73,12 @@ __git_ps1 () if [ -n "$g" ]; then local r local b - if [ -d "$g/rebase" ] + if [ -d "$g/rebase-apply" ] then - if test -f "$g/rebase/rebasing" + if test -f "$g/rebase-apply/rebasing" then r="|REBASE" - elif test -f "$g/rebase/applying" + elif test -f "$g/rebase-apply/applying" then r="|AM" else @@ -488,7 +488,7 @@ __git_whitespacelist="nowarn warn error error-all strip" _git_am () { local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" - if [ -d "$dir"/rebase ]; then + if [ -d "$dir"/rebase-apply ]; then __gitcomp "--skip --resolved" return fi @@ -915,7 +915,7 @@ _git_push () _git_rebase () { local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" - if [ -d "$dir"/rebase ] || [ -d "$dir"/rebase-merge ]; then + if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then __gitcomp "--continue --skip --abort" return fi diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 43b059bbaa..c1cf1cbcc0 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1252,8 +1252,8 @@ Return the list of files that haven't been handled." "\n") (when subject (insert subject "\n\n")) (cond (msg (insert msg "\n")) - ((file-readable-p ".git/rebase/msg") - (insert-file-contents ".git/rebase/msg")) + ((file-readable-p ".git/rebase-apply/msg") + (insert-file-contents ".git/rebase-apply/msg")) ((file-readable-p ".git/MERGE_MSG") (insert-file-contents ".git/MERGE_MSG"))) ; delete empty lines at end @@ -1272,9 +1272,9 @@ Return the list of files that haven't been handled." (coding-system (git-get-commits-coding-system)) author-name author-email subject date) (when (eq 0 (buffer-size buffer)) - (when (file-readable-p ".git/rebase/info") + (when (file-readable-p ".git/rebase-apply/info") (with-temp-buffer - (insert-file-contents ".git/rebase/info") + (insert-file-contents ".git/rebase-apply/info") (goto-char (point-min)) (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) (setq author-name (match-string 1)) -- cgit v1.2.3 From c79cc2e59672fa03985f27ecdbea84e06ea5358f Mon Sep 17 00:00:00 2001 From: Nikolaj Schumacher Date: Mon, 30 Jun 2008 12:08:16 +0200 Subject: Don't cut off last character of commit descriptions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should have been part of 24a2293 (git-blame.el: show the when, who and what in the minibuffer., 2008-02-12), that changed from using --pretty=oneline to --pretty=format:... without terminating newline. Acked-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index 9f92cd250b..4fa70c5ad4 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -381,7 +381,7 @@ See also function `git-blame-mode'." "log" "-1" (concat "--pretty=" git-blame-log-oneline-format) hash) - (buffer-substring (point-min) (1- (point-max))))) + (buffer-substring (point-min) (point-max)))) (defvar git-blame-last-identification nil) (make-variable-buffer-local 'git-blame-last-identification) -- cgit v1.2.3 From 2d71bde2d1a8f4e1c06cb8049a794ba4ba35d37d Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Tue, 22 Jul 2008 12:48:57 -0400 Subject: In perforce, RCS keywords are case-sensitive At least, this is true in 2007.2, according to the documentation. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 87ca51e401..6ae0429c2d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -906,7 +906,7 @@ class P4Sync(Command): if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text) elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): - text = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text) + text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text) contents[stat['depotFile']] = text -- cgit v1.2.3 From a31c00b00eeadbc4b87ce2d578688e9b369a50fe Mon Sep 17 00:00:00 2001 From: Stephan Beyer Date: Wed, 23 Jul 2008 02:10:25 +0200 Subject: am --abort: Add to bash-completion and mention in git-rerere documentation The git-rerere documentation talks about commands that invoke "git rerere clear" automatically. git am --abort is added and a typo is fixed additionally. Signed-off-by: Stephan Beyer Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2edb341b57..8fc9145282 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -489,7 +489,7 @@ _git_am () { local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" if [ -d "$dir"/rebase-apply ]; then - __gitcomp "--skip --resolved" + __gitcomp "--skip --resolved --abort" return fi case "$cur" in -- cgit v1.2.3 From e8a43a132d97165f6e56fa03923a3933cfedde81 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Wed, 23 Jul 2008 23:08:27 +0300 Subject: svnimport: newer libsvn wants us to ask for the root with "", not "/" In r27729, libsvn introduced an assert which explicitly forbids searching the tree at "/". Luckily enough, it still accepts an empty string "" as the starting point. http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_ra/ra_loader.c?r1=27653&r2=27729 Tested against libsvn0-1.5.0-4mdv2009.0 (needs the fix), libsvn0-1.4.6-5mdv2008.1 (works anyway) Signed-off-by: P. Christeas Signed-off-by: Junio C Hamano --- contrib/examples/git-svnimport.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl index ea8c1b2f60..a13bb6afec 100755 --- a/contrib/examples/git-svnimport.perl +++ b/contrib/examples/git-svnimport.perl @@ -933,7 +933,7 @@ while ($to_rev < $opt_l) { $to_rev = $from_rev + $repack_after; $to_rev = $opt_l if $opt_l < $to_rev; print "Fetching from $from_rev to $to_rev ...\n" if $opt_v; - $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all); + $svn->{'svn'}->get_log("",$from_rev,$to_rev,0,1,1,\&commit_all); my $pid = fork(); die "Fork: $!\n" unless defined $pid; unless($pid) { -- cgit v1.2.3 From 08c701d4761abf58dce607e84bf41fb280e38a9e Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 23 Jul 2008 15:21:08 -0600 Subject: bash completion: Add long options for 'git rm' Options added: --cached --dry-run --ignore-unmatch --quiet Signed-off-by: Lee Marlow Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8fc9145282..e20d57a1ba 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1170,6 +1170,20 @@ _git_reset () __gitcomp "$(__git_refs)" } +_git_rm () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --dry-run --ignore-unmatch --quiet" + return + ;; + esac + COMPREPLY=() +} + _git_shortlog () { __git_has_doubledash && return @@ -1425,6 +1439,7 @@ _git () rebase) _git_rebase ;; remote) _git_remote ;; reset) _git_reset ;; + rm) _git_rm ;; send-email) _git_send_email ;; shortlog) _git_shortlog ;; show) _git_show ;; -- cgit v1.2.3 From 2ca880fe54660869bc93a2302efced9ab64511d9 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Wed, 23 Jul 2008 23:36:15 +0200 Subject: git-completion.bash: provide completion for 'show-branch' It previously used the same as 'log', but the options are quite different and the arguments must be single refs (or globs). Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e20d57a1ba..3b049348c3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1224,6 +1224,22 @@ _git_show () __git_complete_file } +_git_show_branch () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --all --remotes --topo-order --current --more= + --list --independent --merge-base --no-name + --sha1-name --topics --reflog + " + return + ;; + esac + __git_complete_revlist +} + _git_stash () { local subcommands='save list show apply clear drop pop create' @@ -1443,7 +1459,7 @@ _git () send-email) _git_send_email ;; shortlog) _git_shortlog ;; show) _git_show ;; - show-branch) _git_log ;; + show-branch) _git_show_branch ;; stash) _git_stash ;; submodule) _git_submodule ;; svn) _git_svn ;; -- cgit v1.2.3 From c84bb14ce52b6559e0b8e10d554ff9b47149c042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Wed, 23 Jul 2008 13:49:22 +0200 Subject: bash: offer only paths after '--' for 'git checkout' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit d773c631 (bash: offer only paths after '--', 2008-07-08) did the same for several other git commands, but 'git checkout' went unnoticed. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3b049348c3..40b3d99737 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -626,6 +626,8 @@ _git_bundle () _git_checkout () { + __git_has_doubledash && return + __gitcomp "$(__git_refs)" } -- cgit v1.2.3 From cbb504c97437653540dc55430a6f64da9ddd24fd Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 26 Jul 2008 12:26:56 +0200 Subject: bash completion: Add long options for 'git describe' Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 40b3d99737..2fb88a8fef 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -667,6 +667,15 @@ _git_commit () _git_describe () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --all --tags --contains --abbrev= --candidates= + --exact-match --debug --long --match --always + " + return + esac __gitcomp "$(__git_refs)" } -- cgit v1.2.3 From 1eb7e2f83472f49fda62cefe1d2d9b4c668c6771 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 23 Jul 2008 18:07:23 -0600 Subject: bash completion: Add completion for 'git help' Rename cached __git_commandlist to __git_porcelain_commandlist and add __git_all_commandlist that only filters out *--* helpers. Completions for 'git help' will use the __git_all_commandlist, while the __git_porcelain_commandlist is used for git command completion. Users who actually read man pages may want to see help for plumbing commands. Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 46 ++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2fb88a8fef..30d870187e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -349,14 +349,32 @@ __git_complete_revlist () esac } -__git_commands () +__git_all_commands () { - if [ -n "$__git_commandlist" ]; then - echo "$__git_commandlist" + if [ -n "$__git_all_commandlist" ]; then + echo "$__git_all_commandlist" return fi local i IFS=" "$'\n' for i in $(git help -a|egrep '^ ') + do + case $i in + *--*) : helper pattern;; + *) echo $i;; + esac + done +} +__git_all_commandlist= +__git_all_commandlist="$(__git_all_commands 2>/dev/null)" + +__git_porcelain_commands () +{ + if [ -n "$__git_porcelain_commandlist" ]; then + echo "$__git_porcelain_commandlist" + return + fi + local i IFS=" "$'\n' + for i in "help" $(__git_all_commands) do case $i in *--*) : helper pattern;; @@ -427,8 +445,8 @@ __git_commands () esac done } -__git_commandlist= -__git_commandlist="$(__git_commands 2>/dev/null)" +__git_porcelain_commandlist= +__git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)" __git_aliases () { @@ -778,6 +796,18 @@ _git_gc () COMPREPLY=() } +_git_help () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--all --info --man --web" + return + ;; + esac + __gitcomp "$(__git_all_commands)" +} + _git_ls_remote () { __gitcomp "$(__git_remotes)" @@ -1410,7 +1440,8 @@ _git () case "$i" in --git-dir=*) __git_dir="${i#--git-dir=}" ;; --bare) __git_dir="." ;; - --version|--help|-p|--paginate) ;; + --version|-p|--paginate) ;; + --help) command="help"; break ;; *) command="$i"; break ;; esac c=$((++c)) @@ -1430,7 +1461,7 @@ _git () --help " ;; - *) __gitcomp "$(__git_commands) $(__git_aliases)" ;; + *) __gitcomp "$(__git_porcelain_commands) $(__git_aliases)" ;; esac return fi @@ -1455,6 +1486,7 @@ _git () fetch) _git_fetch ;; format-patch) _git_format_patch ;; gc) _git_gc ;; + help) _git_help ;; log) _git_log ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; -- cgit v1.2.3 From 5354a56fe70420c147f930e0f7f1decbae685d19 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Wed, 30 Jul 2008 13:48:33 -0400 Subject: Replace uses of "git-var" with "git var" Signed-off-by: Todd Zullinger Signed-off-by: Junio C Hamano --- contrib/examples/git-commit.sh | 6 +++--- contrib/examples/git-tag.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh index 2c4a4062a5..5c72f655c7 100755 --- a/contrib/examples/git-commit.sh +++ b/contrib/examples/git-commit.sh @@ -443,7 +443,7 @@ fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG case "$signoff" in t) - sign=$(git-var GIT_COMMITTER_IDENT | sed -e ' + sign=$(git var GIT_COMMITTER_IDENT | sed -e ' s/>.*/>/ s/^/Signed-off-by: / ') @@ -535,8 +535,8 @@ esac case "$no_edit" in '') - git-var GIT_AUTHOR_IDENT > /dev/null || die - git-var GIT_COMMITTER_IDENT > /dev/null || die + git var GIT_AUTHOR_IDENT > /dev/null || die + git var GIT_COMMITTER_IDENT > /dev/null || die git_editor "$GIT_DIR/COMMIT_EDITMSG" ;; esac diff --git a/contrib/examples/git-tag.sh b/contrib/examples/git-tag.sh index e9f3a228af..2c15bc955b 100755 --- a/contrib/examples/git-tag.sh +++ b/contrib/examples/git-tag.sh @@ -164,7 +164,7 @@ git check-ref-format "tags/$name" || object=$(git rev-parse --verify --default HEAD "$@") || exit 1 type=$(git cat-file -t $object) || exit 1 -tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1 +tagger=$(git var GIT_COMMITTER_IDENT) || exit 1 test -n "$username" || username=$(git config user.signingkey) || -- cgit v1.2.3 From 7339479c2b2986891763300e385e71ab6ae56322 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Fri, 1 Aug 2008 22:47:09 -0600 Subject: bash completion: remove unused function _git_diff_tree completion for git diff-tree was removed in 5cfb4fe Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 ----- 1 file changed, 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 30d870187e..e32c1f1a9c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -721,11 +721,6 @@ _git_diff () __git_complete_file } -_git_diff_tree () -{ - __gitcomp "$(__git_refs)" -} - _git_fetch () { local cur="${COMP_WORDS[COMP_CWORD]}" -- cgit v1.2.3 From e49b99a6f5ac9f6638b0e5f938674f031555f374 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Fri, 1 Aug 2008 18:56:53 -0600 Subject: bash completion: Add more long options for 'git log' Options added: --parents --children --full-history Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e32c1f1a9c..678a155f2e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -848,6 +848,7 @@ _git_log () --stat --numstat --shortstat --decorate --diff-filter= --color-words --walk-reflogs + --parents --children --full-history " return ;; -- cgit v1.2.3 From c72e0db1ff686e93478ee21a3e80a9ca73143753 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Fri, 1 Aug 2008 18:56:33 -0600 Subject: bash completion: Add completion for 'git grep' Add completions for all long options specified in the docs --cached --text --ignore-case --word-regexp --invert-match --full-name --extended-regexp --basic-regexp --fixed-strings --files-with-matches --name-only --files-without-match --count --and --or --not --all-match Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 678a155f2e..253be56ae2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -791,6 +791,29 @@ _git_gc () COMPREPLY=() } +_git_grep () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --cached + --text --ignore-case --word-regexp --invert-match + --full-name + --extended-regexp --basic-regexp --fixed-strings + --files-with-matches --name-only + --files-without-match + --count + --and --or --not --all-match + " + return + ;; + esac + COMPREPLY=() +} + _git_help () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1482,6 +1505,7 @@ _git () fetch) _git_fetch ;; format-patch) _git_format_patch ;; gc) _git_gc ;; + grep) _git_grep ;; help) _git_help ;; log) _git_log ;; ls-remote) _git_ls_remote ;; -- cgit v1.2.3 From 3eb11012078e9a2b9f444dbf1aae1f1cdd33fef1 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:31 -0600 Subject: bash completion: Add completion for 'git clone' Add completions for all long options specified in the docs --local --no-hardlinks --shared --reference --quiet --no-checkout --bare --mirror --origin --upload-pack --template= --depth Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 253be56ae2..1474525283 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -667,6 +667,31 @@ _git_cherry_pick () esac } +_git_clone () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --local + --no-hardlinks + --shared + --reference + --quiet + --no-checkout + --bare + --mirror + --origin + --upload-pack + --template= + --depth + " + return + ;; + esac + COMPREPLY=() +} + _git_commit () { __git_has_doubledash && return @@ -1498,6 +1523,7 @@ _git () checkout) _git_checkout ;; cherry) _git_cherry ;; cherry-pick) _git_cherry_pick ;; + clone) _git_clone ;; commit) _git_commit ;; config) _git_config ;; describe) _git_describe ;; -- cgit v1.2.3 From 4181c7e8a7ece0cf117e7ec6bb8bca989e116f7e Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:32 -0600 Subject: bash completion: Add completion for 'git clean' Add completions for all long options specified in the docs --dry-run --quiet Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1474525283..97ece7d8e5 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -667,6 +667,20 @@ _git_cherry_pick () esac } +_git_clean () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--dry-run --quiet" + return + ;; + esac + COMPREPLY=() +} + _git_clone () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1523,6 +1537,7 @@ _git () checkout) _git_checkout ;; cherry) _git_cherry ;; cherry-pick) _git_cherry_pick ;; + clean) _git_clean ;; clone) _git_clone ;; commit) _git_commit ;; config) _git_config ;; -- cgit v1.2.3 From 5dad868b26919b03dd78fba182a152224a904d3a Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:33 -0600 Subject: bash completion: Add completion for 'git init' Add completions for all long options specified in the docs --quiet --bare --template= --shared --shared={false|true|umask|group|all|world|everybody} Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 97ece7d8e5..a6d8d0c03e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -865,6 +865,24 @@ _git_help () __gitcomp "$(__git_all_commands)" } +_git_init () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --shared=*) + __gitcomp " + false true umask group all world everybody + " "" "${cur##--shared=}" + return + ;; + --*) + __gitcomp "--quiet --bare --template= --shared --shared=" + return + ;; + esac + COMPREPLY=() +} + _git_ls_remote () { __gitcomp "$(__git_remotes)" @@ -1548,6 +1566,7 @@ _git () gc) _git_gc ;; grep) _git_grep ;; help) _git_help ;; + init) _git_init ;; log) _git_log ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; -- cgit v1.2.3 From a6c2be244f45432617944e684156556482fb11b6 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:34 -0600 Subject: bash completion: Add completion for 'git revert' Add completions for all long options specified in the docs --edit --mainline --no-edit --no-commit --signoff Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a6d8d0c03e..265842712e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1287,6 +1287,18 @@ _git_reset () __gitcomp "$(__git_refs)" } +_git_revert () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--edit --mainline --no-edit --no-commit --signoff" + return + ;; + esac + COMPREPLY=() +} + _git_rm () { __git_has_doubledash && return @@ -1578,6 +1590,7 @@ _git () rebase) _git_rebase ;; remote) _git_remote ;; reset) _git_reset ;; + revert) _git_revert ;; rm) _git_rm ;; send-email) _git_send_email ;; shortlog) _git_shortlog ;; -- cgit v1.2.3 From 95d43780cff575cfffeea881464c77f84f8d7244 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:35 -0600 Subject: bash completion: More completions for 'git stash' Add branch subcommand to completions and USAGE for git-stash.sh. Complete stash names for show, apply, drop, pop, and branch. Add "--index" long option for apply. Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 265842712e..49a03c314e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1371,7 +1371,7 @@ _git_show_branch () _git_stash () { - local subcommands='save list show apply clear drop pop create' + local subcommands='save list show apply clear drop pop create branch' local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" @@ -1381,6 +1381,16 @@ _git_stash () save,--*) __gitcomp "--keep-index" ;; + apply,--*) + __gitcomp "--index" + ;; + show,--*|apply,--*|drop,--*|pop,--*|branch,--*) + COMPREPLY=() + ;; + show,*|apply,*|drop,*|pop,*|branch,*) + __gitcomp "$(git --git-dir="$(__gitdir)" stash list \ + | sed -n -e 's/:.*//p')" + ;; *) COMPREPLY=() ;; -- cgit v1.2.3 From b3191ce2d5a77c17e5a236823f2dc7393e440171 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:36 -0600 Subject: bash completion: Add completion for 'git archive' Add completions for all long options specified in the docs --format= --list --verbose --prefix= --remote= --exec= The --format= long option can be completed with available formats and the --remote= can be completed with defined remote repositories. Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 49a03c314e..3209e5c4b1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -561,6 +561,29 @@ _git_add () COMPREPLY=() } +_git_archive () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --format=*) + __gitcomp "$(git archive --list)" "" "${cur##--format=}" + return + ;; + --remote=*) + __gitcomp "$(__git_remotes)" "" "${cur##--remote=}" + return + ;; + --*) + __gitcomp " + --format= --list --verbose + --prefix= --remote= --exec= + " + return + ;; + esac + __git_complete_file +} + _git_bisect () { __git_has_doubledash && return @@ -1571,6 +1594,7 @@ _git () am) _git_am ;; add) _git_add ;; apply) _git_apply ;; + archive) _git_archive ;; bisect) _git_bisect ;; bundle) _git_bundle ;; branch) _git_branch ;; -- cgit v1.2.3 From b1bc1494edb1775e94a159dea67908b27c29017f Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:37 -0600 Subject: bash completion: Add completion for 'git ls-files' Add completions for all long options specified in the docs --cached --deleted --modified --others --ignored --stage --directory --no-empty-directory --unmerged --killed --exclude= --exclude-from= --exclude-per-directory= --exclude-standard --error-unmatch --with-tree= --full-name --abbrev --ignored --exclude-per-directory Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3209e5c4b1..7a7bc99955 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -906,6 +906,26 @@ _git_init () COMPREPLY=() } +_git_ls_files () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --deleted --modified --others --ignored + --stage --directory --no-empty-directory --unmerged + --killed --exclude= --exclude-from= + --exclude-per-directory= --exclude-standard + --error-unmatch --with-tree= --full-name + --abbrev --ignored --exclude-per-directory + " + return + ;; + esac + COMPREPLY=() +} + _git_ls_remote () { __gitcomp "$(__git_remotes)" @@ -1614,6 +1634,7 @@ _git () help) _git_help ;; init) _git_init ;; log) _git_log ;; + ls-files) _git_ls_files ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; merge) _git_merge;; -- cgit v1.2.3 From 1127c51cfe09f0d7d26869e7a895190214ce049a Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:38 -0600 Subject: bash completion: Add completion for 'git mv' Add completions for all long options specified in the docs --dry-run Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7a7bc99955..3396e35d76 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1006,6 +1006,18 @@ _git_merge_base () __gitcomp "$(__git_refs)" } +_git_mv () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--dry-run" + return + ;; + esac + COMPREPLY=() +} + _git_name_rev () { __gitcomp "--tags --all --stdin" @@ -1639,6 +1651,7 @@ _git () ls-tree) _git_ls_tree ;; merge) _git_merge;; merge-base) _git_merge_base ;; + mv) _git_mv ;; name-rev) _git_name_rev ;; pull) _git_pull ;; push) _git_push ;; -- cgit v1.2.3 From 5a7ebd4faab1430a6d4daef0636345709139e3b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Wed, 6 Aug 2008 17:45:23 +0200 Subject: bash: remove redundant check for 'git stash apply' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will never trigger anyway because of the first check, and even if it would, it would not offer the command line option. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3396e35d76..78189c1b7b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1439,7 +1439,7 @@ _git_stash () apply,--*) __gitcomp "--index" ;; - show,--*|apply,--*|drop,--*|pop,--*|branch,--*) + show,--*|drop,--*|pop,--*|branch,--*) COMPREPLY=() ;; show,*|apply,*|drop,*|pop,*|branch,*) -- cgit v1.2.3 From 2318121babea786390f51796bfce17088c3420ee Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:24 +0100 Subject: Create a specific version of the read_pipe_lines command for p4 invocations This will make it easier to isolate changes to how 'p4' is invoked (whether with parameters or not, etc.). Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6ae0429c2d..fc2a60dfee 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -57,6 +57,13 @@ def read_pipe_lines(c): return val +def p4_read_pipe_lines(c): + """Specifically invoke p4 on the command supplied. """ + real_cmd = "%s %s" % ("p4", c) + if verbose: + print real_cmd + return read_pipe_lines(real_cmd) + def system(cmd): if verbose: sys.stderr.write("executing %s\n" % cmd) -- cgit v1.2.3 From b340fa43017437988e233ed4fd8dc00042614071 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:25 +0100 Subject: Utilise the new 'p4_read_pipe_lines' command Now that we have the new command, we can utilise it and then eventually, isolate any changes required to the one place. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index fc2a60dfee..3deaa42559 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -371,7 +371,7 @@ def originP4BranchesExist(): def p4ChangesForPaths(depotPaths, changeRange): assert depotPaths - output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, changeRange) + output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange) for p in depotPaths])) changes = [] @@ -519,7 +519,7 @@ class P4Submit(Command): # remove lines in the Files section that show changes to files outside the depot path we're committing into template = "" inFilesSection = False - for line in read_pipe_lines("p4 change -o"): + for line in p4_read_pipe_lines("change -o"): if line.endswith("\r\n"): line = line[:-2] + "\n" if inFilesSection: -- cgit v1.2.3 From bf9320f1512d7ad4a17a64cfe5a593bba5037b3e Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:26 +0100 Subject: Have a command that specifically invokes 'p4' (via system) Similiar to our 'p4_read_pipe_lines' command, we can isolate specific changes to the invocation method in the one location with this change. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3deaa42559..08acd517ba 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -70,6 +70,13 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) +def p4_system(cmd): + """Specifically invoke p4 as the system command. """ + real_cmd = "%s %s" % ("p4", cmd) + if verbose: + print real_cmd + return system(real_cmd) + def isP4Exec(kind): """Determine if a Perforce 'kind' should have execute permission -- cgit v1.2.3 From 87b611d5fd518e4754e599bc2f348f83db410c56 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:27 +0100 Subject: Utilise the new 'p4_system' function. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 08acd517ba..2ed36ecd6b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -98,7 +98,7 @@ def setP4ExecBit(file, mode): if p4Type[-1] == "+": p4Type = p4Type[0:-1] - system("p4 reopen -t %s %s" % (p4Type, file)) + p4_system("reopen -t %s %s" % (p4Type, file)) def getP4OpenedType(file): # Returns the perforce file type for the given file. @@ -561,7 +561,7 @@ class P4Submit(Command): modifier = diff['status'] path = diff['src'] if modifier == "M": - system("p4 edit \"%s\"" % path) + p4_system("edit \"%s\"" % path) if isModeExecChanged(diff['src_mode'], diff['dst_mode']): filesToChangeExecBit[path] = diff['dst_mode'] editedFiles.add(path) @@ -576,8 +576,8 @@ class P4Submit(Command): filesToAdd.remove(path) elif modifier == "R": src, dest = diff['src'], diff['dst'] - system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) - system("p4 edit \"%s\"" % (dest)) + p4_system("integrate -Dt \"%s\" \"%s\"" % (src, dest)) + p4_system("edit \"%s\"" % (dest)) if isModeExecChanged(diff['src_mode'], diff['dst_mode']): filesToChangeExecBit[dest] = diff['dst_mode'] os.unlink(dest) @@ -601,7 +601,7 @@ class P4Submit(Command): if response == "s": print "Skipping! Good luck with the next patches..." for f in editedFiles: - system("p4 revert \"%s\"" % f); + p4_system("revert \"%s\"" % f); for f in filesToAdd: system("rm %s" %f) return @@ -624,10 +624,10 @@ class P4Submit(Command): system(applyPatchCmd) for f in filesToAdd: - system("p4 add \"%s\"" % f) + p4_system("add \"%s\"" % f) for f in filesToDelete: - system("p4 revert \"%s\"" % f) - system("p4 delete \"%s\"" % f) + p4_system("revert \"%s\"" % f) + p4_system("delete \"%s\"" % f) # Set/clear executable bits for f in filesToChangeExecBit.keys(): @@ -728,7 +728,7 @@ class P4Submit(Command): os.chdir(self.clientPath) print "Syncronizing p4 checkout..." - system("p4 sync ...") + p4_system("sync ...") self.check() -- cgit v1.2.3 From 21a50753852cb51b120ec9933416daa6cea6d184 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:28 +0100 Subject: Add a single command that will be used to construct the 'p4' command Rather than having three locations where the 'p4' command is built up, refactor this into the one place. This will, eventually, allow us to have one place where we modify the evironment or pass extra command-line options to the 'p4' binary. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2ed36ecd6b..b4acf7689d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -16,6 +16,17 @@ from sets import Set; verbose = False + +def p4_build_cmd(cmd): + """Build a suitable p4 command line. + + This consolidates building and returning a p4 command line into one + location. It means that hooking into the environment, or other configuration + can be done more easily. + """ + real_cmd = "%s %s" % ("p4", cmd) + return real_cmd + def die(msg): if verbose: raise Exception(msg) -- cgit v1.2.3 From ee06427aa6d975c35b63c3cd103ace43fbde062b Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:29 +0100 Subject: If we are in verbose mode, output what we are about to run (or return) Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b4acf7689d..d36b0c6bec 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -25,6 +25,8 @@ def p4_build_cmd(cmd): can be done more easily. """ real_cmd = "%s %s" % ("p4", cmd) + if verbose: + print real_cmd return real_cmd def die(msg): -- cgit v1.2.3 From 155af83491b26d958b147c93620816846343b019 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:30 +0100 Subject: Switch to using 'p4_build_cmd' Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d36b0c6bec..2b6ea74d1c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -72,9 +72,7 @@ def read_pipe_lines(c): def p4_read_pipe_lines(c): """Specifically invoke p4 on the command supplied. """ - real_cmd = "%s %s" % ("p4", c) - if verbose: - print real_cmd + real_cmd = p4_build_cmd(c) return read_pipe_lines(real_cmd) def system(cmd): @@ -85,9 +83,7 @@ def system(cmd): def p4_system(cmd): """Specifically invoke p4 as the system command. """ - real_cmd = "%s %s" % ("p4", cmd) - if verbose: - print real_cmd + real_cmd = p4_build_cmd(cmd) return system(real_cmd) def isP4Exec(kind): @@ -172,7 +168,7 @@ def isModeExecChanged(src_mode, dst_mode): return isModeExec(src_mode) != isModeExec(dst_mode) def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): - cmd = "p4 -G %s" % cmd + cmd = p4_build_cmd("-G %s" % (cmd)) if verbose: sys.stderr.write("Opening pipe: %s\n" % cmd) -- cgit v1.2.3 From abcaf07360357cf2e9ce4b34e44adc09bb5587f0 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:31 +0100 Subject: If the user has configured various parameters, use them. Some repositories require authentication and access to certain hosts. Allow git-p4 to pull this information from the configuration Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2b6ea74d1c..a927e50b25 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -24,7 +24,29 @@ def p4_build_cmd(cmd): location. It means that hooking into the environment, or other configuration can be done more easily. """ - real_cmd = "%s %s" % ("p4", cmd) + real_cmd = "%s " % "p4" + + user = gitConfig("git-p4.user") + if len(user) > 0: + real_cmd += "-u %s " % user + + password = gitConfig("git-p4.password") + if len(password) > 0: + real_cmd += "-P %s " % password + + port = gitConfig("git-p4.port") + if len(port) > 0: + real_cmd += "-p %s " % port + + host = gitConfig("git-p4.host") + if len(host) > 0: + real_cmd += "-h %s " % host + + client = gitConfig("git-p4.client") + if len(client) > 0: + real_cmd += "-c %s " % client + + real_cmd += "%s" % (cmd) if verbose: print real_cmd return real_cmd -- cgit v1.2.3 From 3cafb7d8ce63effe6bf477182d431ecb4470eded Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:32 +0100 Subject: Consistently use 'git-p4' for the configuration entries Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a927e50b25..6c64224b77 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1444,7 +1444,7 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch): system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) - if self.useClientSpec or gitConfig("p4.useclientspec") == "true": + if self.useClientSpec or gitConfig("git-p4.useclientspec") == "true": self.getClientSpec() # TODO: should always look at previous commits, -- cgit v1.2.3 From bc02acfc769a1ae19772feaa7f03acfaea18a36f Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:33 +0100 Subject: Move git-p4.syncFromOrigin into a configuration parameters section Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4.txt | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index b16a8384bc..0896abb933 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -63,18 +63,6 @@ It is recommended to run 'git repack -a -d -f' from time to time when using incremental imports to optimally combine the individual git packs that each incremental import creates through the use of git-fast-import. - -A useful setup may be that you have a periodically updated git repository -somewhere that contains a complete import of a Perforce project. That git -repository can be used to clone the working repository from and one would -import from Perforce directly after cloning using git-p4. If the connection to -the Perforce server is slow and the working repository hasn't been synced for a -while it may be desirable to fetch changes from the origin git repository using -the efficient git protocol. git-p4 supports this setup by calling "git fetch origin" -by default if there is an origin branch. You can disable this using - - git config git-p4.syncFromOrigin false - Updating ======== @@ -140,6 +128,22 @@ Example git-p4 rebase +Configuration parameters +======================== + +git-p4.syncFromOrigin + +A useful setup may be that you have a periodically updated git repository +somewhere that contains a complete import of a Perforce project. That git +repository can be used to clone the working repository from and one would +import from Perforce directly after cloning using git-p4. If the connection to +the Perforce server is slow and the working repository hasn't been synced for a +while it may be desirable to fetch changes from the origin git repository using +the efficient git protocol. git-p4 supports this setup by calling "git fetch origin" +by default if there is an origin branch. You can disable this using: + + git config [--global] git-p4.syncFromOrigin false + Implementation Details... ========================= -- cgit v1.2.3 From b87a659635f40b5301c6b18fa5e22c72ca79b830 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:34 +0100 Subject: Put some documentation in about the parameters that have been added Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4.txt | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 0896abb933..79a22e9c10 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -131,6 +131,38 @@ Example Configuration parameters ======================== +git-p4.user ($P4USER) + +Allows you to specify the username to use to connect to the Perforce repository. + + git config [--global] git-p4.user public + +git-p4.password ($P4PASS) + +Allows you to specify the password to use to connect to the Perforce repository. +Warning this password will be visible on the command-line invocation of the p4 binary. + + git config [--global] git-p4.password public1234 + +git-p4.port ($P4PORT) + +Specify the port to be used to contact the Perforce server. As this will be passed +directly to the p4 binary, it may be in the format host:port as well. + + git config [--global] git-p4.port codes.zimbra.com:2666 + +git-p4.host ($P4HOST) + +Specify the host to contact for a Perforce repository. + + git config [--global] git-p4.host perforce.example.com + +git-p4.client ($P4CLIENT) + +Specify the client name to use + + git config [--global] git-p4.client public-view + git-p4.syncFromOrigin A useful setup may be that you have a periodically updated git repository -- cgit v1.2.3 From 5b5aa22f00c315021ff58450f18134b20dfd5abd Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:35 +0100 Subject: Put in the two other configuration elements found in the source I am not entirely clear what these parameters do but felt it useful to call them out in the documentation. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4.txt | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 79a22e9c10..ac551d45f1 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -163,6 +163,10 @@ Specify the client name to use git config [--global] git-p4.client public-view +git-p4.allowSubmit + + git config [--global] git-p4.allowSubmit false + git-p4.syncFromOrigin A useful setup may be that you have a periodically updated git repository @@ -176,6 +180,10 @@ by default if there is an origin branch. You can disable this using: git config [--global] git-p4.syncFromOrigin false +git-p4.useclientspec + + git config [--global] git-p4.useclientspec false + Implementation Details... ========================= -- cgit v1.2.3 From 7950659dc9ef7f2b50b18010622299c508bfdfc3 Mon Sep 17 00:00:00 2001 From: Eric Raible Date: Thu, 14 Aug 2008 10:12:54 -0700 Subject: bash completion: 'git apply' should use 'fix' not 'strip' Bring completion up to date with the man page. Signed-off-by: Eric Raible Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 78189c1b7b..d1afe96e1c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -501,7 +501,7 @@ __git_has_doubledash () return 1 } -__git_whitespacelist="nowarn warn error error-all strip" +__git_whitespacelist="nowarn warn error error-all fix" _git_am () { -- cgit v1.2.3 From b4c72162f6612ce335af79ef19c5ae16f5585e67 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Thu, 14 Aug 2008 16:41:10 -0600 Subject: bash completion: Add completion for 'git mergetool' The --tool= long option to "git mergetool" can be completed with: kdiff3 tkdiff meld xxdiff emerge vimdiff gvimdiff ecmerge opendiff Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d1afe96e1c..2f8036d1d9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1001,6 +1001,25 @@ _git_merge () __gitcomp "$(__git_refs)" } +_git_mergetool () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --tool=*) + __gitcomp " + kdiff3 tkdiff meld xxdiff emerge + vimdiff gvimdiff ecmerge opendiff + " "" "${cur##--tool=}" + return + ;; + --*) + __gitcomp "--tool=" + return + ;; + esac + COMPREPLY=() +} + _git_merge_base () { __gitcomp "$(__git_refs)" @@ -1650,6 +1669,7 @@ _git () ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; merge) _git_merge;; + mergetool) _git_mergetool;; merge-base) _git_merge_base ;; mv) _git_mv ;; name-rev) _git_name_rev ;; -- cgit v1.2.3 From 5a13c8f6f7ef7463ddaa3dd7144bf66af4fcd9be Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Thu, 14 Aug 2008 16:41:11 -0600 Subject: bash completion: Add '--merge' long option for 'git log' Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2f8036d1d9..c0bf7aade6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -972,6 +972,7 @@ _git_log () --decorate --diff-filter= --color-words --walk-reflogs --parents --children --full-history + --merge " return ;; -- cgit v1.2.3 From d9429194f6e30e1f6f46a286217cd88972e1c83b Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Thu, 14 Aug 2008 23:40:38 +0100 Subject: Add p4 read_pipe and write_pipe wrappers Two additional wrappers to cover 3 places where we utilise p4 in piped form. Found by Tor Arvid Lund. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6c64224b77..3e9df70f29 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -69,6 +69,10 @@ def write_pipe(c, str): return val +def p4_write_pipe(c, str): + real_cmd = p4_build_cmd(c) + return write_pipe(c, str) + def read_pipe(c, ignore_error=False): if verbose: sys.stderr.write('Reading pipe: %s\n' % c) @@ -80,6 +84,9 @@ def read_pipe(c, ignore_error=False): return val +def p4_read_pipe(c, ignore_error=False): + real_cmd = p4_build_cmd(c) + return read_pipe(real_cmd, ignore_error) def read_pipe_lines(c): if verbose: -- cgit v1.2.3 From a7d3ef9d099ab00a19595bc3ca8abdc1fc6ff35d Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Thu, 14 Aug 2008 23:40:39 +0100 Subject: Utilise our new p4_read_pipe and p4_write_pipe wrappers Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3e9df70f29..12fa9d3fd8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -141,7 +141,7 @@ def setP4ExecBit(file, mode): def getP4OpenedType(file): # Returns the perforce file type for the given file. - result = read_pipe("p4 opened %s" % file) + result = p4_read_pipe("opened %s" % file) match = re.match(".*\((.+)\)\r?$", result) if match: return match.group(1) @@ -681,7 +681,7 @@ class P4Submit(Command): submitTemplate = self.prepareLogMessage(template, logMessage) if os.environ.has_key("P4DIFF"): del(os.environ["P4DIFF"]) - diff = read_pipe("p4 diff -du ...") + diff = p4_read_pipe("diff -du ...") newdiff = "" for newFile in filesToAdd: @@ -719,7 +719,7 @@ class P4Submit(Command): if self.isWindows: submitTemplate = submitTemplate.replace("\r\n", "\n") - write_pipe("p4 submit -i", submitTemplate) + p4_write_pipe("submit -i", submitTemplate) else: fileName = "submit.txt" file = open(fileName, "w+") -- cgit v1.2.3 From 2946cccfdf2fba591b6af61ad6e658bb927832af Mon Sep 17 00:00:00 2001 From: Marcus Griep Date: Fri, 15 Aug 2008 13:59:28 -0400 Subject: bash-completion: Add non-command git help files to bash-completion Git allows access to the gitattributes man page via `git help attributes`, but this is not discoverable via the bash-completion mechanism. This patch adds all current non-command man pages to the completion candidate list. Signed-off-by: Marcus Griep Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c0bf7aade6..158b912841 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -885,7 +885,11 @@ _git_help () return ;; esac - __gitcomp "$(__git_all_commands)" + __gitcomp "$(__git_all_commands) + attributes cli core-tutorial cvs-migration + diffcore gitk glossary hooks ignore modules + repository-layout tutorial tutorial-2 + " } _git_init () -- cgit v1.2.3 From 053fd0c1c3da20474c4ff175c56ea4c1d6eeda11 Mon Sep 17 00:00:00 2001 From: Robert Blum Date: Fri, 1 Aug 2008 12:50:03 -0700 Subject: git-p4: chdir now properly sets PWD environment variable in msysGit P4 on Windows expects the PWD environment variable to be set to the current working dir, but os.chdir in python doesn't do so. Signed-off-by: Robert Blum Acked-by: Simon Hausmann Acked-by: Han-Wen Nienhuys Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6ae0429c2d..3f2303dcf0 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -16,6 +16,11 @@ from sets import Set; verbose = False +def chdir(dir): + if os.name == 'nt': + os.environ['PWD']=dir + os.chdir(dir) + def die(msg): if verbose: raise Exception(msg) @@ -712,7 +717,7 @@ class P4Submit(Command): print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath) self.oldWorkingDirectory = os.getcwd() - os.chdir(self.clientPath) + chdir(self.clientPath) print "Syncronizing p4 checkout..." system("p4 sync ...") @@ -732,7 +737,7 @@ class P4Submit(Command): if len(commits) == 0: print "All changes applied!" - os.chdir(self.oldWorkingDirectory) + chdir(self.oldWorkingDirectory) sync = P4Sync() sync.run([]) @@ -1670,7 +1675,7 @@ class P4Clone(P4Sync): print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination) if not os.path.exists(self.cloneDestination): os.makedirs(self.cloneDestination) - os.chdir(self.cloneDestination) + chdir(self.cloneDestination) system("git init") self.gitdir = os.getcwd() + "/.git" if not P4Sync.run(self, depotPaths): @@ -1782,7 +1787,7 @@ def main(): if os.path.exists(cmd.gitdir): cdup = read_pipe("git rev-parse --show-cdup").strip() if len(cdup) > 0: - os.chdir(cdup); + chdir(cdup); if not isValidGitDir(cmd.gitdir): if isValidGitDir(cmd.gitdir + "/.git"): -- cgit v1.2.3 From 25b3d4d6f39d70c4d46dee48570ae7aeeb4a6b58 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Aug 2008 14:13:42 -0700 Subject: completion: find out supported merge strategies correctly "git-merge" is a binary executable these days, and looking for assignment to $all_strategies variable with grep/sed does not work well. When asked for an unknown strategy, pre-1.6.0 and post-1.6.0 "git merge" commands respectively say: $ $HOME/git-snap-v1.5.6.5/bin/git merge -s help available strategies are: recur recursive octopus resolve stupid ours subtree $ $HOME/git-snap-v1.6.0/bin/git merge -s help Could not find merge strategy 'help'. Available strategies are: recursive octopus resolve ours subtree. both on their standard error stream. We can use this to learn what strategies are supported. The sed script is written in such a way that it catches both old and new message styles ("Available" vs "available", and the full stop at the end). It also allows future versions of "git merge" to line-wrap the list of strategies, and add extra comments, like this: $ $HOME/git-snap-v1.6.1/bin/git merge -s help Could not find merge strategy 'help'. Available strategies are: blame recursive octopus resolve ours subtree. Also you have custom strategies: theirs Make sure you spell strategy names correctly. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 158b912841..a31004088a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -271,15 +271,17 @@ __git_merge_strategies () echo "$__git_merge_strategylist" return fi - sed -n "/^all_strategies='/{ - s/^all_strategies='// - s/'// + git merge -s help 2>&1 | + sed -n -e '/[Aa]vailable strategies are: /,/^$/{ + s/\.$// + s/.*:// + s/^[ ]*// + s/[ ]*$// p - q - }" "$(git --exec-path)/git-merge" + }' } __git_merge_strategylist= -__git_merge_strategylist="$(__git_merge_strategies 2>/dev/null)" +__git_merge_strategylist=$(__git_merge_strategies 2>/dev/null) __git_complete_file () { -- cgit v1.2.3 From f135aacb5ae30b54bac0dde7462b532d19e4c0d6 Mon Sep 17 00:00:00 2001 From: Eric Raible Date: Fri, 22 Aug 2008 10:25:06 -0700 Subject: Completion: add missing '=' for 'diff --diff-filter' Signed-off-by: Eric Raible Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a31004088a..89858c237e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -771,7 +771,7 @@ _git_diff () __gitcomp "--cached --stat --numstat --shortstat --summary --patch-with-stat --name-only --name-status --color --no-color --color-words --no-renames --check - --full-index --binary --abbrev --diff-filter + --full-index --binary --abbrev --diff-filter= --find-copies-harder --pickaxe-all --pickaxe-regex --text --ignore-space-at-eol --ignore-space-change --ignore-all-space --exit-code --quiet --ext-diff -- cgit v1.2.3 From 893d340f2c735dc85b9360556ccd46cc0bf27fc0 Mon Sep 17 00:00:00 2001 From: Tor Arvid Lund Date: Thu, 21 Aug 2008 23:11:40 +0200 Subject: git-p4: Fix one-liner in p4_write_pipe function. The function built a p4 command string via the p4_build_cmd function, but ignored the result. Signed-off-by: Tor Arvid Lund Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f9865b444f..46136d49bf 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -76,7 +76,7 @@ def write_pipe(c, str): def p4_write_pipe(c, str): real_cmd = p4_build_cmd(c) - return write_pipe(c, str) + return write_pipe(real_cmd, str) def read_pipe(c, ignore_error=False): if verbose: -- cgit v1.2.3 From f5f7e4a18cf656747be8f8447ca304ddf716c742 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 24 Aug 2008 16:12:23 +0200 Subject: Clean up the git-p4 documentation This patch massages the documentation a bit for improved readability and cleans it up from outdated options/commands. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4.txt | 69 +++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 31 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index ac551d45f1..49b335921a 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -3,14 +3,16 @@ git-p4 - Perforce <-> Git converter using git-fast-import Usage ===== -git-p4 supports two main modes: Importing from Perforce to a Git repository is -done using "git-p4 sync" or "git-p4 rebase". Submitting changes from Git back -to Perforce is done using "git-p4 submit". +git-p4 can be used in two different ways: + +1) To import changes from Perforce to a Git repository, using "git-p4 sync". + +2) To submit changes from Git back to Perforce, using "git-p4 submit". Importing ========= -You can simply start with +Simply start with git-p4 clone //depot/path/project @@ -18,11 +20,18 @@ or git-p4 clone //depot/path/project myproject -This will create an empty git repository in a subdirectory called "project" (or -"myproject" with the second command), import the head revision from the -specified perforce path into a git "p4" branch (remotes/p4 actually), create a -master branch off it and check it out. If you want the entire history (not just -the head revision) then you can simply append a "@all" to the depot path: +This will: + +1) Create an empty git repository in a subdirectory called "project" (or +"myproject" with the second command) + +2) Import the head revision from the given Perforce path into a git branch +called "p4" (remotes/p4 actually) + +3) Create a master branch based on it and check it out. + +If you want the entire history (not just the head revision) then you can simply +append a "@all" to the depot path: git-p4 clone //depot/project/main@all myproject @@ -37,31 +46,40 @@ If you want more control you can also use the git-p4 sync command directly: This will import the current head revision of the specified depot path into a "remotes/p4/master" branch of your git repository. You can use the ---branch=mybranch option to use a different branch. +--branch=mybranch option to import into a different branch. -If you want to import the entire history of a given depot path just use +If you want to import the entire history of a given depot path simply use: git-p4 sync //path/in/depot@all + +Note: + To achieve optimal compression you may want to run 'git repack -a -d -f' after a big import. This may take a while. -Support for Perforce integrations is still work in progress. Don't bother -trying it unless you want to hack on it :) - Incremental Imports =================== -After an initial import you can easily synchronize your git repository with -newer changes from the Perforce depot by just calling +After an initial import you can continue to synchronize your git repository +with newer changes from the Perforce depot by just calling git-p4 sync in your git repository. By default the "remotes/p4/master" branch is updated. -It is recommended to run 'git repack -a -d -f' from time to time when using -incremental imports to optimally combine the individual git packs that each -incremental import creates through the use of git-fast-import. +Advanced Setup +============== + +Suppose you have a periodically updated git repository somewhere, containing a +complete import of a Perforce project. This repository can be cloned and used +with git-p4. When updating the cloned repository with the "sync" command, +git-p4 will try to fetch changes from the original repository first. The git +protocol used with this is usually faster than importing from Perforce +directly. + +This behaviour can be disabled by setting the "git-p4.syncFromOrigin" git +configuration variable to "false". Updating ======== @@ -79,7 +97,7 @@ Submitting ========== git-p4 has support for submitting changes from a git repository back to the -Perforce depot. This requires a Perforce checkout separate to your git +Perforce depot. This requires a Perforce checkout separate from your git repository. To submit all changes that are in the current git branch but not in the "p4" branch (or "origin" if "p4" doesn't exist) simply call @@ -97,17 +115,6 @@ continue importing the remaining changes with git-p4 submit --continue -After submitting you should sync your perforce import branch ("p4" or "origin") -from Perforce using git-p4's sync command. - -If you have changes in your working directory that you haven't committed into -git yet but that you want to commit to Perforce directly ("quick fixes") then -you do not have to go through the intermediate step of creating a git commit -first but you can just call - - git-p4 submit --direct - - Example ======= -- cgit v1.2.3 From cdc7e388da47b66fe11c92ed7698ec233ce70635 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 27 Aug 2008 09:30:29 +0200 Subject: Make it possible to abort the submission of a change to Perforce Currently it is not possible to skip the submission of a change to Perforce when running git-p4 submit. This patch compares the modification time before and after the submit editor invokation and offers a prompt for skipping if the submit template file was not saved. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 46136d49bf..c1d24b38f3 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -708,6 +708,7 @@ class P4Submit(Command): newdiff = newdiff.replace("\n", "\r\n") tmpFile.write(submitTemplate + separatorLine + diff + newdiff) tmpFile.close() + mtime = os.stat(fileName).st_mtime defaultEditor = "vi" if platform.system() == "Windows": defaultEditor = "notepad" @@ -716,15 +717,29 @@ class P4Submit(Command): else: editor = os.environ.get("EDITOR", defaultEditor); system(editor + " " + fileName) - tmpFile = open(fileName, "rb") - message = tmpFile.read() - tmpFile.close() - os.remove(fileName) - submitTemplate = message[:message.index(separatorLine)] - if self.isWindows: - submitTemplate = submitTemplate.replace("\r\n", "\n") - p4_write_pipe("submit -i", submitTemplate) + response = "y" + if os.stat(fileName).st_mtime <= mtime: + response = "x" + while response != "y" and response != "n": + response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ") + + if response == "y": + tmpFile = open(fileName, "rb") + message = tmpFile.read() + tmpFile.close() + submitTemplate = message[:message.index(separatorLine)] + if self.isWindows: + submitTemplate = submitTemplate.replace("\r\n", "\n") + p4_write_pipe("submit -i", submitTemplate) + else: + for f in editedFiles: + p4_system("revert \"%s\"" % f); + for f in filesToAdd: + p4_system("revert \"%s\"" % f); + system("rm %s" %f) + + os.remove(fileName) else: fileName = "submit.txt" file = open(fileName, "w+") -- cgit v1.2.3 From 1b0f7978ddb9e2ed4437ce68a4b82ab831288a41 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Thu, 28 Aug 2008 10:57:55 +0200 Subject: bash-completion: Add all submodule subcommands to the completion list Signed-off-by: Matthias Kestenholz Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 89858c237e..4f64f8ab7d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1483,7 +1483,7 @@ _git_submodule () { __git_has_doubledash && return - local subcommands="add status init update" + local subcommands="add status init update summary foreach sync" if [ -z "$(__git_find_subcommand "$subcommands")" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in -- cgit v1.2.3 From e990501312e22cfa910d88dc7143bc4eb3632ae1 Mon Sep 17 00:00:00 2001 From: Tor Arvid Lund Date: Thu, 28 Aug 2008 00:36:12 +0200 Subject: git-p4: Fix checkout bug when using --import-local. When this option is passed to git p4 clone, the checkout at the end would previously fail. This patch fixes it by optionally creating the master branch from refs/heads/p4/master, which is the correct one for this option. Signed-off-by: Tor Arvid Lund Acked-By: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c1d24b38f3..2216cacba7 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1748,8 +1748,12 @@ class P4Clone(P4Sync): if not P4Sync.run(self, depotPaths): return False if self.branch != "master": - if gitBranchExists("refs/remotes/p4/master"): - system("git branch master refs/remotes/p4/master") + if self.importIntoRemotes: + masterbranch = "refs/remotes/p4/master" + else: + masterbranch = "refs/heads/p4/master" + if gitBranchExists(masterbranch): + system("git branch master %s" % masterbranch) system("git checkout -f") else: print "Could not detect main branch. No checkout/master branch created." -- cgit v1.2.3 From ff2549dc9af3fffa8a8285418601d9eab94de7b7 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 26 Aug 2008 19:11:44 +0200 Subject: bash completion: Hide more plumbing commands git still shows way too many commands, some of them are clearly plumbing. This patch hides the plumbing commands liberally (that is, in special cases, users still might want to call one of the hidden commands, a *normal* workflow should never involve these, though - and if it does, we have a UI problem anyway). Signed-off-by: Petr Baudis Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 89858c237e..1154ae1ac8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -386,7 +386,9 @@ __git_porcelain_commands () cat-file) : plumbing;; check-attr) : plumbing;; check-ref-format) : plumbing;; + checkout-index) : plumbing;; commit-tree) : plumbing;; + count-objects) : infrequent;; cvsexportcommit) : export;; cvsimport) : import;; cvsserver) : daemon;; @@ -395,6 +397,7 @@ __git_porcelain_commands () diff-index) : plumbing;; diff-tree) : plumbing;; fast-import) : import;; + fast-export) : export;; fsck-objects) : plumbing;; fetch-pack) : plumbing;; fmt-merge-msg) : plumbing;; @@ -404,6 +407,10 @@ __git_porcelain_commands () index-pack) : plumbing;; init-db) : deprecated;; local-fetch) : plumbing;; + lost-found) : infrequent;; + ls-files) : plumbing;; + ls-remote) : plumbing;; + ls-tree) : plumbing;; mailinfo) : plumbing;; mailsplit) : plumbing;; merge-*) : plumbing;; @@ -428,6 +435,7 @@ __git_porcelain_commands () runstatus) : plumbing;; sh-setup) : internal;; shell) : daemon;; + show-ref) : plumbing;; send-pack) : plumbing;; show-index) : plumbing;; ssh-*) : transport;; @@ -442,6 +450,8 @@ __git_porcelain_commands () upload-archive) : plumbing;; upload-pack) : plumbing;; write-tree) : plumbing;; + var) : infrequent;; + verify-pack) : infrequent;; verify-tag) : plumbing;; *) echo $i;; esac -- cgit v1.2.3 From aa5735bed459be71473b4fda1a9a026f798acf76 Mon Sep 17 00:00:00 2001 From: Eric Raible Date: Wed, 10 Sep 2008 17:40:20 -0700 Subject: completion: git commit should list --interactive Signed-off-by: Eric Raible Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1154ae1ac8..3bc45f6b47 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -750,7 +750,7 @@ _git_commit () --*) __gitcomp " --all --author= --signoff --verify --no-verify - --edit --amend --include --only + --edit --amend --include --only --interactive " return esac -- cgit v1.2.3 From 05e8b3d6f43bd28aef85ec6e7658536510d6f959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sat, 20 Sep 2008 00:15:25 +0200 Subject: bash: use for-each-ref format 'refname:short' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using this format simplifies the code for completing refs and (in some cases) improves performance significantly. For repositories like the current git.git (with more than 200 refs) there is no real performance difference, but for a repository with 2000 refs the total time needed to complete the refs is reduced by ~25% (from around 400ms to around 305ms). Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d3fb6ae507..fccb499850 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -154,11 +154,8 @@ __git_heads () { local cmd i is_hash=y dir="$(__gitdir "$1")" if [ -d "$dir" ]; then - for i in $(git --git-dir="$dir" \ - for-each-ref --format='%(refname)' \ - refs/heads ); do - echo "${i#refs/heads/}" - done + git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ + refs/heads return fi for i in $(git ls-remote "$1" 2>/dev/null); do @@ -175,11 +172,8 @@ __git_tags () { local cmd i is_hash=y dir="$(__gitdir "$1")" if [ -d "$dir" ]; then - for i in $(git --git-dir="$dir" \ - for-each-ref --format='%(refname)' \ - refs/tags ); do - echo "${i#refs/tags/}" - done + git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ + refs/tags return fi for i in $(git ls-remote "$1" 2>/dev/null); do @@ -197,16 +191,8 @@ __git_refs () local cmd i is_hash=y dir="$(__gitdir "$1")" if [ -d "$dir" ]; then if [ -e "$dir/HEAD" ]; then echo HEAD; fi - for i in $(git --git-dir="$dir" \ - for-each-ref --format='%(refname)' \ - refs/tags refs/heads refs/remotes); do - case "$i" in - refs/tags/*) echo "${i#refs/tags/}" ;; - refs/heads/*) echo "${i#refs/heads/}" ;; - refs/remotes/*) echo "${i#refs/remotes/}" ;; - *) echo "$i" ;; - esac - done + git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ + refs/tags refs/heads refs/remotes return fi for i in $(git ls-remote "$dir" 2>/dev/null); do -- cgit v1.2.3 From 18309f4c3e00886660f15e18c1aaab2f5bc25715 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Mon, 22 Sep 2008 07:30:08 -0400 Subject: Use dashless git commands in setgitperms.perl Signed-off-by: Todd Zullinger Signed-off-by: Junio C Hamano --- contrib/hooks/setgitperms.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl index dab7c8e3a1..a577ad095f 100644 --- a/contrib/hooks/setgitperms.perl +++ b/contrib/hooks/setgitperms.perl @@ -50,7 +50,7 @@ if ((@ARGV < 0) || !GetOptions( )) { die $usage; } die $usage unless ($read_mode xor $write_mode); -my $topdir = `git-rev-parse --show-cdup` or die "\n"; chomp $topdir; +my $topdir = `git rev-parse --show-cdup` or die "\n"; chomp $topdir; my $gitdir = $topdir . '.git'; my $gitmeta = $topdir . '.gitmeta'; @@ -155,7 +155,7 @@ elsif ($read_mode) { open (OUT, ">$gitmeta.tmp") or die "Could not open $gitmeta.tmp for writing: $!\n"; } - my @files = `git-ls-files`; + my @files = `git ls-files`; my %dirs; foreach my $path (@files) { -- cgit v1.2.3 From c11c7a5db3676258512c4067c5279377811d3ab8 Mon Sep 17 00:00:00 2001 From: Nanako Shiraishi Date: Sat, 27 Sep 2008 20:44:15 +0900 Subject: Add contrib/rerere-train script This script takes a range of commits (e.g. maint..next) as its arguments, recreates merge commits in the range to prime rr-cache database. Signed-off-by: Nanako Shiraishi Signed-off-by: Shawn O. Pearce --- contrib/rerere-train.sh | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100755 contrib/rerere-train.sh (limited to 'contrib') diff --git a/contrib/rerere-train.sh b/contrib/rerere-train.sh new file mode 100755 index 0000000000..2cfe1b936b --- /dev/null +++ b/contrib/rerere-train.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# Copyright (c) 2008, Nanako Shiraishi +# Prime rerere database from existing merge commits + +me=rerere-train +USAGE="$me rev-list-args" + +SUBDIRECTORY_OK=Yes +OPTIONS_SPEC= +. git-sh-setup +require_work_tree +cd_to_toplevel + +# Remember original branch +branch=$(git symbolic-ref -q HEAD) || +original_HEAD=$(git rev-parse --verify HEAD) || { + echo >&2 "Not on any branch and no commit yet?" + exit 1 +} + +mkdir -p "$GIT_DIR/rr-cache" || exit + +git rev-list --parents "$@" | +while read commit parent1 other_parents +do + if test -z "$other_parents" + then + # Skip non-merges + continue + fi + git checkout -q "$parent1^0" + if git merge $other_parents >/dev/null 2>&1 + then + # Cleanly merges + continue + fi + if test -s "$GIT_DIR/MERGE_RR" + then + git show -s --pretty=format:"Learning from %h %s" "$commit" + git rerere + git checkout -q $commit -- . + git rerere + fi + git reset -q --hard +done + +if test -z "$branch" +then + git checkout "$original_HEAD" +else + git checkout "${branch#refs/heads/}" +fi -- cgit v1.2.3 From 1d4c4986708688c14e72a5b442a3432048aeea79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 29 Sep 2008 22:08:14 +0200 Subject: remove vim syntax highlighting in favor of upstream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As of version 7.2, vim ships with its own syntax highlighting for git commit messages, which is: 1. more comprehensive in splitting up the various components of the file 2. in accordance with the usual vim behavior for syntax highlighting (e.g., respecting b:current_syntax) 3. presumably better maintained (I have not been using what's in git's contrib/ directory for some time in favor of the upstream version) Furthermore, vim upsream also provides syntax highlighting for other git filetypes (gitconfig, rebase, send-email). This patch gets rid of our local version and just points interested parties to the upstream version. The code for auto-detecting filetypes is taken from vim's runtime/filetype.vim. Signed-off-by: SZEDER Gábor Signed-off-by: Jeff King Signed-off-by: Shawn O. Pearce --- contrib/vim/README | 38 ++++++++++++++++++++++++++++++-------- contrib/vim/syntax/gitcommit.vim | 18 ------------------ 2 files changed, 30 insertions(+), 26 deletions(-) delete mode 100644 contrib/vim/syntax/gitcommit.vim (limited to 'contrib') diff --git a/contrib/vim/README b/contrib/vim/README index 9e7881fea9..c487346eba 100644 --- a/contrib/vim/README +++ b/contrib/vim/README @@ -1,8 +1,30 @@ -To syntax highlight git's commit messages, you need to: - 1. Copy syntax/gitcommit.vim to vim's syntax directory: - $ mkdir -p $HOME/.vim/syntax - $ cp syntax/gitcommit.vim $HOME/.vim/syntax - 2. Auto-detect the editing of git commit files: - $ cat >>$HOME/.vimrc <<'EOF' - autocmd BufNewFile,BufRead COMMIT_EDITMSG set filetype=gitcommit - EOF +Syntax highlighting for git commit messages, config files, etc. is +included with the vim distribution as of vim 7.2, and should work +automatically. + +If you have an older version of vim, you can get the latest syntax +files from the vim project: + + http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/git.vim + http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitcommit.vim + http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitconfig.vim + http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitrebase.vim + http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitsendemail.vim + +To install: + + 1. Copy these files to vim's syntax directory $HOME/.vim/syntax + 2. To auto-detect the editing of various git-related filetypes: + $ cat >>$HOME/.vim/filetype.vim <<'EOF' + autocmd BufNewFile,BufRead *.git/COMMIT_EDITMSG setf gitcommit + autocmd BufNewFile,BufRead *.git/config,.gitconfig setf gitconfig + autocmd BufNewFile,BufRead git-rebase-todo setf gitrebase + autocmd BufNewFile,BufRead .msg.[0-9]* + \ if getline(1) =~ '^From.*# This line is ignored.$' | + \ setf gitsendemail | + \ endif + autocmd BufNewFile,BufRead *.git/** + \ if getline(1) =~ '^\x\{40\}\>\|^ref: ' | + \ setf git | + \ endif + EOF diff --git a/contrib/vim/syntax/gitcommit.vim b/contrib/vim/syntax/gitcommit.vim deleted file mode 100644 index 332121b40e..0000000000 --- a/contrib/vim/syntax/gitcommit.vim +++ /dev/null @@ -1,18 +0,0 @@ -syn region gitLine start=/^#/ end=/$/ -syn region gitCommit start=/^# Changes to be committed:$/ end=/^#$/ contains=gitHead,gitCommitFile -syn region gitHead contained start=/^# (.*)/ end=/^#$/ -syn region gitChanged start=/^# Changed but not updated:/ end=/^#$/ contains=gitHead,gitChangedFile -syn region gitUntracked start=/^# Untracked files:/ end=/^#$/ contains=gitHead,gitUntrackedFile - -syn match gitCommitFile contained /^#\t.*/hs=s+2 -syn match gitChangedFile contained /^#\t.*/hs=s+2 -syn match gitUntrackedFile contained /^#\t.*/hs=s+2 - -hi def link gitLine Comment -hi def link gitCommit Comment -hi def link gitChanged Comment -hi def link gitHead Comment -hi def link gitUntracked Comment -hi def link gitCommitFile Type -hi def link gitChangedFile Constant -hi def link gitUntrackedFile Constant -- cgit v1.2.3 From c33912ae2463b9e486fb016ffa9d21ff8e62984a Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Tue, 30 Sep 2008 00:36:28 +0100 Subject: Add OS X support to the pre-auto-gc example hook Signed-off-by: Jonathan del Strother Acked-by: Miklos Vajna Signed-off-by: Shawn O. Pearce --- contrib/hooks/pre-auto-gc-battery | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery index 0096f57b7e..b0a8caae9e 100644 --- a/contrib/hooks/pre-auto-gc-battery +++ b/contrib/hooks/pre-auto-gc-battery @@ -1,9 +1,9 @@ #!/bin/sh # # An example hook script to verify if you are on battery, in case you -# are running Linux. Called by git-gc --auto with no arguments. The hook -# should exit with non-zero status after issuing an appropriate message -# if it wants to stop the auto repacking. +# are running Linux or OS X. Called by git-gc --auto with no arguments. +# The hook should exit with non-zero status after issuing an appropriate +# message if it wants to stop the auto repacking. # # This hook is stored in the contrib/hooks directory. Your distribution # may have put this somewhere else. If you want to use this hook, you @@ -28,6 +28,10 @@ elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null then exit 0 elif grep -q '0x01$' /proc/apm 2>/dev/null +then + exit 0 +elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt | + grep -q "Currently drawing from 'AC Power'" then exit 0 fi -- cgit v1.2.3 From fd3a8dcbbd5972912cad44d7a5571d7606b8739e Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Sun, 28 Sep 2008 07:51:21 +0300 Subject: bash completion: Add --[no-]validate to "git send-email" Signed-off-by: Teemu Likonen Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 93f088189e..7284c3b5a8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1141,7 +1141,8 @@ _git_send_email () --no-suppress-from --no-thread --quiet --signed-off-by-cc --smtp-pass --smtp-server --smtp-server-port --smtp-ssl --smtp-user --subject - --suppress-cc --suppress-from --thread --to" + --suppress-cc --suppress-from --thread --to + --validate --no-validate" return ;; esac -- cgit v1.2.3 From 5a625b07bbff709037f26be1c393337b6d9e103f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 3 Oct 2008 21:34:49 +0200 Subject: bash: remove fetch, push, pull dashed form leftovers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't provide complation for git-commands in dashed form anymore, so there is no need to keep those cases. Signed-off-by: SZEDER Gábor Tested-by: Thomas Rast Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 36 +++++++++------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3bc45f6b47..751e273e1a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -799,14 +799,9 @@ _git_fetch () { local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[0]},$COMP_CWORD" in - git-fetch*,1) + if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" - ;; - git,2) - __gitcomp "$(__git_remotes)" - ;; - *) + else case "$cur" in *:*) local pfx="" @@ -825,8 +820,7 @@ _git_fetch () __gitcomp "$(__git_refs2 "$remote")" ;; esac - ;; - esac + fi } _git_format_patch () @@ -1063,36 +1057,25 @@ _git_pull () { local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[0]},$COMP_CWORD" in - git-pull*,1) - __gitcomp "$(__git_remotes)" - ;; - git,2) + if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" - ;; - *) + else local remote case "${COMP_WORDS[0]}" in git-pull) remote="${COMP_WORDS[1]}" ;; git) remote="${COMP_WORDS[2]}" ;; esac __gitcomp "$(__git_refs "$remote")" - ;; - esac + fi } _git_push () { local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[0]},$COMP_CWORD" in - git-push*,1) - __gitcomp "$(__git_remotes)" - ;; - git,2) + if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" - ;; - *) + else case "$cur" in *:*) local remote @@ -1116,8 +1099,7 @@ _git_push () __gitcomp "$(__git_refs)" ;; esac - ;; - esac + fi } _git_rebase () -- cgit v1.2.3 From 84ed4c5d117d72f02cc918e413b9861a9d2846d7 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Tue, 14 Oct 2008 16:42:45 +0200 Subject: Add Linux PPC support to the pre-auto-gc example hook Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hooks/pre-auto-gc-battery | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery index b0a8caae9e..1f914c94aa 100644 --- a/contrib/hooks/pre-auto-gc-battery +++ b/contrib/hooks/pre-auto-gc-battery @@ -28,6 +28,9 @@ elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null then exit 0 elif grep -q '0x01$' /proc/apm 2>/dev/null +then + exit 0 +elif grep -q "AC Power \+: 1" /proc/pmu/info 2>/dev/null then exit 0 elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt | -- cgit v1.2.3 From 6c2a6022dbc5879f5d6c267925408e484be6214a Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Fri, 19 Sep 2008 15:48:08 +0200 Subject: Typo "does not exists" when git remote update remote. --- contrib/examples/git-remote.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/examples/git-remote.perl b/contrib/examples/git-remote.perl index 36bd54c985..b17952a785 100755 --- a/contrib/examples/git-remote.perl +++ b/contrib/examples/git-remote.perl @@ -309,7 +309,7 @@ sub update_remote { } } } else { - print STDERR "Remote group $name does not exists.\n"; + print STDERR "Remote group $name does not exist.\n"; exit(1); } for (@remotes) { -- cgit v1.2.3 From 3b1eb124932772daee09419a581d418ea2d50045 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 17 Oct 2008 21:41:18 -0500 Subject: contrib: update packinfo.pl to not use dashed commands Signed-off-by: Dan McGee Signed-off-by: Junio C Hamano --- contrib/stats/packinfo.pl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl index f4a7b62cd9..be188c0f11 100755 --- a/contrib/stats/packinfo.pl +++ b/contrib/stats/packinfo.pl @@ -1,9 +1,9 @@ #!/usr/bin/perl # # This tool will print vaguely pretty information about a pack. It -# expects the output of "git-verify-pack -v" as input on stdin. +# expects the output of "git verify-pack -v" as input on stdin. # -# $ git-verify-pack -v | packinfo.pl +# $ git verify-pack -v | packinfo.pl # # This prints some full-pack statistics; currently "all sizes", "all # path sizes", "tree sizes", "tree path sizes", and "depths". @@ -20,7 +20,7 @@ # # When run as: # -# $ git-verify-pack -v | packinfo.pl -tree +# $ git verify-pack -v | packinfo.pl -tree # # the trees of objects are output along with the stats. This looks # like: @@ -43,7 +43,7 @@ # # When run as: # -# $ git-verify-pack -v | packinfo.pl -tree -filenames +# $ git verify-pack -v | packinfo.pl -tree -filenames # # it adds filenames to the tree. Getting this information is slow: # @@ -58,7 +58,7 @@ # # When run as: # -# $ git-verify-pack -v | packinfo.pl -dump +# $ git verify-pack -v | packinfo.pl -dump # # it prints out "sha1 size pathsize depth" for each sha1 in lexical # order. @@ -106,7 +106,7 @@ while () { } if ($filenames && ($tree || $dump)) { - open(NAMES, "git-name-rev --all|"); + open(NAMES, "git name-rev --all|"); while () { if (/^(\S+)\s+(.*)$/) { my ($sha1, $name) = ($1, $2); @@ -117,7 +117,7 @@ if ($filenames && ($tree || $dump)) { for my $commit (@commits) { my $name = $names{$commit}; - open(TREE, "git-ls-tree -t -r $commit|"); + open(TREE, "git ls-tree -t -r $commit|"); print STDERR "Plumbing tree $name\n"; while () { if (/^(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) { -- cgit v1.2.3 From 99f0b59954c4682fc3145ba2c49f88ea20b30174 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 20 Oct 2008 11:31:38 -0600 Subject: bash completion: Add 'workflows' to 'git help' Completion for new workflow documentation introduced in f948dd8 Signed-off-by: Lee Marlow Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d192927c20..eebe73409b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -881,6 +881,7 @@ _git_help () attributes cli core-tutorial cvs-migration diffcore gitk glossary hooks ignore modules repository-layout tutorial tutorial-2 + workflows " } -- cgit v1.2.3 From 41d8cf7d7fd79fe1fd00b04052c49bffaedfd309 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Fri, 31 Oct 2008 01:04:46 +0100 Subject: bash completion: add doubledash to "git show" Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 751e273e1a..39a1ce5a39 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1410,6 +1410,8 @@ _git_shortlog () _git_show () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) -- cgit v1.2.3 From 4471649f44d7a8e4b7b927e43b848bb71b75630d Mon Sep 17 00:00:00 2001 From: Pete Harlan Date: Mon, 3 Nov 2008 23:19:53 -0800 Subject: contrib/hooks/post-receive-email: Put rev display in separate function The display of a revision in an email-appropriate format is done in two places with similar code. In preparation for making that display more complex, move it into a separate function that handles both cases. Signed-off-by: Pete Harlan Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 41 +++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 41368950d6..2cd373d625 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -224,13 +224,7 @@ generate_create_branch_email() echo "" echo $LOGBEGIN - # This shows all log entries that are not already covered by - # another ref - i.e. commits that are now accessible from this - # ref that were previously not accessible - # (see generate_update_branch_email for the explanation of this - # command) - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $newrev + show_new_revisions echo $LOGEND } @@ -390,8 +384,7 @@ generate_update_branch_email() echo "" echo $LOGBEGIN - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $oldrev..$newrev + show_new_revisions # XXX: Need a way of detecting whether git rev-list actually # outputted anything, so that we can issue a "no new @@ -591,6 +584,36 @@ generate_delete_general_email() echo $LOGEND } + +# --------------- Miscellaneous utilities + +# +# Show new revisions as the user would like to see them in the email. +# +show_new_revisions() +{ + # This shows all log entries that are not already covered by + # another ref - i.e. commits that are now accessible from this + # ref that were previously not accessible + # (see generate_update_branch_email for the explanation of this + # command) + + # Revision range passed to rev-list differs for new vs. updated + # branches. + if [ "$change_type" = create ] + then + # Show all revisions exclusive to this (new) branch. + revspec=$newrev + else + # Branch update; show revisions not part of $oldrev. + revspec=$oldrev..$newrev + fi + + git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + git rev-list --pretty --stdin $revspec +} + + send_mail() { if [ -n "$envelopesender" ]; then -- cgit v1.2.3 From b0a7d111732f644623253927c9ef2d4f3009e668 Mon Sep 17 00:00:00 2001 From: Pete Harlan Date: Mon, 3 Nov 2008 23:19:54 -0800 Subject: contrib/hooks/post-receive-email: Make revision display configurable Add configuration option hooks.showrev, letting the user override how revisions will be shown in the commit email. Signed-off-by: Pete Harlan Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 2cd373d625..28a3c0e46e 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -38,6 +38,12 @@ # hooks.emailprefix # All emails have their subjects prefixed with this prefix, or "[SCM]" # if emailprefix is unset, to aid filtering +# hooks.showrev +# The shell command used to format each revision in the email, with +# "%s" replaced with the commit id. Defaults to "git rev-list -1 +# --pretty %s", displaying the commit id, author, date and log +# message. To list full patches separated by a blank line, you +# could set this to "git show -C %s; echo". # # Notes # ----- @@ -610,7 +616,16 @@ show_new_revisions() fi git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $revspec + if [ -z "$custom_showrev" ] + then + git rev-list --pretty --stdin $revspec + else + git rev-list --stdin $revspec | + while read onerev + do + eval $(printf "$custom_showrev" $onerev) + done + fi } @@ -650,6 +665,7 @@ recipients=$(git config hooks.mailinglist) announcerecipients=$(git config hooks.announcelist) envelopesender=$(git config hooks.envelopesender) emailprefix=$(git config hooks.emailprefix || echo '[SCM] ') +custom_showrev=$(git config hooks.showrev) # --- Main loop # Allow dual mode: run from the command line just like the update hook, or -- cgit v1.2.3 From 7f96e2e25aa008556a4ede7a65de8488eb9890e6 Mon Sep 17 00:00:00 2001 From: John Chapman Date: Sat, 8 Nov 2008 14:22:48 +1100 Subject: git-p4: Support purged files and optimize memory usage Purged files are handled as if they are merely deleted, which is not entirely optimal, but I don't know of any other way to handle them. File data is deleted from memory as early as they can, and they are more efficiently handled, at (significant) cost to CPU usage. Still need to handle p4 branches with spaces in their names. Still need to make git-p4 clone more reliable. - Perhaps with a --continue option. (Sometimes the p4 server kills the connection) Signed-off-by: John Chapman Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2216cacba7..38d1a17333 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -946,7 +946,7 @@ class P4Sync(Command): if includeFile: filesForCommit.append(f) - if f['action'] != 'delete': + if f['action'] not in ('delete', 'purge'): filesToRead.append(f) filedata = [] @@ -965,11 +965,11 @@ class P4Sync(Command): while j < len(filedata): stat = filedata[j] j += 1 - text = []; + text = '' while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'): - text.append(filedata[j]['data']) + text += filedata[j]['data'] + del filedata[j]['data'] j += 1 - text = ''.join(text) if not stat.has_key('depotFile'): sys.stderr.write("p4 print fails with: %s\n" % repr(stat)) @@ -1038,7 +1038,7 @@ class P4Sync(Command): continue relPath = self.stripRepoPath(file['path'], branchPrefixes) - if file["action"] == "delete": + if file["action"] in ("delete", "purge"): self.gitStream.write("D %s\n" % relPath) else: data = file['data'] @@ -1077,7 +1077,7 @@ class P4Sync(Command): cleanedFiles = {} for info in files: - if info["action"] == "delete": + if info["action"] in ("delete", "purge"): continue cleanedFiles[info["depotFile"]] = info["rev"] @@ -1400,7 +1400,7 @@ class P4Sync(Command): if change > newestRevision: newestRevision = change - if info["action"] == "delete": + if info["action"] in ("delete", "purge"): # don't increase the file cnt, otherwise details["depotFile123"] will have gaps! #fileCnt = fileCnt + 1 continue -- cgit v1.2.3 From 36bd844658cf244ec2c6756c18673a4b7ed8ec9e Mon Sep 17 00:00:00 2001 From: John Chapman Date: Sat, 8 Nov 2008 14:22:49 +1100 Subject: git-p4: Cache git config for performance This makes git-p4 noticibly faster on Windows. Signed-off-by: John Chapman Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 38d1a17333..9f0a5f92c1 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -316,8 +316,11 @@ def gitBranchExists(branch): stderr=subprocess.PIPE, stdout=subprocess.PIPE); return proc.wait() == 0; +_gitConfig = {} def gitConfig(key): - return read_pipe("git config %s" % key, ignore_error=True).strip() + if not _gitConfig.has_key(key): + _gitConfig[key] = read_pipe("git config %s" % key, ignore_error=True).strip() + return _gitConfig[key] def p4BranchesInGit(branchesAreInRemotes = True): branches = {} -- cgit v1.2.3 From 36d2078ff19c849aef94f045bf2b4fb73a5098e8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 1 Nov 2008 20:34:33 +0100 Subject: git.el: Improve error handling for commits. Display all errors happening in the various subcommands of the commit sequence, and abort on any error. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c1cf1cbcc0..d357b543e2 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -208,6 +208,15 @@ and returns the process output as a string, or nil if the git failed." (and (eq 0 (apply #' git-call-process-env t env args)) (buffer-string)))) +(defun git-call-process-string-display-error (&rest args) + "Wrapper for call-process that displays error message and returns +the process output as a string, or nil if the git command failed." + (with-temp-buffer + (if (eq 0 (apply #'git-call-process-env (list t t) nil args)) + (buffer-string) + (display-message-or-buffer (current-buffer)) + nil))) + (defun git-run-process-region (buffer start end program args) "Run a git process with a buffer region as input." (let ((output-buffer (current-buffer)) @@ -391,14 +400,16 @@ and returns the process output as a string, or nil if the git failed." (defun git-read-tree (tree &optional index-file) "Read a tree into the index file." - (apply #'git-call-process-env nil - (if index-file `(("GIT_INDEX_FILE" . ,index-file)) nil) - "read-tree" (if tree (list tree)))) + (let ((process-environment + (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment))) + (apply 'git-call-process-display-error "read-tree" (if tree (list tree))))) (defun git-write-tree (&optional index-file) "Call git-write-tree and return the resulting tree SHA1 as a string." - (git-get-string-sha1 - (git-call-process-env-string (and index-file `(("GIT_INDEX_FILE" . ,index-file))) "write-tree"))) + (let ((process-environment + (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment))) + (git-get-string-sha1 + (git-call-process-string-display-error "write-tree")))) (defun git-commit-tree (buffer tree head) "Call git-commit-tree with buffer as input and return the resulting commit SHA1." @@ -824,19 +835,18 @@ Return the list of files that haven't been handled." (defun git-update-index (index-file files) "Run git-update-index on a list of files." - (let ((env (and index-file `(("GIT_INDEX_FILE" . ,index-file)))) + (let ((process-environment (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) + process-environment)) added deleted modified) (dolist (info files) (case (git-fileinfo->state info) ('added (push info added)) ('deleted (push info deleted)) ('modified (push info modified)))) - (when added - (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added))) - (when deleted - (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted))) - (when modified - (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified))))) + (and + (or (not added) (apply #'git-call-process-display-error "update-index" "--add" "--" (git-get-filenames added))) + (or (not deleted) (apply #'git-call-process-display-error "update-index" "--remove" "--" (git-get-filenames deleted))) + (or (not modified) (apply #'git-call-process-display-error "update-index" "--" (git-get-filenames modified)))))) (defun git-run-pre-commit-hook () "Run the pre-commit hook if any." @@ -862,17 +872,19 @@ Return the list of files that haven't been handled." (message "You cannot commit unmerged files, resolve them first.") (unwind-protect (let ((files (git-marked-files-state 'added 'deleted 'modified)) - head head-tree) + head tree head-tree) (unless (git-empty-db-p) (setq head (git-rev-parse "HEAD") head-tree (git-rev-parse "HEAD^{tree}"))) (if files (progn (message "Running git commit...") - (git-read-tree head-tree index-file) - (git-update-index nil files) ;update both the default index - (git-update-index index-file files) ;and the temporary one - (let ((tree (git-write-tree index-file))) + (when + (and + (git-read-tree head-tree index-file) + (git-update-index nil files) ;update both the default index + (git-update-index index-file files) ;and the temporary one + (setq tree (git-write-tree index-file))) (if (or (not (string-equal tree head-tree)) (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? ")) (let ((commit (git-commit-tree buffer tree head))) -- cgit v1.2.3 From 9ddf6d7c105e58d76ea6762d8cc8eebdf463903e Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 1 Nov 2008 20:42:39 +0100 Subject: git.el: Remove the env parameter in git-call-process and git-call-process-string. All callers that need to change the environment now set process-environment themselves. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 54 +++++++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 28 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d357b543e2..d28b45e558 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -183,11 +183,9 @@ if there is already one that displays the same directory." "Build a list of NAME=VALUE strings from a list of environment strings." (mapcar (lambda (entry) (concat (car entry) "=" (cdr entry))) env)) -(defun git-call-process-env (buffer env &rest args) +(defun git-call-process (buffer &rest args) "Wrapper for call-process that sets environment strings." - (let ((process-environment (append (git-get-env-strings env) - process-environment))) - (apply #'call-process "git" nil buffer nil args))) + (apply #'call-process "git" nil buffer nil args)) (defun git-call-process-display-error (&rest args) "Wrapper for call-process that displays error messages." @@ -197,22 +195,22 @@ if there is already one that displays the same directory." (let ((default-directory dir) (buffer-read-only nil)) (erase-buffer) - (eq 0 (apply 'call-process "git" nil (list buffer t) nil args)))))) + (eq 0 (apply #'git-call-process (list buffer t) args)))))) (unless ok (display-message-or-buffer buffer)) ok)) -(defun git-call-process-env-string (env &rest args) - "Wrapper for call-process that sets environment strings, -and returns the process output as a string, or nil if the git failed." +(defun git-call-process-string (&rest args) + "Wrapper for call-process that returns the process output as a string, +or nil if the git command failed." (with-temp-buffer - (and (eq 0 (apply #' git-call-process-env t env args)) + (and (eq 0 (apply #'git-call-process t args)) (buffer-string)))) (defun git-call-process-string-display-error (&rest args) "Wrapper for call-process that displays error message and returns the process output as a string, or nil if the git command failed." (with-temp-buffer - (if (eq 0 (apply #'git-call-process-env (list t t) nil args)) + (if (eq 0 (apply #'git-call-process (list t t) args)) (buffer-string) (display-message-or-buffer (current-buffer)) nil))) @@ -235,7 +233,7 @@ the process output as a string, or nil if the git command failed." (let ((default-directory dir) (buffer-read-only nil)) (erase-buffer) - (apply #'git-call-process-env buffer nil args))) + (apply #'git-call-process buffer args))) (message "Running git %s...done" (car args)) buffer)) @@ -336,7 +334,7 @@ the process output as a string, or nil if the git command failed." (let ((cdup (with-output-to-string (with-current-buffer standard-output (cd dir) - (unless (eq 0 (call-process "git" nil t nil "rev-parse" "--show-cdup")) + (unless (eq 0 (git-call-process t "rev-parse" "--show-cdup")) (error "cannot find top-level git tree for %s." dir)))))) (expand-file-name (concat (file-name-as-directory dir) (car (split-string cdup "\n")))))) @@ -357,7 +355,7 @@ the process output as a string, or nil if the git command failed." (sort-lines nil (point-min) (point-max)) (save-buffer)) (when created - (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) + (git-call-process nil "update-index" "--add" "--" (file-relative-name ignore-name))) (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) ; propertize definition for XEmacs, stolen from erc-compat @@ -376,16 +374,16 @@ the process output as a string, or nil if the git command failed." (defun git-rev-parse (rev) "Parse a revision name and return its SHA1." (git-get-string-sha1 - (git-call-process-env-string nil "rev-parse" rev))) + (git-call-process-string "rev-parse" rev))) (defun git-config (key) "Retrieve the value associated to KEY in the git repository config file." - (let ((str (git-call-process-env-string nil "config" key))) + (let ((str (git-call-process-string "config" key))) (and str (car (split-string str "\n"))))) (defun git-symbolic-ref (ref) "Wrapper for the git-symbolic-ref command." - (let ((str (git-call-process-env-string nil "symbolic-ref" ref))) + (let ((str (git-call-process-string "symbolic-ref" ref))) (and str (car (split-string str "\n"))))) (defun git-update-ref (ref newval &optional oldval reason) @@ -463,7 +461,7 @@ the process output as a string, or nil if the git command failed." (defun git-empty-db-p () "Check if the git db is empty (no commit done yet)." - (not (eq 0 (call-process "git" nil nil nil "rev-parse" "--verify" "HEAD")))) + (not (eq 0 (git-call-process nil "rev-parse" "--verify" "HEAD")))) (defun git-get-merge-heads () "Retrieve the merge heads from the MERGE_HEAD file if present." @@ -479,7 +477,7 @@ the process output as a string, or nil if the git command failed." (defun git-get-commit-description (commit) "Get a one-line description of COMMIT." (let ((coding-system-for-read (git-get-logoutput-coding-system))) - (let ((descr (git-call-process-env-string nil "log" "--max-count=1" "--pretty=oneline" commit))) + (let ((descr (git-call-process-string "log" "--max-count=1" "--pretty=oneline" commit))) (if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr)) (concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr)) descr)))) @@ -655,7 +653,7 @@ Return the list of files that haven't been handled." (let ((remaining (copy-sequence files)) infolist) (with-temp-buffer - (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files) + (apply #'git-call-process t "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) (while (re-search-forward ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" @@ -688,7 +686,7 @@ Return the list of files that haven't been handled." Return the list of files that haven't been handled." (let (infolist) (with-temp-buffer - (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files)) + (apply #'git-call-process t "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1) (let ((name (match-string 1))) @@ -705,7 +703,7 @@ Return the list of files that haven't been handled." (let ((remaining (copy-sequence files)) infolist) (with-temp-buffer - (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files) + (apply #'git-call-process t "ls-files" "-z" "-s" "-c" "--" files) (goto-char (point-min)) (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t) (let* ((new-perm (string-to-number (match-string 1) 8)) @@ -719,7 +717,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." (with-temp-buffer - (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files) + (apply #'git-call-process t "ls-files" "-z" "-u" "--" files) (goto-char (point-min)) (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) @@ -893,8 +891,8 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) (git-update-status-files (git-get-filenames files) 'uptodate) - (git-call-process-env nil nil "rerere") - (git-call-process-env nil nil "gc" "--auto") + (git-call-process nil "rerere") + (git-call-process nil "gc" "--auto") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) @@ -1311,7 +1309,7 @@ Return the list of files that haven't been handled." (let (author-name author-email subject date msg) (with-temp-buffer (let ((coding-system (git-get-logoutput-coding-system))) - (git-call-process-env t nil "log" "-1" "--pretty=medium" commit) + (git-call-process t "log" "-1" "--pretty=medium" commit) (goto-char (point-min)) (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) (setq author-name (match-string 1)) @@ -1331,7 +1329,7 @@ Return the list of files that haven't been handled." "Retrieve the list of files modified by COMMIT." (let (files) (with-temp-buffer - (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) + (git-call-process t "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) @@ -1395,7 +1393,7 @@ amended version of it." (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) (message "Refreshing git status...") - (git-call-process-env nil nil "update-index" "--refresh") + (git-call-process nil "update-index" "--refresh") (git-clear-status status) (git-update-status-files nil) ; restore file marks @@ -1588,7 +1586,7 @@ Meant to be used in `after-save-hook'." (let ((filename (file-relative-name file dir))) ; skip files located inside the .git directory (unless (string-match "^\\.git/" filename) - (git-call-process-env nil nil "add" "--refresh" "--" filename) + (git-call-process nil "add" "--refresh" "--" filename) (git-update-status-files (list filename) 'uptodate))))))) (defun git-help () -- cgit v1.2.3 From 6fb204266cc985284b554e9f9f1894ec8360b2f5 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 18:04:31 +0200 Subject: git.el: Simplify handling of merge heads in the commit log-edit buffer. Use a single Merge: header instead of one Parent: header for each parent, and don't list the current HEAD as a merged head. Support symbolic references too. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d28b45e558..53ada39b43 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -173,7 +173,7 @@ if there is already one that displays the same directory." (defconst git-log-msg-separator "--- log message follows this line ---") (defvar git-log-edit-font-lock-keywords - `(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)$" + `(("^\\(Author:\\|Date:\\|Merge:\\|Signed-off-by:\\)\\(.*\\)$" (1 font-lock-keyword-face) (2 font-lock-function-name-face)) (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$") @@ -433,11 +433,11 @@ the process output as a string, or nil if the git command failed." (when (re-search-forward "^Date: +\\(.*\\)$" nil t) (setq author-date (match-string 1))) (goto-char (point-min)) - (while (re-search-forward "^Parent: +\\([0-9a-f]+\\)" nil t) - (unless (string-equal head (match-string 1)) - (setq subject "commit (merge): ") + (when (re-search-forward "^Merge: +\\(.*\\)" nil t) + (setq subject "commit (merge): ") + (dolist (parent (split-string (match-string 1) " +" t)) (push "-p" args) - (push (match-string 1) args)))) + (push parent args)))) (setq log-start (point-min))) (setq log-end (point-max)) (goto-char log-start) @@ -1253,9 +1253,8 @@ Return the list of files that haven't been handled." (or author-email committer-email) (if date (format "Date: %s\n" date) "") (if merge-heads - (format "Parent: %s\n%s\n" - (git-rev-parse "HEAD") - (mapconcat (lambda (str) (concat "Parent: " str)) merge-heads "\n")) + (format "Merge: %s\n" + (mapconcat 'identity merge-heads " ")) "")) 'face 'git-header-face) (propertize git-log-msg-separator 'face 'git-separator-face) -- cgit v1.2.3 From ef5133df7c9d0304ce2fc437abc74c220f49dabd Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 18:05:58 +0200 Subject: git.el: Properly handle merge commits in git-amend-commit. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 53ada39b43..207ff0ba27 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1235,11 +1235,10 @@ Return the list of files that haven't been handled." (goto-char (point-max)) (insert sign-off "\n")))) -(defun git-setup-log-buffer (buffer &optional author-name author-email subject date msg) +(defun git-setup-log-buffer (buffer &optional merge-heads author-name author-email subject date msg) "Setup the log buffer for a commit." (unless git-status (error "Not in git-status buffer.")) - (let ((merge-heads (git-get-merge-heads)) - (dir default-directory) + (let ((dir default-directory) (committer-name (git-get-committer-name)) (committer-email (git-get-committer-email)) (sign-off git-append-signed-off-by)) @@ -1294,7 +1293,7 @@ Return the list of files that haven't been handled." (goto-char (point-min)) (when (re-search-forward "^Date: \\(.*\\)$" nil t) (setq date (match-string 1))))) - (git-setup-log-buffer buffer author-name author-email subject date)) + (git-setup-log-buffer buffer (git-get-merge-heads) author-name author-email subject date)) (if (boundp 'log-edit-diff-function) (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files) (log-edit-diff-function . git-log-edit-diff)) buffer) @@ -1305,11 +1304,13 @@ Return the list of files that haven't been handled." (defun git-setup-commit-buffer (commit) "Setup the commit buffer with the contents of COMMIT." - (let (author-name author-email subject date msg) + (let (parents author-name author-email subject date msg) (with-temp-buffer (let ((coding-system (git-get-logoutput-coding-system))) - (git-call-process t "log" "-1" "--pretty=medium" commit) + (git-call-process t "log" "-1" "--pretty=medium" "--abbrev=40" commit) (goto-char (point-min)) + (when (re-search-forward "^Merge: *\\(.*\\)$" nil t) + (setq parents (cdr (split-string (match-string 1) " +")))) (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) (setq author-name (match-string 1)) (setq author-email (match-string 2))) @@ -1321,14 +1322,14 @@ Return the list of files that haven't been handled." (setq subject (pop msg)) (while (and msg (zerop (length (car msg))) (pop msg))))) (git-setup-log-buffer (get-buffer-create "*git-commit*") - author-name author-email subject date + parents author-name author-email subject date (mapconcat #'identity msg "\n")))) (defun git-get-commit-files (commit) "Retrieve the list of files modified by COMMIT." (let (files) (with-temp-buffer - (git-call-process t "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) + (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) -- cgit v1.2.3 From db18a182a28f254fa514179dcd49ceb3d95cd9d8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 20:35:20 +0200 Subject: git.el: Fix git-amend-commit to support amending an initial commit. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 207ff0ba27..2dd97eeb54 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -389,11 +389,12 @@ the process output as a string, or nil if the git command failed." (defun git-update-ref (ref newval &optional oldval reason) "Update a reference by calling git-update-ref." (let ((args (and oldval (list oldval)))) - (push newval args) + (when newval (push newval args)) (push ref args) (when reason (push reason args) (push "-m" args)) + (unless newval (push "-d" args)) (apply 'git-call-process-display-error "update-ref" args))) (defun git-read-tree (tree &optional index-file) @@ -1329,7 +1330,7 @@ Return the list of files that haven't been handled." "Retrieve the list of files modified by COMMIT." (let (files) (with-temp-buffer - (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" commit) + (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" "--root" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) @@ -1343,7 +1344,10 @@ amended version of it." (when (git-empty-db-p) (error "No commit to amend.")) (let* ((commit (git-rev-parse "HEAD")) (files (git-get-commit-files commit))) - (when (git-call-process-display-error "reset" "--soft" "HEAD^") + (when (if (git-rev-parse "HEAD^") + (git-call-process-display-error "reset" "--soft" "HEAD^") + (and (git-update-ref "ORIG_HEAD" commit) + (git-update-ref "HEAD" nil commit))) (git-update-status-files (copy-sequence files) 'uptodate) (git-mark-files git-status files) (git-refresh-files) -- cgit v1.2.3 From 433ee03f9748a8a5d0e0495bbf4b054ae922103b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 20:35:52 +0200 Subject: git.el: Never clear the status buffer, only update the files. This makes it unnecessary to save/restore the file marks. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 169 ++++++++++++++++++++++++++------------------------- 1 file changed, 85 insertions(+), 84 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2dd97eeb54..67c5275992 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -356,7 +356,7 @@ the process output as a string, or nil if the git command failed." (save-buffer)) (when created (git-call-process nil "update-index" "--add" "--" (file-relative-name ignore-name))) - (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) + (git-update-status-files (list (file-relative-name ignore-name))))) ; propertize definition for XEmacs, stolen from erc-compat (eval-when-compile @@ -497,14 +497,11 @@ the process output as a string, or nil if the git command failed." old-perm new-perm ;; permission flags rename-state ;; rename or copy state orig-name ;; original name for renames or copies + needs-update ;; whether file needs to be updated needs-refresh) ;; whether file needs to be refreshed (defvar git-status nil) -(defun git-clear-status (status) - "Remove everything from the status list." - (ewoc-filter status (lambda (info) nil))) - (defun git-set-fileinfo-state (info state) "Set the state of a file info." (unless (eq (git-fileinfo->state info) state) @@ -512,6 +509,7 @@ the process output as a string, or nil if the git command failed." (git-fileinfo->new-perm info) (git-fileinfo->old-perm info) (git-fileinfo->rename-state info) nil (git-fileinfo->orig-name info) nil + (git-fileinfo->needs-update info) nil (git-fileinfo->needs-refresh info) t))) (defun git-status-filenames-map (status func files &rest args) @@ -521,10 +519,11 @@ the process output as a string, or nil if the git command failed." (let ((file (pop files)) (node (ewoc-nth status 0))) (while (and file node) - (let ((info (ewoc-data node))) - (if (string-lessp (git-fileinfo->name info) file) + (let* ((info (ewoc-data node)) + (name (git-fileinfo->name info))) + (if (string-lessp name file) (setq node (ewoc-next status node)) - (if (string-equal (git-fileinfo->name info) file) + (if (string-equal name file) (apply func info args)) (setq file (pop files)))))))) @@ -622,37 +621,50 @@ the process output as a string, or nil if the git command failed." (git-file-type-as-string old-perm new-perm) (git-rename-as-string info))))) -(defun git-insert-info-list (status infolist) - "Insert a list of file infos in the status buffer, replacing existing ones if any." - (setq infolist (sort infolist - (lambda (info1 info2) - (string-lessp (git-fileinfo->name info1) - (git-fileinfo->name info2))))) - (let ((info (pop infolist)) - (node (ewoc-nth status 0))) +(defun git-update-node-fileinfo (node info) + "Update the fileinfo of the specified node. The names are assumed to match already." + (let ((data (ewoc-data node))) + (setf + ;; preserve the marked flag + (git-fileinfo->marked info) (git-fileinfo->marked data) + (git-fileinfo->needs-update data) nil) + (when (not (equal info data)) + (setf (git-fileinfo->needs-refresh info) t + (ewoc-data node) info)))) + +(defun git-insert-info-list (status infolist files) + "Insert a sorted list of file infos in the status buffer, replacing existing ones if any." + (let* ((info (pop infolist)) + (node (ewoc-nth status 0)) + (name (and info (git-fileinfo->name info))) + remaining) (while info - (cond ((not node) - (setq node (ewoc-enter-last status info)) - (setq info (pop infolist))) - ((string-lessp (git-fileinfo->name (ewoc-data node)) - (git-fileinfo->name info)) - (setq node (ewoc-next status node))) - ((string-equal (git-fileinfo->name (ewoc-data node)) - (git-fileinfo->name info)) - ;; preserve the marked flag - (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))) - (setf (git-fileinfo->needs-refresh info) t) - (setf (ewoc-data node) info) - (setq info (pop infolist))) - (t - (setq node (ewoc-enter-before status node info)) - (setq info (pop infolist))))))) + (let ((nodename (and node (git-fileinfo->name (ewoc-data node))))) + (while (and files (string-lessp (car files) name)) + (push (pop files) remaining)) + (when (and files (string-equal (car files) name)) + (setq files (cdr files))) + (cond ((not nodename) + (setq node (ewoc-enter-last status info)) + (setq info (pop infolist)) + (setq name (and info (git-fileinfo->name info)))) + ((string-lessp nodename name) + (setq node (ewoc-next status node))) + ((string-equal nodename name) + ;; preserve the marked flag + (git-update-node-fileinfo node info) + (setq info (pop infolist)) + (setq name (and info (git-fileinfo->name info)))) + (t + (setq node (ewoc-enter-before status node info)) + (setq info (pop infolist)) + (setq name (and info (git-fileinfo->name info))))))) + (nconc (nreverse remaining) files))) (defun git-run-diff-index (status files) "Run git-diff-index on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let ((remaining (copy-sequence files)) - infolist) + (let (infolist) (with-temp-buffer (apply #'git-call-process t "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) @@ -669,11 +681,12 @@ Return the list of files that haven't been handled." (push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist) (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist) (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist)) - (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)) - (setq remaining (delete name remaining)) - (when new-name (setq remaining (delete new-name remaining)))))) - (git-insert-info-list status infolist) - remaining)) + (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))))) + (setq infolist (sort (nreverse infolist) + (lambda (info1 info2) + (string-lessp (git-fileinfo->name info1) + (git-fileinfo->name info2))))) + (git-insert-info-list status infolist files))) (defun git-find-status-file (status file) "Find a given file in the status ewoc and return its node." @@ -693,16 +706,14 @@ Return the list of files that haven't been handled." (let ((name (match-string 1))) (push (git-create-fileinfo default-state name 0 (if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0)) - infolist) - (setq files (delete name files))))) - (git-insert-info-list status infolist) - files)) + infolist)))) + (setq infolist (nreverse infolist)) ;; assume it is sorted already + (git-insert-info-list status infolist files))) (defun git-run-ls-files-cached (status files default-state) "Run git-ls-files -c on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let ((remaining (copy-sequence files)) - infolist) + (let (infolist) (with-temp-buffer (apply #'git-call-process t "ls-files" "-z" "-s" "-c" "--" files) (goto-char (point-min)) @@ -710,10 +721,9 @@ Return the list of files that haven't been handled." (let* ((new-perm (string-to-number (match-string 1) 8)) (old-perm (if (eq default-state 'added) 0 new-perm)) (name (match-string 2))) - (push (git-create-fileinfo default-state name old-perm new-perm) infolist) - (setq remaining (delete name remaining))))) - (git-insert-info-list status infolist) - remaining)) + (push (git-create-fileinfo default-state name old-perm new-perm) infolist)))) + (setq infolist (nreverse infolist)) ;; assume it is sorted already + (git-insert-info-list status infolist files))) (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." @@ -742,11 +752,17 @@ Return the list of files that haven't been handled." (concat "--exclude-per-directory=" git-per-dir-ignore-file) (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) -(defun git-update-status-files (files &optional default-state) +(defun git-update-status-files (&optional files) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) - (when (or git-show-uptodate files) - (git-run-ls-files-cached git-status files 'uptodate)) + ;; set the needs-update flag on existing files + (if (setq files (sort files #'string-lessp)) + (git-status-filenames-map + git-status (lambda (info) (setf (git-fileinfo->needs-update info) t)) files) + (ewoc-map (lambda (info) (setf (git-fileinfo->needs-update info) t) nil) git-status) + (git-call-process nil "update-index" "--refresh") + (when git-show-uptodate + (git-run-ls-files-cached git-status nil 'uptodate))) (let* ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db (git-run-ls-files-cached git-status files 'added) @@ -756,7 +772,11 @@ Return the list of files that haven't been handled." (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o"))) (when (or remaining-files (and git-show-ignored (not files))) (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i"))) - (git-set-filenames-state git-status remaining-files default-state) + (unless files + (setq remaining-files (git-get-filenames (ewoc-collect git-status #'git-fileinfo->needs-update)))) + (when remaining-files + (setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate))) + (git-set-filenames-state git-status remaining-files nil) (git-refresh-files) (git-refresh-ewoc-hf git-status))) @@ -891,11 +911,9 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) - (git-update-status-files (git-get-filenames files) 'uptodate) + (git-update-status-files (git-get-filenames files)) (git-call-process nil "rerere") (git-call-process nil "gc" "--auto") - (git-refresh-files) - (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) (git-run-hook "post-commit" nil))) (message "Commit aborted.")))) @@ -1009,7 +1027,7 @@ Return the list of files that haven't been handled." (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) (when (apply 'git-call-process-display-error "update-index" "--add" "--" files) - (git-update-status-files files 'uptodate) + (git-update-status-files files) (git-success-message "Added" files)))) (defun git-ignore-file () @@ -1019,7 +1037,7 @@ Return the list of files that haven't been handled." (unless files (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files)) (dolist (f files) (git-append-to-ignore f)) - (git-update-status-files files 'ignored) + (git-update-status-files files) (git-success-message "Ignored" files))) (defun git-remove-file () @@ -1037,7 +1055,7 @@ Return the list of files that haven't been handled." (delete-directory name) (delete-file name)))) (when (apply 'git-call-process-display-error "update-index" "--remove" "--" files) - (git-update-status-files files nil) + (git-update-status-files files) (git-success-message "Removed" files))) (message "Aborting")))) @@ -1065,7 +1083,7 @@ Return the list of files that haven't been handled." (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added)) (or (not modified) (apply 'git-call-process-display-error "checkout" "HEAD" modified))))) - (git-update-status-files (append added modified) 'uptodate) + (git-update-status-files (append added modified)) (when ok (dolist (file modified) (let ((buffer (get-file-buffer file))) @@ -1078,7 +1096,7 @@ Return the list of files that haven't been handled." (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files (when (apply 'git-call-process-display-error "update-index" "--" files) - (git-update-status-files files 'uptodate) + (git-update-status-files files) (git-success-message "Resolved" files))))) (defun git-remove-handled () @@ -1348,7 +1366,7 @@ amended version of it." (git-call-process-display-error "reset" "--soft" "HEAD^") (and (git-update-ref "ORIG_HEAD" commit) (git-update-ref "HEAD" nil commit))) - (git-update-status-files (copy-sequence files) 'uptodate) + (git-update-status-files (copy-sequence files)) (git-mark-files git-status files) (git-refresh-files) (git-setup-commit-buffer commit) @@ -1391,27 +1409,10 @@ amended version of it." (defun git-refresh-status () "Refresh the git status buffer." (interactive) - (let* ((status git-status) - (pos (ewoc-locate status)) - (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info))))) - (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) - (unless status (error "Not in git-status buffer.")) - (message "Refreshing git status...") - (git-call-process nil "update-index" "--refresh") - (git-clear-status status) - (git-update-status-files nil) - ; restore file marks - (when marked-files - (git-status-filenames-map status - (lambda (info) - (setf (git-fileinfo->marked info) t) - (setf (git-fileinfo->needs-refresh info) t)) - marked-files) - (git-refresh-files)) - ; move point to the current file name if any - (message "Refreshing git status...done") - (let ((node (and cur-name (git-find-status-file status cur-name)))) - (when node (ewoc-goto-node status node))))) + (unless git-status (error "Not in git-status buffer.")) + (message "Refreshing git status...") + (git-update-status-files) + (message "Refreshing git status...done")) (defun git-status-quit () "Quit git-status mode." @@ -1591,7 +1592,7 @@ Meant to be used in `after-save-hook'." ; skip files located inside the .git directory (unless (string-match "^\\.git/" filename) (git-call-process nil "add" "--refresh" "--" filename) - (git-update-status-files (list filename) 'uptodate))))))) + (git-update-status-files (list filename)))))))) (defun git-help () "Display help for Git mode." -- cgit v1.2.3 From b0a53e9e56d0a501aebc99d3614be413e91613f6 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 4 Aug 2008 09:30:42 +0200 Subject: git.el: Add an insert file command. This allows to insert a file in the buffer no matter what its state is, making it possible for instance to remove an up-to-date file. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 67c5275992..6119c31d05 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1019,6 +1019,11 @@ Return the list of files that haven't been handled." (setq node (ewoc-prev git-status node))) (ewoc-goto-node git-status last))) +(defun git-insert-file (file) + "Insert file(s) into the git-status buffer." + (interactive "fInsert file: ") + (git-update-status-files (list (file-relative-name file)))) + (defun git-add-file () "Add marked file(s) to the index cache." (interactive) @@ -1449,6 +1454,7 @@ amended version of it." (define-key map "\r" 'git-find-file) (define-key map "g" 'git-refresh-status) (define-key map "i" 'git-ignore-file) + (define-key map "I" 'git-insert-file) (define-key map "l" 'git-log-file) (define-key map "m" 'git-mark-file) (define-key map "M" 'git-mark-all) @@ -1505,6 +1511,7 @@ amended version of it." ["Revert File" git-revert-file t] ["Ignore File" git-ignore-file t] ["Remove File" git-remove-file t] + ["Insert File" git-insert-file t] "--------" ["Find File" git-find-file t] ["View File" git-view-file t] -- cgit v1.2.3 From c4e8b72f228b20d3ed6cfba0586364ea8ca431af Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 1 Nov 2008 20:14:10 +0100 Subject: git.el: Add possibility to mark files directly in git-update-status-files. This avoids the need to go through the list twice, which helps performance on large file lists. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 6119c31d05..9e9101b17e 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -752,7 +752,7 @@ Return the list of files that haven't been handled." (concat "--exclude-per-directory=" git-per-dir-ignore-file) (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) -(defun git-update-status-files (&optional files) +(defun git-update-status-files (&optional files mark-files) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) ;; set the needs-update flag on existing files @@ -777,12 +777,12 @@ Return the list of files that haven't been handled." (when remaining-files (setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate))) (git-set-filenames-state git-status remaining-files nil) + (when mark-files (git-mark-files git-status files)) (git-refresh-files) (git-refresh-ewoc-hf git-status))) (defun git-mark-files (status files) "Mark all the specified FILES, and unmark the others." - (setq files (sort files #'string-lessp)) (let ((file (and files (pop files))) (node (ewoc-nth status 0))) (while node @@ -1371,9 +1371,7 @@ amended version of it." (git-call-process-display-error "reset" "--soft" "HEAD^") (and (git-update-ref "ORIG_HEAD" commit) (git-update-ref "HEAD" nil commit))) - (git-update-status-files (copy-sequence files)) - (git-mark-files git-status files) - (git-refresh-files) + (git-update-status-files files t) (git-setup-commit-buffer commit) (git-commit-file)))) -- cgit v1.2.3 From 1905a8666a676f7070ba15c6f56f98bb1da20f7b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 7 Nov 2008 14:28:09 +0100 Subject: git.el: Allow to commit even if there are no marked files. This can be useful to commit a merge that didn't result in any changes. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 9e9101b17e..09e8bae3a4 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -895,29 +895,26 @@ Return the list of files that haven't been handled." (unless (git-empty-db-p) (setq head (git-rev-parse "HEAD") head-tree (git-rev-parse "HEAD^{tree}"))) - (if files - (progn - (message "Running git commit...") - (when - (and - (git-read-tree head-tree index-file) - (git-update-index nil files) ;update both the default index - (git-update-index index-file files) ;and the temporary one - (setq tree (git-write-tree index-file))) - (if (or (not (string-equal tree head-tree)) - (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? ")) - (let ((commit (git-commit-tree buffer tree head))) - (when commit - (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) - (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) - (with-current-buffer buffer (erase-buffer)) - (git-update-status-files (git-get-filenames files)) - (git-call-process nil "rerere") - (git-call-process nil "gc" "--auto") - (message "Committed %s." commit) - (git-run-hook "post-commit" nil))) - (message "Commit aborted.")))) - (message "No files to commit."))) + (message "Running git commit...") + (when + (and + (git-read-tree head-tree index-file) + (git-update-index nil files) ;update both the default index + (git-update-index index-file files) ;and the temporary one + (setq tree (git-write-tree index-file))) + (if (or (not (string-equal tree head-tree)) + (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? ")) + (let ((commit (git-commit-tree buffer tree head))) + (when commit + (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) + (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) + (with-current-buffer buffer (erase-buffer)) + (git-update-status-files (git-get-filenames files)) + (git-call-process nil "rerere") + (git-call-process nil "gc" "--auto") + (message "Committed %s." commit) + (git-run-hook "post-commit" nil))) + (message "Commit aborted.")))) (delete-file index-file)))))) -- cgit v1.2.3 From 3d51c853df5730212f704a526340a5a059dffeda Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Wed, 26 Nov 2008 13:52:15 -0500 Subject: git-p4: fix keyword-expansion regex This text: my $dir = $File::Find::dir; return if ($dir !~ m,$options->{dirpat}$,); was improperly converted to: my $dir = $File$dir !~ m,$options->{dirpat}$,); by the keyword identifier expansion code. Add a \n to make sure the regex doesn't go across end-of-line boundaries. Signed-off-by: Pete Wyckoff Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 46136d49bf..2b122d3f51 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -963,7 +963,7 @@ class P4Sync(Command): if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text) elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): - text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text) + text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text) contents[stat['depotFile']] = text -- cgit v1.2.3 From 8d8163f377829d5f61f6053bd55fdcecaf360d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 27 Nov 2008 14:35:38 +0100 Subject: bash: remove dashed command leftovers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 5a625b07 (bash: remove fetch, push, pull dashed form leftovers, 2008-10-03) did that already, but there were still some git-cmd left here and there. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 41 +++++++--------------------------- 1 file changed, 8 insertions(+), 33 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 39a1ce5a39..bec09bdd62 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -650,21 +650,12 @@ _git_branch () _git_bundle () { - local mycword="$COMP_CWORD" - case "${COMP_WORDS[0]}" in - git) - local cmd="${COMP_WORDS[2]}" - mycword="$((mycword-1))" - ;; - git-bundle*) - local cmd="${COMP_WORDS[1]}" - ;; - esac - case "$mycword" in - 1) + local cmd="${COMP_WORDS[2]}" + case "$COMP_CWORD" in + 2) __gitcomp "create list-heads verify unbundle" ;; - 2) + 3) # looking for a file ;; *) @@ -812,12 +803,7 @@ _git_fetch () __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}" ;; *) - local remote - case "${COMP_WORDS[0]}" in - git-fetch) remote="${COMP_WORDS[1]}" ;; - git) remote="${COMP_WORDS[2]}" ;; - esac - __gitcomp "$(__git_refs2 "$remote")" + __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")" ;; esac fi @@ -1060,12 +1046,7 @@ _git_pull () if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" else - local remote - case "${COMP_WORDS[0]}" in - git-pull) remote="${COMP_WORDS[1]}" ;; - git) remote="${COMP_WORDS[2]}" ;; - esac - __gitcomp "$(__git_refs "$remote")" + __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" fi } @@ -1078,19 +1059,13 @@ _git_push () else case "$cur" in *:*) - local remote - case "${COMP_WORDS[0]}" in - git-push) remote="${COMP_WORDS[1]}" ;; - git) remote="${COMP_WORDS[2]}" ;; - esac - local pfx="" case "$COMP_WORDBREAKS" in *:*) : great ;; *) pfx="${cur%%:*}:" ;; esac - __gitcomp "$(__git_refs "$remote")" "$pfx" "${cur#*:}" + __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}" ;; +*) __gitcomp "$(__git_refs)" + "${cur#+}" @@ -1591,7 +1566,7 @@ _git_tag () -m|-F) COMPREPLY=() ;; - -*|tag|git-tag) + -*|tag) if [ $f = 1 ]; then __gitcomp "$(__git_tags)" else -- cgit v1.2.3 From 608efb875f89a946d5cb37b2dd4077132618e0e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 28 Nov 2008 01:46:38 +0100 Subject: bash: complete full refs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes it's handy to complete full refs, e.g. the user has some refs outside of refs/{heads,remotes,tags} or the user wants to complete some git command's special refs (like 'git show refs/bisect/bad'). To do that, we check whether the ref to be completed starts with 'refs/' or is 'refs' (to reduce the risk of matching 'refs-'). If it does, then we offer full refs for completion; otherwise everything works as usual. This way the impact on the common case is fairly small (hopefully not many users have branches or tags starting with 'refs'), and in the special case the cost of typing out 'refs' is bearable. While at it, also remove the unused 'cmd' variable from '__git_refs'. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index de193ba7c1..5fb34c49dc 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -188,11 +188,22 @@ __git_tags () __git_refs () { - local cmd i is_hash=y dir="$(__gitdir "$1")" + local i is_hash=y dir="$(__gitdir "$1")" + local cur="${COMP_WORDS[COMP_CWORD]}" format refs if [ -d "$dir" ]; then - if [ -e "$dir/HEAD" ]; then echo HEAD; fi - git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ - refs/tags refs/heads refs/remotes + case "$cur" in + refs|refs/*) + format="refname" + refs="${cur%/*}" + ;; + *) + if [ -e "$dir/HEAD" ]; then echo HEAD; fi + format="refname:short" + refs="refs/tags refs/heads refs/remotes" + ;; + esac + git --git-dir="$dir" for-each-ref --format="%($format)" \ + $refs return fi for i in $(git ls-remote "$dir" 2>/dev/null); do -- cgit v1.2.3 From c07838371b116251b6c4bc62a2ba64109baf74f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 27 Nov 2008 14:35:53 +0100 Subject: bash: offer refs instead of filenames for 'git revert' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The completion script for 'git revert' currently offers options and filenames. However, 'git revert' doesn't take any filenames from the command line, but a single commit. Therefore, it's more sane to offer refs instead. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index bec09bdd62..554a03ff4f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1344,7 +1344,7 @@ _git_revert () return ;; esac - COMPREPLY=() + __gitcomp "$(__git_refs)" } _git_rm () -- cgit v1.2.3 From 7f705dc3686c2b1c77172b6847c1406eb66a20c3 Mon Sep 17 00:00:00 2001 From: Tor Arvid Lund Date: Thu, 4 Dec 2008 14:37:33 +0100 Subject: git-p4: Fix bug in p4Where method. When running: p4 where //depot/SomePath/... The result can in some situations look like: //depot/SomePath/... //client/SomePath/... /home/user/p4root/SomePath/... -//depot/SomePath/UndesiredSubdir/... //client/SomePath/UndesiredSubdir/... /home/user/p4root/SomePath/UndesiredSubdir/... This depends on the users Client view. The current p4Where method will now return /home/user/p4root/SomePath/UndesiredSubdir/... which is not what we want. This patch loops through the results from "p4 where", and picks the one where the depotFile exactly matches the given depotPath (//depot/SomePath/... in this example). Signed-off-by: Tor Arvid Lund Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b44fbfc9b3..ee504e90ed 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -245,7 +245,15 @@ def p4Cmd(cmd): def p4Where(depotPath): if not depotPath.endswith("/"): depotPath += "/" - output = p4Cmd("where %s..." % depotPath) + depotPath = depotPath + "..." + outputList = p4CmdList("where %s" % depotPath) + output = None + for entry in outputList: + if entry["depotFile"] == depotPath: + output = entry + break + if output == None: + return "" if output["code"] == "error": return "" clientPath = "" -- cgit v1.2.3 From 75bc9573b0e332c34bc1c3d97306077fda573083 Mon Sep 17 00:00:00 2001 From: Tor Arvid Lund Date: Tue, 9 Dec 2008 16:41:50 +0100 Subject: git-p4: Fix regression in p4Where method. Unfortunately, I introduced a bug in commit 7f705dc36 (git-p4: Fix bug in p4Where method). This happens because sometimes the result from "p4 where " doesn't contain a "depotFile" key, but instead a "data" key that needs further parsing. This commit should ensure that both of these cases are checked. Signed-off-by: Tor Arvid Lund Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ee504e90ed..a85a7b2a58 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -249,9 +249,16 @@ def p4Where(depotPath): outputList = p4CmdList("where %s" % depotPath) output = None for entry in outputList: - if entry["depotFile"] == depotPath: - output = entry - break + if "depotFile" in entry: + if entry["depotFile"] == depotPath: + output = entry + break + elif "data" in entry: + data = entry.get("data") + space = data.find(" ") + if data[:space] == depotPath: + output = entry + break if output == None: return "" if output["code"] == "error": -- cgit v1.2.3 From 025a19298dca1a94e8d39ea28cb57412c12dfbff Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 15 Dec 2008 10:45:48 -0700 Subject: bash completion: Sort config completion variables Sort the config variables to make sync-ing them with Documents/config.txt easier in the future. Signed-off-by: Lee Marlow Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 77 ++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 37 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c79c98ffec..79cbed589d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1206,84 +1206,87 @@ _git_config () esac __gitcomp " apply.whitespace - core.fileMode - core.gitProxy - core.ignoreStat - core.preferSymlinkRefs - core.logAllRefUpdates - core.loosecompression - core.repositoryFormatVersion - core.sharedRepository - core.warnAmbiguousRefs - core.compression - core.packedGitWindowSize - core.packedGitLimit clean.requireForce color.branch color.branch.current color.branch.local - color.branch.remote color.branch.plain + color.branch.remote color.diff - color.diff.plain - color.diff.meta + color.diff.commit color.diff.frag - color.diff.old + color.diff.meta color.diff.new - color.diff.commit + color.diff.old + color.diff.plain color.diff.whitespace color.pager color.status - color.status.header color.status.added color.status.changed + color.status.header color.status.untracked + core.compression + core.fileMode + core.gitProxy + core.ignoreStat + core.logAllRefUpdates + core.loosecompression + core.packedGitLimit + core.packedGitWindowSize + core.preferSymlinkRefs + core.repositoryFormatVersion + core.sharedRepository + core.warnAmbiguousRefs diff.renameLimit diff.renames fetch.unpackLimit format.headers format.subjectprefix - gitcvs.enabled - gitcvs.logfile - gitcvs.allbinary - gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dbpass - gitcvs.dbtablenameprefix gc.packrefs gc.reflogexpire gc.reflogexpireunreachable gc.rerereresolved gc.rerereunresolved - http.sslVerify - http.sslCert - http.sslKey - http.sslCAInfo - http.sslCAPath - http.maxRequests + gitcvs.allbinary + gitcvs.dbdriver + gitcvs.dbname + gitcvs.dbpass + gitcvs.dbtablenameprefix + gitcvs.dbuser + gitcvs.enabled + gitcvs.logfile http.lowSpeedLimit http.lowSpeedTime + http.maxRequests http.noEPSV + http.sslCAInfo + http.sslCAPath + http.sslCert + http.sslKey + http.sslVerify i18n.commitEncoding i18n.logOutputEncoding log.showroot - merge.tool merge.summary + merge.tool merge.verbosity - pack.window - pack.depth - pack.windowMemory pack.compression - pack.deltaCacheSize pack.deltaCacheLimit + pack.deltaCacheSize + pack.depth + pack.window + pack.windowMemory pull.octopus pull.twohead + receive.denyNonFastForwards + receive.unpackLimit repack.useDeltaBaseOffset showbranch.default tar.umask transfer.unpackLimit - receive.unpackLimit - receive.denyNonFastForwards - user.name user.email + user.name user.signingkey branch. remote. " -- cgit v1.2.3 From 98171a07ae74e796a6c78e7c446ac9a5aaf28c07 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 15 Dec 2008 10:45:49 -0700 Subject: bash completion: Sync config variables with their man pages Add 'normal' to config color options. Add 'mergeoptions' to branch config options. Add 'proxy' and 'mirror' to remote config options. Signed-off-by: Lee Marlow Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 87 +++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 79cbed589d..e00454983e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1155,7 +1155,7 @@ _git_config () ;; color.*.*) __gitcomp " - black red green yellow blue magenta cyan white + normal black red green yellow blue magenta cyan white bold dim ul blink reverse " return @@ -1179,7 +1179,7 @@ _git_config () branch.*.*) local pfx="${cur%.*}." cur="${cur##*.}" - __gitcomp "remote merge" "$pfx" "$cur" + __gitcomp "remote merge mergeoptions" "$pfx" "$cur" return ;; branch.*) @@ -1192,7 +1192,7 @@ _git_config () local pfx="${cur%.*}." cur="${cur##*.}" __gitcomp " - url fetch push skipDefaultUpdate + url proxy fetch push mirror skipDefaultUpdate receivepack uploadpack tagopt " "$pfx" "$cur" return @@ -1206,6 +1206,8 @@ _git_config () esac __gitcomp " apply.whitespace + branch.autosetupmerge + branch.autosetuprebase clean.requireForce color.branch color.branch.current @@ -1220,46 +1222,95 @@ _git_config () color.diff.old color.diff.plain color.diff.whitespace + color.interactive + color.interactive.header + color.interactive.help + color.interactive.prompt color.pager color.status color.status.added color.status.changed color.status.header + color.status.nobranch color.status.untracked + color.status.updated + color.ui + commit.template + core.autocrlf + core.bare core.compression + core.deltaBaseCacheLimit + core.editor + core.excludesfile core.fileMode + core.fsyncobjectfiles core.gitProxy + core.ignoreCygwinFSTricks core.ignoreStat core.logAllRefUpdates core.loosecompression core.packedGitLimit core.packedGitWindowSize + core.pager core.preferSymlinkRefs + core.preloadindex + core.quotepath core.repositoryFormatVersion + core.safecrlf core.sharedRepository + core.symlinks + core.trustctime core.warnAmbiguousRefs + core.whitespace + core.worktree + diff.autorefreshindex + diff.external + diff.mnemonicprefix diff.renameLimit + diff.renameLimit. diff.renames fetch.unpackLimit format.headers - format.subjectprefix + format.numbered + format.pretty + format.suffix + gc.aggressiveWindow + gc.auto + gc.autopacklimit gc.packrefs + gc.pruneexpire gc.reflogexpire gc.reflogexpireunreachable gc.rerereresolved gc.rerereunresolved gitcvs.allbinary + gitcvs.dbTableNamePrefix gitcvs.dbdriver gitcvs.dbname gitcvs.dbpass - gitcvs.dbtablenameprefix gitcvs.dbuser gitcvs.enabled gitcvs.logfile + gitcvs.usecrlfattr + gui.blamehistoryctx + gui.commitmsgwidth + gui.copyblamethreshold + gui.diffcontext + gui.encoding + gui.fastcopyblame + gui.matchtrackingbranch + gui.newbranchtemplate + gui.pruneduringfetch + gui.spellingdictionary + gui.trustmtime + help.autocorrect + help.browser + help.format http.lowSpeedLimit http.lowSpeedTime http.maxRequests http.noEPSV + http.proxy http.sslCAInfo http.sslCAPath http.sslCert @@ -1267,27 +1318,49 @@ _git_config () http.sslVerify i18n.commitEncoding i18n.logOutputEncoding + instaweb.browser + instaweb.httpd + instaweb.local + instaweb.modulepath + instaweb.port + log.date log.showroot - merge.summary + man.viewer + merge.conflictstyle + merge.log + merge.renameLimit + merge.stat merge.tool merge.verbosity + mergetool.keepBackup pack.compression pack.deltaCacheLimit pack.deltaCacheSize pack.depth + pack.indexVersion + pack.packSizeLimit + pack.threads pack.window pack.windowMemory pull.octopus pull.twohead + receive.denyCurrentBranch + receive.denyDeletes receive.denyNonFastForwards + receive.fsckObjects receive.unpackLimit - repack.useDeltaBaseOffset + repack.usedeltabaseoffset + rerere.autoupdate + rerere.enabled showbranch.default + status.relativePaths + status.showUntrackedFiles tar.umask transfer.unpackLimit user.email user.name user.signingkey + web.browser branch. remote. " } -- cgit v1.2.3 From f66bc5f928194366ee5eb78ef18a3562fb1bb7cf Mon Sep 17 00:00:00 2001 From: Richard Hartmann Date: Mon, 22 Dec 2008 00:17:32 +0100 Subject: Always show which directory is not a git repository Unify all fatal: Not a git repository error messages so they include path information. Signed-off-by: Richard Hartmann Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 7959eab902..993cacf324 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -22,7 +22,7 @@ branch=$3 # want to make sure that what is pointed to has a .git directory ... git_dir=$(cd "$orig_git" 2>/dev/null && git rev-parse --git-dir 2>/dev/null) || - die "\"$orig_git\" is not a git repository!" + die "Not a git repository: \"$orig_git\"" case "$git_dir" in .git) -- cgit v1.2.3 From 6d0e674a575421347abe5749e645ca6dc78c8207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 28 Dec 2008 19:45:32 +0100 Subject: diff: add option to show context between close hunks Merge two hunks if there is only the specified number of otherwise unshown context between them. For --inter-hunk-context=1, the resulting patch has the same number of lines but shows uninterrupted context instead of a context header line in between. Patches generated with this option are easier to read but are also more likely to conflict if the file to be patched contains other changes. This patch keeps the default for this option at 0. It is intended to just make the feature available in order to see its advantages and downsides. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e00454983e..a046441974 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -776,6 +776,7 @@ _git_diff () --no-ext-diff --no-prefix --src-prefix= --dst-prefix= --base --ours --theirs + --inter-hunk-context= " return ;; @@ -967,6 +968,7 @@ _git_log () --color-words --walk-reflogs --parents --children --full-history --merge + --inter-hunk-context= " return ;; -- cgit v1.2.3 From e89e2ed7c225cf16cffbd9648895528e471e2fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 29 Dec 2008 16:05:46 +0100 Subject: bash: add '--merge' to 'git reset' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e00454983e..3b25d48098 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1403,7 +1403,7 @@ _git_reset () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--mixed --hard --soft" + __gitcomp "--merge --mixed --hard --soft" return ;; esac -- cgit v1.2.3 From cc545709253fe8440db2648cb5c771e5b126bdb5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 1 Jan 2009 17:39:37 +0100 Subject: bash completions: Add the --patience option Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e00454983e..b98d765deb 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -776,6 +776,7 @@ _git_diff () --no-ext-diff --no-prefix --src-prefix= --dst-prefix= --base --ours --theirs + --patience " return ;; @@ -967,6 +968,7 @@ _git_log () --color-words --walk-reflogs --parents --children --full-history --merge + --patience " return ;; -- cgit v1.2.3 From c9a114b591e42be3ed8e5e4812dfd1031df79a78 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 10 Dec 2008 12:39:17 -0700 Subject: bash completion: Add '--intent-to-add' long option for 'git add' Signed-off-by: Lee Marlow Trivially-Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8ec782dc54..e986e783e2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -563,7 +563,7 @@ _git_add () --*) __gitcomp " --interactive --refresh --patch --update --dry-run - --ignore-errors + --ignore-errors --intent-to-add " return esac -- cgit v1.2.3 From df3987717f1546719a1bf1828fb3714cd5ca9faa Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 10 Dec 2008 12:39:18 -0700 Subject: bash completion: Use 'git add' completions for 'git stage' Signed-off-by: Lee Marlow Trivially-Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e986e783e2..7b074d7985 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1738,6 +1738,7 @@ _git () show) _git_show ;; show-branch) _git_show_branch ;; stash) _git_stash ;; + stage) _git_add ;; submodule) _git_submodule ;; svn) _git_svn ;; tag) _git_tag ;; -- cgit v1.2.3 From 47a845bfc3a9e1d378c43c3b3ea4291d5d79eca8 Mon Sep 17 00:00:00 2001 From: "jidanni@jidanni.org" Date: Tue, 13 Jan 2009 09:19:42 +0800 Subject: contrib/examples/README: give an explanation of the status of these files We attempt to give an explanation of the status of the files in this directory. Signed-off-by: jidanni Signed-off-by: Junio C Hamano --- contrib/examples/README | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 contrib/examples/README (limited to 'contrib') diff --git a/contrib/examples/README b/contrib/examples/README new file mode 100644 index 0000000000..6946f3dd2a --- /dev/null +++ b/contrib/examples/README @@ -0,0 +1,3 @@ +These are original scripted implementations, kept primarily for their +reference value to any aspiring plumbing users who want to learn how +pieces can be fit together. -- cgit v1.2.3 From abc776f7880c2dc0a4179420366e40ecb99d223f Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Tue, 13 Jan 2009 03:10:26 +0100 Subject: contrib/vim: change URL to point to the latest syntax files Vim's SVN repository doesn't offer the latest runtime files, since normally they are only updated there on a release. Though currently there is no difference between the SVN and HTTP/FTP version of the git syntax files. Signed-off-by: Markus Heidelberg Acked-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/vim/README | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/vim/README b/contrib/vim/README index c487346eba..fca1e17251 100644 --- a/contrib/vim/README +++ b/contrib/vim/README @@ -5,11 +5,13 @@ automatically. If you have an older version of vim, you can get the latest syntax files from the vim project: - http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/git.vim - http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitcommit.vim - http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitconfig.vim - http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitrebase.vim - http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitsendemail.vim + http://ftp.vim.org/pub/vim/runtime/syntax/git.vim + http://ftp.vim.org/pub/vim/runtime/syntax/gitcommit.vim + http://ftp.vim.org/pub/vim/runtime/syntax/gitconfig.vim + http://ftp.vim.org/pub/vim/runtime/syntax/gitrebase.vim + http://ftp.vim.org/pub/vim/runtime/syntax/gitsendemail.vim + +These files are also available via FTP at the same location. To install: -- cgit v1.2.3 From 25a31f814016891c7728fdebca056ef47ca75547 Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Thu, 15 Jan 2009 11:02:21 -0500 Subject: bash-completion: Support running when set -u is enabled Under "set -u" semantics, it is an error to access undefined variables. Some user environments may enable this setting in the interactive shell. In any context where the completion functions access an undefined variable, accessing a default empty string (aka "${1-}" instead of "$1") is a reasonable way to code the function, as it silences the undefined variable error while still supplying an empty string. In this patch, functions that should always take an argument still use $1. Functions that have optional arguments use ${1-}. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7b074d7985..5d1515cec4 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -52,7 +52,7 @@ esac __gitdir () { - if [ -z "$1" ]; then + if [ -z "${1-}" ]; then if [ -n "$__git_dir" ]; then echo "$__git_dir" elif [ -d .git ]; then @@ -111,7 +111,7 @@ __git_ps1 () fi fi - if [ -n "$1" ]; then + if [ -n "${1-}" ]; then printf "$1" "${b##refs/heads/}$r" else printf " (%s)" "${b##refs/heads/}$r" @@ -143,8 +143,8 @@ __gitcomp () ;; *) local IFS=$'\n' - COMPREPLY=($(compgen -P "$2" \ - -W "$(__gitcomp_1 "$1" "$4")" \ + COMPREPLY=($(compgen -P "${2-}" \ + -W "$(__gitcomp_1 "${1-}" "${4-}")" \ -- "$cur")) ;; esac @@ -152,13 +152,13 @@ __gitcomp () __git_heads () { - local cmd i is_hash=y dir="$(__gitdir "$1")" + local cmd i is_hash=y dir="$(__gitdir "${1-}")" if [ -d "$dir" ]; then git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ refs/heads return fi - for i in $(git ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "${1-}" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -170,13 +170,13 @@ __git_heads () __git_tags () { - local cmd i is_hash=y dir="$(__gitdir "$1")" + local cmd i is_hash=y dir="$(__gitdir "${1-}")" if [ -d "$dir" ]; then git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ refs/tags return fi - for i in $(git ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "${1-}" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -188,7 +188,7 @@ __git_tags () __git_refs () { - local i is_hash=y dir="$(__gitdir "$1")" + local i is_hash=y dir="$(__gitdir "${1-}")" local cur="${COMP_WORDS[COMP_CWORD]}" format refs if [ -d "$dir" ]; then case "$cur" in -- cgit v1.2.3 From 50e126e185c196b9b66dcdefb4b05f090d62dd4c Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Thu, 15 Jan 2009 11:02:22 -0500 Subject: bash-completion: Try bash completions before simple filetype When a git completion is not found, a bash shell should try bash-type completions first before going to standard filetype completions. This patch adds "-o bashdefault" to the completion line. If that option is not available, it uses the old method. This behavior was inspired by Mercurial's bash completion script. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 5d1515cec4..201f9a6894 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1766,13 +1766,16 @@ _gitk () __git_complete_revlist } -complete -o default -o nospace -F _git git -complete -o default -o nospace -F _gitk gitk +complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \ + || complete -o default -o nospace -F _git git +complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \ + || complete -o default -o nospace -F _gitk gitk # The following are necessary only for Cygwin, and only are needed # when the user has tab-completed the executable name and consequently # included the '.exe' suffix. # if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then -complete -o default -o nospace -F _git git.exe +complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \ + || complete -o default -o nospace -F _git git.exe fi -- cgit v1.2.3 From a42577d4c868141343378824814c058de043e24d Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Thu, 15 Jan 2009 11:02:23 -0500 Subject: bash-completion: Add comments to remind about required arguments Add a few simple comments above commands that take arguments. These comments are meant to remind developers of potential problems that can occur when the script is sourced on systems with "set -u." Any function which requires arguments really ought to be called with explicit arguments given. Also adds a #!bash to the top of bash completions so that editing software can always identify that the file is of sh type. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 201f9a6894..f8b845a4ac 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1,3 +1,4 @@ +#!bash # # bash completion support for core Git. # @@ -50,6 +51,8 @@ case "$COMP_WORDBREAKS" in *) COMP_WORDBREAKS="$COMP_WORDBREAKS:" esac +# __gitdir accepts 0 or 1 arguments (i.e., location) +# returns location of .git repo __gitdir () { if [ -z "${1-}" ]; then @@ -67,6 +70,8 @@ __gitdir () fi } +# __git_ps1 accepts 0 or 1 arguments (i.e., format string) +# returns text to add to bash PS1 prompt (includes branch name) __git_ps1 () { local g="$(git rev-parse --git-dir 2>/dev/null)" @@ -119,6 +124,7 @@ __git_ps1 () fi } +# __gitcomp_1 requires 2 arguments __gitcomp_1 () { local c IFS=' '$'\t'$'\n' @@ -131,6 +137,8 @@ __gitcomp_1 () done } +# __gitcomp accepts 1, 2, 3, or 4 arguments +# generates completion reply with compgen __gitcomp () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -150,6 +158,7 @@ __gitcomp () esac } +# __git_heads accepts 0 or 1 arguments (to pass to __gitdir) __git_heads () { local cmd i is_hash=y dir="$(__gitdir "${1-}")" @@ -168,6 +177,7 @@ __git_heads () done } +# __git_tags accepts 0 or 1 arguments (to pass to __gitdir) __git_tags () { local cmd i is_hash=y dir="$(__gitdir "${1-}")" @@ -186,6 +196,7 @@ __git_tags () done } +# __git_refs accepts 0 or 1 arguments (to pass to __gitdir) __git_refs () { local i is_hash=y dir="$(__gitdir "${1-}")" @@ -218,6 +229,7 @@ __git_refs () done } +# __git_refs2 requires 1 argument (to pass to __git_refs) __git_refs2 () { local i @@ -226,6 +238,7 @@ __git_refs2 () done } +# __git_refs_remotes requires 1 argument (to pass to ls-remote) __git_refs_remotes () { local cmd i is_hash=y @@ -470,6 +483,7 @@ __git_aliases () done } +# __git_aliased_command requires 1 argument __git_aliased_command () { local word cmdline=$(git --git-dir="$(__gitdir)" \ @@ -482,6 +496,7 @@ __git_aliased_command () done } +# __git_find_subcommand requires 1 argument __git_find_subcommand () { local word subcommand c=1 -- cgit v1.2.3 From 7de931c3c2c7850dd7f26c24fbe9d12d223f55f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 16 Jan 2009 17:01:57 +0100 Subject: bash: remove unnecessary checks for long options with argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __gitcomp takes care of it since 5447aac7 (bash: fix long option with argument double completion, 2008-03-05) Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 -- 1 file changed, 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f8b845a4ac..9021220421 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -643,7 +643,6 @@ _git_branch () done case "${COMP_WORDS[COMP_CWORD]}" in - --*=*) COMPREPLY=() ;; --*) __gitcomp " --color --no-color --verbose --abbrev= --no-abbrev @@ -1689,7 +1688,6 @@ _git () if [ -z "$command" ]; then case "${COMP_WORDS[COMP_CWORD]}" in - --*=*) COMPREPLY=() ;; --*) __gitcomp " --paginate --no-pager -- cgit v1.2.3 From 8108513422eac0b0df947ab58f63a6a215faa1ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 16 Jan 2009 17:02:04 +0100 Subject: bash: add missing format-patch command line options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9021220421..80edfcacc9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -838,6 +838,8 @@ _git_format_patch () --not --all --cover-letter --no-prefix --src-prefix= --dst-prefix= + --inline --suffix= --ignore-if-in-upstream + --subject-prefix= " return ;; -- cgit v1.2.3 From 3d279863dedf6c07eefe307b27fb3a08e519140f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 16 Jan 2009 17:02:15 +0100 Subject: bash: refactor 'git log --pretty=' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both 'git log' and 'show' have the same '--pretty=' option with the same formats. So refactor these formats into a common variable. While at it, also add 'format:' to the list. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 80edfcacc9..ec701e8069 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -947,6 +947,8 @@ _git_ls_tree () __git_complete_file } +__git_log_pretty_formats="oneline short medium full fuller email raw format:" + _git_log () { __git_has_doubledash && return @@ -954,8 +956,7 @@ _git_log () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) - __gitcomp " - oneline short medium full fuller email raw + __gitcomp "$__git_log_pretty_formats " "" "${cur##--pretty=}" return ;; @@ -1483,8 +1484,7 @@ _git_show () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) - __gitcomp " - oneline short medium full fuller email raw + __gitcomp "$__git_log_pretty_formats " "" "${cur##--pretty=}" return ;; -- cgit v1.2.3 From 5c38ea31f345d08f37685cf4f50c599a7af56bcf Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Fri, 16 Jan 2009 00:00:02 -0800 Subject: contrib: add 'git difftool' for launching common merge tools 'git difftool' is a git command that allows you to compare and edit files between revisions using common merge tools. 'git difftool' does what 'git mergetool' does but its use is for non-merge situations such as when preparing commits or comparing changes against the index. It uses the same configuration variables as 'git mergetool' and provides the same command-line interface as 'git diff'. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 74 +++++++++++ contrib/difftool/git-difftool-helper | 240 +++++++++++++++++++++++++++++++++++ contrib/difftool/git-difftool.txt | 104 +++++++++++++++ 3 files changed, 418 insertions(+) create mode 100755 contrib/difftool/git-difftool create mode 100755 contrib/difftool/git-difftool-helper create mode 100644 contrib/difftool/git-difftool.txt (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool new file mode 100755 index 0000000000..1fc087c5fc --- /dev/null +++ b/contrib/difftool/git-difftool @@ -0,0 +1,74 @@ +#!/usr/bin/env perl +# Copyright (c) 2009 David Aguilar +# +# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible +# git-difftool-helper script. This script exports +# GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and +# GIT_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper. +# Any arguments that are unknown to this script are forwarded to 'git diff'. + +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Basename qw(dirname); + +my $DIR = abs_path(dirname($0)); + + +sub usage +{ + print << 'USAGE'; + +usage: git difftool [--no-prompt] [--tool=tool] ["git diff" options] +USAGE + exit 1; +} + +sub setup_environment +{ + $ENV{PATH} = "$DIR:$ENV{PATH}"; + $ENV{GIT_PAGER} = ''; + $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper'; +} + +sub exe +{ + my $exe = shift; + return defined $ENV{COMSPEC} ? "$exe.exe" : $exe; +} + +sub generate_command +{ + my @command = (exe('git'), 'diff'); + my $skip_next = 0; + my $idx = -1; + for my $arg (@ARGV) { + $idx++; + if ($skip_next) { + $skip_next = 0; + next; + } + if ($arg eq '-t' or $arg eq '--tool') { + usage() if $#ARGV <= $idx; + $ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1]; + $skip_next = 1; + next; + } + if ($arg =~ /^--tool=/) { + $ENV{GIT_MERGE_TOOL} = substr($arg, 7); + next; + } + if ($arg eq '--no-prompt') { + $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true'; + next; + } + if ($arg eq '-h' or $arg eq '--help') { + usage(); + } + push @command, $arg; + } + return @command +} + +setup_environment(); +exec(generate_command()); diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper new file mode 100755 index 0000000000..0b266e3603 --- /dev/null +++ b/contrib/difftool/git-difftool-helper @@ -0,0 +1,240 @@ +#!/bin/sh +# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher. +# It supports kdiff3, tkdiff, xxdiff, meld, opendiff, emerge, ecmerge, +# vimdiff, gvimdiff, and custom user-configurable tools. +# This script is typically launched by using the 'git difftool' +# convenience command. +# +# Copyright (c) 2009 David Aguilar + +# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt. +should_prompt () { + ! test -n "$GIT_DIFFTOOL_NO_PROMPT" +} + +# Should we keep the backup .orig file? +keep_backup_mode="$(git config --bool merge.keepBackup || echo true)" +keep_backup () { + test "$keep_backup_mode" = "true" +} + +# This function manages the backup .orig file. +# A backup $MERGED.orig file is created if changes are detected. +cleanup_temp_files () { + if test -n "$MERGED"; then + if keep_backup && test "$MERGED" -nt "$BACKUP"; then + test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig" + else + rm -f -- "$BACKUP" + fi + fi +} + +# This is called when users Ctrl-C out of git-difftool-helper +sigint_handler () { + echo + cleanup_temp_files + exit 1 +} + +# This function prepares temporary files and launches the appropriate +# merge tool. +launch_merge_tool () { + # Merged is the filename as it appears in the work tree + # Local is the contents of a/filename + # Remote is the contents of b/filename + # Custom merge tool commands might use $BASE so we provide it + MERGED="$1" + LOCAL="$2" + REMOTE="$3" + BASE="$1" + ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" + BACKUP="$MERGED.BACKUP.$ext" + + # Create and ensure that we clean up $BACKUP + test -f "$MERGED" && cp -- "$MERGED" "$BACKUP" + trap sigint_handler SIGINT + + # $LOCAL and $REMOTE are temporary files so prompt + # the user with the real $MERGED name before launching $merge_tool. + if should_prompt; then + printf "\nViewing: '$MERGED'\n" + printf "Hit return to launch '%s': " "$merge_tool" + read ans + fi + + # Run the appropriate merge tool command + case "$merge_tool" in + kdiff3) + basename=$(basename "$MERGED") + "$merge_tool_path" --auto \ + --L1 "$basename (A)" \ + --L2 "$basename (B)" \ + -o "$MERGED" "$LOCAL" "$REMOTE" \ + > /dev/null 2>&1 + ;; + + tkdiff) + "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE" + ;; + + meld|vimdiff) + "$merge_tool_path" "$LOCAL" "$REMOTE" + ;; + + gvimdiff) + "$merge_tool_path" -f "$LOCAL" "$REMOTE" + ;; + + xxdiff) + "$merge_tool_path" \ + -X \ + -R 'Accel.SaveAsMerged: "Ctrl-S"' \ + -R 'Accel.Search: "Ctrl+F"' \ + -R 'Accel.SearchForward: "Ctrl-G"' \ + --merged-file "$MERGED" \ + "$LOCAL" "$REMOTE" + ;; + + opendiff) + "$merge_tool_path" "$LOCAL" "$REMOTE" \ + -merge "$MERGED" | cat + ;; + + ecmerge) + "$merge_tool_path" "$LOCAL" "$REMOTE" \ + --default --mode=merge2 --to="$MERGED" + ;; + + emerge) + "$merge_tool_path" -f emerge-files-command \ + "$LOCAL" "$REMOTE" "$(basename "$MERGED")" + ;; + + *) + if test -n "$merge_tool_cmd"; then + ( eval $merge_tool_cmd ) + fi + ;; + esac + + cleanup_temp_files +} + +# Verifies that mergetool..cmd exists +valid_custom_tool() { + merge_tool_cmd="$(git config mergetool.$1.cmd)" + test -n "$merge_tool_cmd" +} + +# Verifies that the chosen merge tool is properly setup. +# Built-in merge tools are always valid. +valid_tool() { + case "$1" in + kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) + ;; # happy + *) + if ! valid_custom_tool "$1" + then + return 1 + fi + ;; + esac +} + +# Sets up the merge_tool_path variable. +# This handles the mergetool..path configuration. +init_merge_tool_path() { + merge_tool_path=$(git config mergetool."$1".path) + if test -z "$merge_tool_path"; then + case "$1" in + emerge) + merge_tool_path=emacs + ;; + *) + merge_tool_path="$1" + ;; + esac + fi +} + +# Allow the GIT_MERGE_TOOL variable to provide a default value +test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL" + +# If not merge tool was specified then use the merge.tool +# configuration variable. If that's invalid then reset merge_tool. +if test -z "$merge_tool"; then + merge_tool=$(git config merge.tool) + if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then + echo >&2 "git config option merge.tool set to unknown tool: $merge_tool" + echo >&2 "Resetting to default..." + unset merge_tool + fi +fi + +# Try to guess an appropriate merge tool if no tool has been set. +if test -z "$merge_tool"; then + + # We have a $DISPLAY so try some common UNIX merge tools + if test -n "$DISPLAY"; then + merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff" + # If gnome then prefer meld + if test -n "$GNOME_DESKTOP_SESSION_ID"; then + merge_tool_candidates="meld $merge_tool_candidates" + fi + # If KDE then prefer kdiff3 + if test "$KDE_FULL_SESSION" = "true"; then + merge_tool_candidates="kdiff3 $merge_tool_candidates" + fi + fi + + # $EDITOR is emacs so add emerge as a candidate + if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then + merge_tool_candidates="$merge_tool_candidates emerge" + fi + + # $EDITOR is vim so add vimdiff as a candidate + if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then + merge_tool_candidates="$merge_tool_candidates vimdiff" + fi + + merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" + echo "merge tool candidates: $merge_tool_candidates" + + # Loop over each candidate and stop when a valid merge tool is found. + for i in $merge_tool_candidates + do + init_merge_tool_path $i + if type "$merge_tool_path" > /dev/null 2>&1; then + merge_tool=$i + break + fi + done + + if test -z "$merge_tool" ; then + echo "No known merge resolution program available." + exit 1 + fi + +else + # A merge tool has been set, so verify that it's valid. + if ! valid_tool "$merge_tool"; then + echo >&2 "Unknown merge tool $merge_tool" + exit 1 + fi + + init_merge_tool_path "$merge_tool" + + if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then + echo "The merge tool $merge_tool is not available as '$merge_tool_path'" + exit 1 + fi +fi + + +# Launch the merge tool on each path provided by 'git diff' +while test $# -gt 6 +do + launch_merge_tool "$1" "$2" "$5" + shift 7 +done diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt new file mode 100644 index 0000000000..3940c7057c --- /dev/null +++ b/contrib/difftool/git-difftool.txt @@ -0,0 +1,104 @@ +git-difftool(1) +=============== + +NAME +---- +git-difftool - compare changes using common merge tools + +SYNOPSIS +-------- +'git difftool' [--tool=] [--no-prompt] ['git diff' options] + +DESCRIPTION +----------- +'git difftool' is a git command that allows you to compare and edit files +between revisions using common merge tools. At its most basic level, +'git difftool' does what 'git mergetool' does but its use is for non-merge +situations such as when preparing commits or comparing changes against +the index. + +'git difftool' is a frontend to 'git diff' and accepts the same +arguments and options. + +See linkgit:git-diff[7] for the full list of supported options. + +OPTIONS +------- +-t :: +--tool=:: + Use the merge resolution program specified by . + Valid merge tools are: + kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff ++ +If a merge resolution program is not specified, 'git difftool' +will use the configuration variable `merge.tool`. If the +configuration variable `merge.tool` is not set, 'git difftool' +will pick a suitable default. ++ +You can explicitly provide a full path to the tool by setting the +configuration variable `mergetool..path`. For example, you +can configure the absolute path to kdiff3 by setting +`mergetool.kdiff3.path`. Otherwise, 'git difftool' assumes the +tool is available in PATH. ++ +Instead of running one of the known merge tool programs, +'git difftool' can be customized to run an alternative program +by specifying the command line to invoke in a configuration +variable `mergetool..cmd`. ++ +When 'git difftool' is invoked with this tool (either through the +`-t` or `--tool` option or the `merge.tool` configuration variable) +the configured command line will be invoked with the following +variables available: `$LOCAL` is set to the name of the temporary +file containing the contents of the diff pre-image and `$REMOTE` +is set to the name of the temporary file containing the contents +of the diff post-image. `$BASE` is provided for compatibility +with custom merge tool commands and has the same value as `$LOCAL`. + +--no-prompt:: + Do not prompt before launching a merge tool. + +CONFIG VARIABLES +---------------- +merge.tool:: + The default merge tool to use. ++ +See the `--tool=` option above for more details. + +merge.keepBackup:: + The original, unedited file content can be saved to a file with + a `.orig` extension. Defaults to `true` (i.e. keep the backup files). + +mergetool..path:: + Override the path for the given tool. This is useful in case + your tool is not in the PATH. + +mergetool..cmd:: + Specify the command to invoke the specified merge tool. ++ +See the `--tool=` option above for more details. + + +SEE ALSO +-------- +linkgit:git-diff[7]:: + Show changes between commits, commit and working tree, etc + +linkgit:git-mergetool[1]:: + Run merge conflict resolution tools to resolve merge conflicts + +linkgit:git-config[7]:: + Get and set repository or global options + + +AUTHOR +------ +Written by David Aguilar . + +Documentation +-------------- +Documentation by David Aguilar and the git-list . + +GIT +--- +Part of the linkgit:git[1] suite -- cgit v1.2.3 From e82f0d73f02e89a95d9477911774d314f70f1063 Mon Sep 17 00:00:00 2001 From: Pete Harlan Date: Sat, 17 Jan 2009 20:10:14 -0800 Subject: git-svn: Add --localtime option to "fetch" By default git-svn stores timestamps of fetched commits in Subversion's UTC format. Passing --localtime to fetch will convert them to the timezone of the server on which git-svn is run. This makes the timestamps of a resulting "git log" agree with what "svn log" shows for the same repository. Signed-off-by: Pete Harlan Acked-by: Eric Wong --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ec701e8069..60497a4c2a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1576,7 +1576,7 @@ _git_svn () --follow-parent --authors-file= --repack= --no-metadata --use-svm-props --use-svnsync-props --log-window-size= --no-checkout --quiet - --repack-flags --user-log-author $remote_opts + --repack-flags --user-log-author --localtime $remote_opts " local init_opts=" --template= --shared= --trunk= --tags= -- cgit v1.2.3 From 507cfcbd81196e14053bcd25735aaefabd99395d Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sun, 18 Jan 2009 21:27:19 -0800 Subject: difftool: fix documentation problems This patch makes the difftool docs always refer to the git-difftool script using the dashed form of the name. Only command examples use the non-dashed form now. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 5 ++--- contrib/difftool/git-difftool.txt | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool index 1fc087c5fc..0cda3d2eea 100755 --- a/contrib/difftool/git-difftool +++ b/contrib/difftool/git-difftool @@ -4,7 +4,7 @@ # This is a wrapper around the GIT_EXTERNAL_DIFF-compatible # git-difftool-helper script. This script exports # GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and -# GIT_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper. +# GIT_DIFFTOOL_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper. # Any arguments that are unknown to this script are forwarded to 'git diff'. use strict; @@ -18,8 +18,7 @@ my $DIR = abs_path(dirname($0)); sub usage { print << 'USAGE'; - -usage: git difftool [--no-prompt] [--tool=tool] ["git diff" options] +usage: git difftool [--tool=] [--no-prompt] ["git diff" options] USAGE exit 1; } diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index 3940c7057c..ca3dbd2465 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -11,16 +11,16 @@ SYNOPSIS DESCRIPTION ----------- -'git difftool' is a git command that allows you to compare and edit files +'git-difftool' is a git command that allows you to compare and edit files between revisions using common merge tools. At its most basic level, -'git difftool' does what 'git mergetool' does but its use is for non-merge +'git-difftool' does what 'git-mergetool' does but its use is for non-merge situations such as when preparing commits or comparing changes against the index. 'git difftool' is a frontend to 'git diff' and accepts the same arguments and options. -See linkgit:git-diff[7] for the full list of supported options. +See linkgit:git-diff[1] for the full list of supported options. OPTIONS ------- @@ -30,7 +30,7 @@ OPTIONS Valid merge tools are: kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff + -If a merge resolution program is not specified, 'git difftool' +If a merge resolution program is not specified, 'git-difftool' will use the configuration variable `merge.tool`. If the configuration variable `merge.tool` is not set, 'git difftool' will pick a suitable default. @@ -38,15 +38,15 @@ will pick a suitable default. You can explicitly provide a full path to the tool by setting the configuration variable `mergetool..path`. For example, you can configure the absolute path to kdiff3 by setting -`mergetool.kdiff3.path`. Otherwise, 'git difftool' assumes the +`mergetool.kdiff3.path`. Otherwise, 'git-difftool' assumes the tool is available in PATH. + Instead of running one of the known merge tool programs, -'git difftool' can be customized to run an alternative program +'git-difftool' can be customized to run an alternative program by specifying the command line to invoke in a configuration variable `mergetool..cmd`. + -When 'git difftool' is invoked with this tool (either through the +When 'git-difftool' is invoked with this tool (either through the `-t` or `--tool` option or the `merge.tool` configuration variable) the configured command line will be invoked with the following variables available: `$LOCAL` is set to the name of the temporary @@ -56,7 +56,7 @@ of the diff post-image. `$BASE` is provided for compatibility with custom merge tool commands and has the same value as `$LOCAL`. --no-prompt:: - Do not prompt before launching a merge tool. + Do not prompt before launching a diff tool. CONFIG VARIABLES ---------------- @@ -81,13 +81,13 @@ See the `--tool=` option above for more details. SEE ALSO -------- -linkgit:git-diff[7]:: +linkgit:git-diff[1]:: Show changes between commits, commit and working tree, etc linkgit:git-mergetool[1]:: Run merge conflict resolution tools to resolve merge conflicts -linkgit:git-config[7]:: +linkgit:git-config[1]:: Get and set repository or global options -- cgit v1.2.3 From 28da86a58d7861626eb9d33a1bcfa3e1e79a4d13 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sun, 18 Jan 2009 21:34:29 -0800 Subject: difftool: put the cursor on the editable file for Vim You only need to edit worktree files when comparing against the worktree. Put the cursor automatically into its window for vimdiff and gvimdiff to avoid doing l every time. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index 0b266e3603..f013726d0f 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -78,12 +78,16 @@ launch_merge_tool () { "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE" ;; - meld|vimdiff) + meld) "$merge_tool_path" "$LOCAL" "$REMOTE" ;; + vimdiff) + "$merge_tool_path" -c "wincmd l" "$LOCAL" "$REMOTE" + ;; + gvimdiff) - "$merge_tool_path" -f "$LOCAL" "$REMOTE" + "$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$REMOTE" ;; xxdiff) -- cgit v1.2.3 From 47d5a8fa7188cceb90fe50f1561e64381e8530a3 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 19 Jan 2009 22:17:59 +0100 Subject: bash completion: move pickaxe options to log Move the options --pickaxe-all and --pickaxe-regex to git-log, where they make more sense than with git-diff. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 60497a4c2a..b5d3bbbceb 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -784,7 +784,7 @@ _git_diff () --patch-with-stat --name-only --name-status --color --no-color --color-words --no-renames --check --full-index --binary --abbrev --diff-filter= - --find-copies-harder --pickaxe-all --pickaxe-regex + --find-copies-harder --text --ignore-space-at-eol --ignore-space-change --ignore-all-space --exit-code --quiet --ext-diff --no-ext-diff @@ -986,6 +986,7 @@ _git_log () --parents --children --full-history --merge --inter-hunk-context= + --pickaxe-all --pickaxe-regex " return ;; -- cgit v1.2.3 From 20bf7292314972d4c418056ad94c28c6363058d7 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 19 Jan 2009 22:18:00 +0100 Subject: bash completion: refactor diff options diff, log and show all take the same diff options. Refactor them from __git_diff and __git_log into a variable, and complete them in __git_show too. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 36 ++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b5d3bbbceb..a1298c4f96 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -773,14 +773,7 @@ _git_describe () __gitcomp "$(__git_refs)" } -_git_diff () -{ - __git_has_doubledash && return - - local cur="${COMP_WORDS[COMP_CWORD]}" - case "$cur" in - --*) - __gitcomp "--cached --stat --numstat --shortstat --summary +__git_diff_common_options="--stat --numstat --shortstat --summary --patch-with-stat --name-only --name-status --color --no-color --color-words --no-renames --check --full-index --binary --abbrev --diff-filter= @@ -789,8 +782,20 @@ _git_diff () --ignore-all-space --exit-code --quiet --ext-diff --no-ext-diff --no-prefix --src-prefix= --dst-prefix= - --base --ours --theirs --inter-hunk-context= + --raw +" + +_git_diff () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --pickaxe-all --pickaxe-regex + --base --ours --theirs + $__git_diff_common_options " return ;; @@ -976,16 +981,15 @@ _git_log () --relative-date --date= --author= --committer= --grep= --all-match - --pretty= --name-status --name-only --raw + --pretty= --not --all --left-right --cherry-pick --graph - --stat --numstat --shortstat - --decorate --diff-filter= - --color-words --walk-reflogs + --decorate + --walk-reflogs --parents --children --full-history --merge - --inter-hunk-context= + $__git_diff_common_options --pickaxe-all --pickaxe-regex " return @@ -1490,7 +1494,9 @@ _git_show () return ;; --*) - __gitcomp "--pretty=" + __gitcomp "--pretty= + $__git_diff_common_options + " return ;; esac -- cgit v1.2.3 From f13bfc1be7d25955e3ff5563fb6e35d03a408b4e Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Tue, 20 Jan 2009 00:38:16 +0100 Subject: contrib/difftool: change trap condition from SIGINT to INT git-difftool worked for me on an up-to-date Gentoo Linux at home, but didn't work on a somewhat older Ubuntu Linux 7.10 at work and failed with the following error, where 'Makefile' was locally modified: trap: 244: SIGINT: bad trap external diff died, stopping at Makefile. In 'man 1p trap' there is written: "The condition can be EXIT, 0 (equivalent to EXIT), or a signal specified using a symbolic name, without the SIG prefix, [...]" "Implementations may permit names with the SIG prefix or ignore case in signal names as an extension." So now we do it the POSIX compliant way instead of using an extension. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index f013726d0f..a2eb59b0f0 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -53,7 +53,7 @@ launch_merge_tool () { # Create and ensure that we clean up $BACKUP test -f "$MERGED" && cp -- "$MERGED" "$BACKUP" - trap sigint_handler SIGINT + trap sigint_handler INT # $LOCAL and $REMOTE are temporary files so prompt # the user with the real $MERGED name before launching $merge_tool. -- cgit v1.2.3 From bc08fc4e850794c76da367bb628e508e6d41b1eb Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Tue, 20 Jan 2009 00:41:18 +0100 Subject: contrib/difftool: remove distracting 'echo' in the SIGINT handler When interrupting git-difftool with Ctrl-C, the output of this echo command led to having the cursor at the beginning of the line below the shell prompt. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index a2eb59b0f0..0c48506eeb 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -32,7 +32,6 @@ cleanup_temp_files () { # This is called when users Ctrl-C out of git-difftool-helper sigint_handler () { - echo cleanup_temp_files exit 1 } -- cgit v1.2.3 From f135e72d611ff6faf3d413a85f1620227d9f0705 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Wed, 21 Jan 2009 20:14:55 +0100 Subject: bash completion: add 'rename' subcommand to git-remote Signed-off-by: Markus Heidelberg Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a1298c4f96..703f4c2e90 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1391,7 +1391,7 @@ _git_config () _git_remote () { - local subcommands="add rm show prune update" + local subcommands="add rename rm show prune update" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" @@ -1399,7 +1399,7 @@ _git_remote () fi case "$subcommand" in - rm|show|prune) + rename|rm|show|prune) __gitcomp "$(__git_remotes)" ;; update) -- cgit v1.2.3 From 384770a5e79938b6a7633c5996597ef3211e4a7c Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Sat, 31 Jan 2009 00:19:29 +0100 Subject: contrib/difftool: add support for Kompare Signed-off-by: Markus Heidelberg Acked-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 16 ++++++++++------ contrib/difftool/git-difftool.txt | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index 0c48506eeb..10632a3917 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -1,7 +1,7 @@ #!/bin/sh # git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher. -# It supports kdiff3, tkdiff, xxdiff, meld, opendiff, emerge, ecmerge, -# vimdiff, gvimdiff, and custom user-configurable tools. +# It supports kdiff3, kompare, tkdiff, xxdiff, meld, opendiff, +# emerge, ecmerge, vimdiff, gvimdiff, and custom user-configurable tools. # This script is typically launched by using the 'git difftool' # convenience command. # @@ -73,6 +73,10 @@ launch_merge_tool () { > /dev/null 2>&1 ;; + kompare) + "$merge_tool_path" "$LOCAL" "$REMOTE" + ;; + tkdiff) "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE" ;; @@ -134,7 +138,7 @@ valid_custom_tool() { # Built-in merge tools are always valid. valid_tool() { case "$1" in - kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) + kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) ;; # happy *) if ! valid_custom_tool "$1" @@ -180,14 +184,14 @@ if test -z "$merge_tool"; then # We have a $DISPLAY so try some common UNIX merge tools if test -n "$DISPLAY"; then - merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff" + merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff" # If gnome then prefer meld if test -n "$GNOME_DESKTOP_SESSION_ID"; then merge_tool_candidates="meld $merge_tool_candidates" fi - # If KDE then prefer kdiff3 + # If KDE then prefer kdiff3 or kompare if test "$KDE_FULL_SESSION" = "true"; then - merge_tool_candidates="kdiff3 $merge_tool_candidates" + merge_tool_candidates="kdiff3 kompare $merge_tool_candidates" fi fi diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index ca3dbd2465..6e2610cda6 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -28,7 +28,8 @@ OPTIONS --tool=:: Use the merge resolution program specified by . Valid merge tools are: - kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff + kdiff3, kompare, tkdiff, meld, xxdiff, emerge, + vimdiff, gvimdiff, ecmerge, and opendiff + If a merge resolution program is not specified, 'git-difftool' will use the configuration variable `merge.tool`. If the -- cgit v1.2.3 From 99ccabaffa201e867f2073947dcccae3947ec4f1 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sat, 31 Jan 2009 12:27:56 -0800 Subject: contrib/difftool: Don't repeat merge tool candidates git difftool listed some candidates for mergetools twice, depending on the environment. This slightly changes the behavior when both KDE_FULL_SESSION and GNOME_DESKTOP_SESSION_ID are set at the same time; in such a case meld is used in favor of kdiff3 (the old code favored kdiff3 in such a case), but it should not matter in practice. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index 10632a3917..db3af6a833 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -181,31 +181,24 @@ fi # Try to guess an appropriate merge tool if no tool has been set. if test -z "$merge_tool"; then - # We have a $DISPLAY so try some common UNIX merge tools if test -n "$DISPLAY"; then - merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff" - # If gnome then prefer meld - if test -n "$GNOME_DESKTOP_SESSION_ID"; then - merge_tool_candidates="meld $merge_tool_candidates" - fi - # If KDE then prefer kdiff3 or kompare - if test "$KDE_FULL_SESSION" = "true"; then - merge_tool_candidates="kdiff3 kompare $merge_tool_candidates" + # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare + if test -n "$GNOME_DESKTOP_SESSION_ID" ; then + merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff" + else + merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff" fi fi - - # $EDITOR is emacs so add emerge as a candidate if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then - merge_tool_candidates="$merge_tool_candidates emerge" + # $EDITOR is emacs so add emerge as a candidate + merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff" + elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then + # $EDITOR is vim so add vimdiff as a candidate + merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge" + else + merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" fi - - # $EDITOR is vim so add vimdiff as a candidate - if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then - merge_tool_candidates="$merge_tool_candidates vimdiff" - fi - - merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" echo "merge tool candidates: $merge_tool_candidates" # Loop over each candidate and stop when a valid merge tool is found. -- cgit v1.2.3 From 738a94a9f645a5f63fbccaab76d5e43a97e53d78 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Tue, 3 Feb 2009 10:20:54 +0100 Subject: bash: offer to show (un)staged changes Add a bit of code to __git_ps1 that lets it append '*' to the branch name if there are any unstaged changes, and '+' if there are any staged changes. Since this is a rather expensive operation and will force a lot of data into the cache whenever you first enter a repository, you have to enable it manually by setting GIT_PS1_SHOWDIRTYSTATE to a nonempty value. The configuration variable bash.showDirtyState can then be used to disable it again for some repositories. Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 81f70ec644..307bf5d4f9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -34,6 +34,12 @@ # are currently in a git repository. The %s token will be # the name of the current branch. # +# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty +# value, unstaged (*) and staged (+) changes will be shown next +# to the branch name. You can configure this per-repository +# with the bash.showDirtyState variable, which defaults to true +# once GIT_PS1_SHOWDIRTYSTATE is enabled. +# # To submit patches: # # *) Read Documentation/SubmittingPatches @@ -116,10 +122,26 @@ __git_ps1 () fi fi + local w + local i + + if test -n "$GIT_PS1_SHOWDIRTYSTATE"; then + if test "$(git config --bool bash.showDirtyState)" != "false"; then + git diff --no-ext-diff --ignore-submodules \ + --quiet --exit-code || w="*" + if git rev-parse --quiet --verify HEAD >/dev/null; then + git diff-index --cached --quiet \ + --ignore-submodules HEAD -- || i="+" + else + i="#" + fi + fi + fi + if [ -n "${1-}" ]; then - printf "$1" "${b##refs/heads/}$r" + printf "$1" "${b##refs/heads/}$w$i$r" else - printf " (%s)" "${b##refs/heads/}$r" + printf " (%s)" "${b##refs/heads/}$w$i$r" fi fi } -- cgit v1.2.3 From e1ff064e1bffedb917c491681b7bfd4d66633ef5 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Wed, 4 Feb 2009 11:04:18 +0100 Subject: contrib git-resurrect: find traces of a branch name and resurrect it Add a tool 'git-resurrect.sh ' that tries to find traces of the in the HEAD reflog and, optionally, all merge commits in the repository. It can then resurrect the branch, pointing it at the most recent of all candidate commits found. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- contrib/git-resurrect.sh | 180 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100755 contrib/git-resurrect.sh (limited to 'contrib') diff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh new file mode 100755 index 0000000000..c364dda696 --- /dev/null +++ b/contrib/git-resurrect.sh @@ -0,0 +1,180 @@ +#!/bin/sh + +USAGE="[-a] [-r] [-m] [-t] [-n] [-b ] " +LONG_USAGE="git-resurrect attempts to find traces of a branch tip +called , and tries to resurrect it. Currently, the reflog is +searched for checkout messages, and with -r also merge messages. With +-m and -t, the history of all refs is scanned for Merge into +other/Merge into (respectively) commit subjects, which +is rather slow but allows you to resurrect other people's topic +branches." + +OPTIONS_SPEC="\ +git resurrect $USAGE +-- +b,branch= save branch as instead of +a,all same as -l -r -m -t +k,keep-going full rev-list scan (instead of first match) +l,reflog scan reflog for checkouts (enabled by default) +r,reflog-merges scan for merges recorded in reflog +m,merges scan for merges into other branches (slow) +t,merge-targets scan for merges of other branches into +n,dry-run don't recreate the branch" + +. git-sh-setup + +search_reflog () { + sed -ne 's~^\([^ ]*\) .*\tcheckout: moving from '"$1"' .*~\1~p' \ + < "$GIT_DIR"/logs/HEAD +} + +search_reflog_merges () { + git rev-parse $( + sed -ne 's~^[^ ]* \([^ ]*\) .*\tmerge '"$1"':.*~\1^2~p' \ + < "$GIT_DIR"/logs/HEAD + ) +} + +_x40="[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]" +_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" + +search_merges () { + git rev-list --all --grep="Merge branch '$1'" \ + --pretty=tformat:"%P %s" | + sed -ne "/^$_x40 \($_x40\) Merge .*/ {s//\1/p;$early_exit}" +} + +search_merge_targets () { + git rev-list --all --grep="Merge branch '[^']*' into $branch\$" \ + --pretty=tformat:"%H %s" --all | + sed -ne "/^\($_x40\) Merge .*/ {s//\1/p;$early_exit} " +} + +dry_run= +early_exit=q +scan_reflog=t +scan_reflog_merges= +scan_merges= +scan_merge_targets= +new_name= + +while test "$#" != 0; do + case "$1" in + -b|--branch) + shift + new_name="$1" + ;; + -n|--dry-run) + dry_run=t + ;; + --no-dry-run) + dry_run= + ;; + -k|--keep-going) + early_exit= + ;; + --no-keep-going) + early_exit=q + ;; + -m|--merges) + scan_merges=t + ;; + --no-merges) + scan_merges= + ;; + -l|--reflog) + scan_reflog=t + ;; + --no-reflog) + scan_reflog= + ;; + -r|--reflog_merges) + scan_reflog_merges=t + ;; + --no-reflog_merges) + scan_reflog_merges= + ;; + -t|--merge-targets) + scan_merge_targets=t + ;; + --no-merge-targets) + scan_merge_targets= + ;; + -a|--all) + scan_reflog=t + scan_reflog_merges=t + scan_merges=t + scan_merge_targets=t + ;; + --) + shift + break + ;; + *) + usage + ;; + esac + shift +done + +test "$#" = 1 || usage + +all_strategies="$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets" +if test -z "$all_strategies"; then + die "must enable at least one of -lrmt" +fi + +branch="$1" +test -z "$new_name" && new_name="$branch" + +if test ! -z "$scan_reflog"; then + if test -r "$GIT_DIR"/logs/HEAD; then + candidates="$(search_reflog $branch)" + else + die 'reflog scanning requested, but' \ + '$GIT_DIR/logs/HEAD not readable' + fi +fi +if test ! -z "$scan_reflog_merges"; then + if test -r "$GIT_DIR"/logs/HEAD; then + candidates="$candidates $(search_reflog_merges $branch)" + else + die 'reflog scanning requested, but' \ + '$GIT_DIR/logs/HEAD not readable' + fi +fi +if test ! -z "$scan_merges"; then + candidates="$candidates $(search_merges $branch)" +fi +if test ! -z "$scan_merge_targets"; then + candidates="$candidates $(search_merge_targets $branch)" +fi + +candidates="$(git rev-parse $candidates | sort -u)" + +if test -z "$candidates"; then + hint= + test "z$all_strategies" != "ztttt" \ + && hint=" (maybe try again with -a)" + die "no candidates for $branch found$hint" +fi + +echo "** Candidates for $branch **" +for cmt in $candidates; do + git --no-pager log --pretty=tformat:"%ct:%h [%cr] %s" --abbrev-commit -1 $cmt +done \ +| sort -n | cut -d: -f2- + +newest="$(git rev-list -1 $candidates)" +if test ! -z "$dry_run"; then + printf "** Most recent: " + git --no-pager log -1 --pretty=tformat:"%h %s" $newest +elif ! git rev-parse --verify --quiet $new_name >/dev/null; then + printf "** Restoring $new_name to " + git --no-pager log -1 --pretty=tformat:"%h %s" $newest + git branch $new_name $newest +else + printf "Most recent: " + git --no-pager log -1 --pretty=tformat:"%h %s" $newest + echo "** $new_name already exists, doing nothing" +fi -- cgit v1.2.3 From c375e9d04cbcaaca7ae459437d185eda0a4472b4 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 23 Nov 2008 14:16:22 +0100 Subject: git.el: Add a checkout command. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prompts for a branch name and checks it out. Bound to C-c C-o by default. Based on a patch by Rémi Vanicat . Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 09e8bae3a4..5ce9bf19a7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -39,10 +39,8 @@ ;; - renaming files from the status buffer ;; - creating tags ;; - fetch/pull -;; - switching branches ;; - revlist browser ;; - git-show-branch browser -;; - menus ;; (eval-when-compile (require 'cl)) @@ -397,6 +395,17 @@ the process output as a string, or nil if the git command failed." (unless newval (push "-d" args)) (apply 'git-call-process-display-error "update-ref" args))) +(defun git-for-each-ref (&rest specs) + "Return a list of refs using git-for-each-ref. +Each entry is a cons of (SHORT-NAME . FULL-NAME)." + (let (refs) + (with-temp-buffer + (apply #'git-call-process t "for-each-ref" "--format=%(refname)" specs) + (goto-char (point-min)) + (while (re-search-forward "^[^/\n]+/[^/\n]+/\\(.+\\)$" nil t) + (push (cons (match-string 1) (match-string 0)) refs))) + (nreverse refs))) + (defun git-read-tree (tree &optional index-file) "Read a tree into the index file." (let ((process-environment @@ -1356,6 +1365,24 @@ Return the list of files that haven't been handled." (push (match-string 1) files))) files)) +(defun git-read-commit-name (prompt &optional default) + "Ask for a commit name, with completion for local branch, remote branch and tag." + (completing-read prompt + (list* "HEAD" "ORIG_HEAD" "FETCH_HEAD" (mapcar #'car (git-for-each-ref))) + nil nil nil nil default)) + +(defun git-checkout (branch &optional merge) + "Checkout a branch, tag, or any commit. +Use a prefix arg if git should merge while checking out." + (interactive + (list (git-read-commit-name "Checkout: ") + current-prefix-arg)) + (unless git-status (error "Not in git-status buffer.")) + (let ((args (list branch "--"))) + (when merge (push "-m" args)) + (when (apply #'git-call-process-display-error "checkout" args) + (git-update-status-files)))) + (defun git-amend-commit () "Undo the last commit on HEAD, and set things up to commit an amended version of it." @@ -1471,6 +1498,7 @@ amended version of it." (define-key map "\M-\C-?" 'git-unmark-all) ; the commit submap (define-key commit-map "\C-a" 'git-amend-commit) + (define-key commit-map "\C-o" 'git-checkout) ; the diff submap (define-key diff-map "b" 'git-diff-file-base) (define-key diff-map "c" 'git-diff-file-combined) @@ -1491,6 +1519,7 @@ amended version of it." `("Git" ["Refresh" git-refresh-status t] ["Commit" git-commit-file t] + ["Checkout..." git-checkout t] ("Merge" ["Next Unmerged File" git-next-unmerged-file t] ["Prev Unmerged File" git-prev-unmerged-file t] -- cgit v1.2.3 From 811b10c746a63d1818d52c9ecbf247d9a3891597 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 23 Nov 2008 14:25:50 +0100 Subject: git.el: Add a command to create a new branch. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prompts for a branch name, create a new branch at HEAD and switch to it. Bound to C-c C-b by default. Based on a patch by Rémi Vanicat . Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 5ce9bf19a7..6727ff54be 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1383,6 +1383,18 @@ Use a prefix arg if git should merge while checking out." (when (apply #'git-call-process-display-error "checkout" args) (git-update-status-files)))) +(defun git-branch (branch) + "Create a branch from the current HEAD and switch to it." + (interactive (list (git-read-commit-name "Branch: "))) + (unless git-status (error "Not in git-status buffer.")) + (if (git-rev-parse (concat "refs/heads/" branch)) + (if (yes-or-no-p (format "Branch %s already exists, replace it? " branch)) + (and (git-call-process-display-error "branch" "-f" branch) + (git-call-process-display-error "checkout" branch)) + (message "Canceled.")) + (git-call-process-display-error "checkout" "-b" branch)) + (git-refresh-ewoc-hf git-status)) + (defun git-amend-commit () "Undo the last commit on HEAD, and set things up to commit an amended version of it." @@ -1498,6 +1510,7 @@ amended version of it." (define-key map "\M-\C-?" 'git-unmark-all) ; the commit submap (define-key commit-map "\C-a" 'git-amend-commit) + (define-key commit-map "\C-b" 'git-branch) (define-key commit-map "\C-o" 'git-checkout) ; the diff submap (define-key diff-map "b" 'git-diff-file-base) @@ -1520,6 +1533,7 @@ amended version of it." ["Refresh" git-refresh-status t] ["Commit" git-commit-file t] ["Checkout..." git-checkout t] + ["New Branch..." git-branch t] ("Merge" ["Next Unmerged File" git-next-unmerged-file t] ["Prev Unmerged File" git-prev-unmerged-file t] -- cgit v1.2.3 From ab69e3e43a12cf02505f3e9e561c49c1fe8a81a6 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 23 Nov 2008 14:34:48 +0100 Subject: git.el: Add commands for cherry-pick and revert. Support for cherry-picking and reverting commits, with automatic formatting of the commit log message. Bound to C-c C-p and C-c C-v respectively. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 6727ff54be..b7ea636534 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1411,6 +1411,44 @@ amended version of it." (git-setup-commit-buffer commit) (git-commit-file)))) +(defun git-cherry-pick-commit (arg) + "Cherry-pick a commit." + (interactive (list (git-read-commit-name "Cherry-pick commit: "))) + (unless git-status (error "Not in git-status buffer.")) + (let ((commit (git-rev-parse (concat arg "^0")))) + (unless commit (error "Not a valid commit '%s'." arg)) + (when (git-rev-parse (concat commit "^2")) + (error "Cannot cherry-pick a merge commit.")) + (let ((files (git-get-commit-files commit)) + (ok (git-call-process-display-error "cherry-pick" "-n" commit))) + (git-update-status-files files ok) + (with-current-buffer (git-setup-commit-buffer commit) + (goto-char (point-min)) + (if (re-search-forward "^\n*Signed-off-by:" nil t 1) + (goto-char (match-beginning 0)) + (goto-char (point-max))) + (insert "(cherry picked from commit " commit ")\n")) + (when ok (git-commit-file))))) + +(defun git-revert-commit (arg) + "Revert a commit." + (interactive (list (git-read-commit-name "Revert commit: "))) + (unless git-status (error "Not in git-status buffer.")) + (let ((commit (git-rev-parse (concat arg "^0")))) + (unless commit (error "Not a valid commit '%s'." arg)) + (when (git-rev-parse (concat commit "^2")) + (error "Cannot revert a merge commit.")) + (let ((files (git-get-commit-files commit)) + (subject (git-get-commit-description commit)) + (ok (git-call-process-display-error "revert" "-n" commit))) + (git-update-status-files files ok) + (when (string-match "^[0-9a-f]+ - \\(.*\\)$" subject) + (setq subject (match-string 1 subject))) + (git-setup-log-buffer (get-buffer-create "*git-commit*") + (git-get-merge-heads) nil nil (format "Revert \"%s\"" subject) nil + (format "This reverts commit %s.\n" commit)) + (when ok (git-commit-file))))) + (defun git-find-file () "Visit the current file in its own buffer." (interactive) @@ -1512,6 +1550,8 @@ amended version of it." (define-key commit-map "\C-a" 'git-amend-commit) (define-key commit-map "\C-b" 'git-branch) (define-key commit-map "\C-o" 'git-checkout) + (define-key commit-map "\C-p" 'git-cherry-pick-commit) + (define-key commit-map "\C-v" 'git-revert-commit) ; the diff submap (define-key diff-map "b" 'git-diff-file-base) (define-key diff-map "c" 'git-diff-file-combined) @@ -1534,6 +1574,8 @@ amended version of it." ["Commit" git-commit-file t] ["Checkout..." git-checkout t] ["New Branch..." git-branch t] + ["Cherry-pick Commit..." git-cherry-pick-commit t] + ["Revert Commit..." git-revert-commit t] ("Merge" ["Next Unmerged File" git-next-unmerged-file t] ["Prev Unmerged File" git-prev-unmerged-file t] -- cgit v1.2.3 From a7da5c425970372f75d7cc2c194d5646554f8a32 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 23 Nov 2008 16:12:45 +0100 Subject: git.el: Make git-run-command-region display the error if any. This makes it easier to figure out why a commit has failed. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index b7ea636534..415765ec51 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -220,7 +220,7 @@ the process output as a string, or nil if the git command failed." (with-current-buffer buffer (cd dir) (apply #'call-process-region start end program - nil (list output-buffer nil) nil args)))) + nil (list output-buffer t) nil args)))) (defun git-run-command-buffer (buffer-name &rest args) "Run a git command, sending the output to a buffer named BUFFER-NAME." @@ -237,13 +237,15 @@ the process output as a string, or nil if the git command failed." (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (unless (eq 0 (if env - (git-run-process-region - buffer start end "env" - (append (git-get-env-strings env) (list "git") args)) + (with-temp-buffer + (if (eq 0 (if env (git-run-process-region - buffer start end "git" args))) - (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) + buffer start end "env" + (append (git-get-env-strings env) (list "git") args)) + (git-run-process-region buffer start end "git" args))) + (buffer-string) + (display-message-or-buffer (current-buffer)) + nil))) (defun git-run-hook (hook env &rest args) "Run a git hook and display its output if any." @@ -456,18 +458,16 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (setq coding-system-for-write buffer-file-coding-system)) (let ((commit (git-get-string-sha1 - (with-output-to-string - (with-current-buffer standard-output - (let ((env `(("GIT_AUTHOR_NAME" . ,author-name) - ("GIT_AUTHOR_EMAIL" . ,author-email) - ("GIT_COMMITTER_NAME" . ,(git-get-committer-name)) - ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email))))) - (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env)) - (apply #'git-run-command-region - buffer log-start log-end env - "commit-tree" tree (nreverse args)))))))) - (and (git-update-ref "HEAD" commit head subject) - commit)))) + (let ((env `(("GIT_AUTHOR_NAME" . ,author-name) + ("GIT_AUTHOR_EMAIL" . ,author-email) + ("GIT_COMMITTER_NAME" . ,(git-get-committer-name)) + ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email))))) + (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env)) + (apply #'git-run-command-region + buffer log-start log-end env + "commit-tree" tree (nreverse args)))))) + (when commit (git-update-ref "HEAD" commit head subject)) + commit))) (defun git-empty-db-p () "Check if the git db is empty (no commit done yet)." -- cgit v1.2.3 From efd49f50fc087df2ad46f194ca848c5335f4cca9 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 27 Jan 2009 11:59:54 +0100 Subject: git.el: Set a regexp for paragraph-separate in log-edit mode. This allows using fill-paragraph on the log message without interference from the various header fields. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 415765ec51..f86c437518 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1329,6 +1329,7 @@ Return the list of files that haven't been handled." (log-edit-diff-function . git-log-edit-diff)) buffer) (log-edit 'git-do-commit nil 'git-log-edit-files buffer)) (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) + (setq paragraph-separate (concat (regexp-quote git-log-msg-separator) "$\\|Author: \\|Date: \\|Merge: \\|Signed-off-by: \\|\f\\|[ ]*$")) (setq buffer-file-coding-system coding-system) (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) -- cgit v1.2.3 From 6c4f70d5b2fb8f9275ca85e0927f00b8bc892819 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 7 Feb 2009 14:01:26 +0100 Subject: git.el: Use integer instead of character constants in case statement. This is for compatibility with XEmacs. Reported by Vassili Karpov. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index f86c437518..7651a0a8e1 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -571,29 +571,29 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (let* ((old-type (lsh (or old-perm 0) -9)) (new-type (lsh (or new-perm 0) -9)) (str (case new-type - (?\100 ;; file + (64 ;; file (case old-type - (?\100 nil) - (?\120 " (type change symlink -> file)") - (?\160 " (type change subproject -> file)"))) - (?\120 ;; symlink + (64 nil) + (80 " (type change symlink -> file)") + (112 " (type change subproject -> file)"))) + (80 ;; symlink (case old-type - (?\100 " (type change file -> symlink)") - (?\160 " (type change subproject -> symlink)") + (64 " (type change file -> symlink)") + (112 " (type change subproject -> symlink)") (t " (symlink)"))) - (?\160 ;; subproject + (112 ;; subproject (case old-type - (?\100 " (type change file -> subproject)") - (?\120 " (type change symlink -> subproject)") + (64 " (type change file -> subproject)") + (80 " (type change symlink -> subproject)") (t " (subproject)"))) - (?\110 nil) ;; directory (internal, not a real git state) - (?\000 ;; deleted or unknown + (72 nil) ;; directory (internal, not a real git state) + (0 ;; deleted or unknown (case old-type - (?\120 " (symlink)") - (?\160 " (subproject)"))) + (80 " (symlink)") + (112 " (subproject)"))) (t (format " (unknown type %o)" new-type))))) (cond (str (propertize str 'face 'git-status-face)) - ((eq new-type ?\110) "/") + ((eq new-type 72) "/") (t "")))) (defun git-rename-as-string (info) -- cgit v1.2.3 From 5a7b3bf5275adf86fdd23f8824562b88c8a20e33 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 7 Feb 2009 14:21:58 +0100 Subject: git.el: Add some notes about Emacs versions compatibility. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 7651a0a8e1..fcbe2d9cf5 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1,6 +1,6 @@ ;;; git.el --- A user interface for git -;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard +;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Alexandre Julliard ;; Version: 1.0 @@ -34,7 +34,6 @@ ;; To start: `M-x git-status' ;; ;; TODO -;; - portability to XEmacs ;; - diff against other branch ;; - renaming files from the status buffer ;; - creating tags @@ -43,6 +42,15 @@ ;; - git-show-branch browser ;; +;;; Compatibility: +;; +;; This file works on GNU Emacs 21 or later. It may work on older +;; versions but this is not guaranteed. +;; +;; It may work on XEmacs 21, provided that you first install the ewoc +;; and log-edit packages. +;; + (eval-when-compile (require 'cl)) (require 'ewoc) (require 'log-edit) -- cgit v1.2.3 From 7851386948dce72c739bcdfe08f069afe4f5ea45 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 7 Feb 2009 14:24:54 +0100 Subject: emacs: Remove the no longer maintained vc-git package. vc-git is distributed with Emacs since version 22.2, and is maintained in the Emacs CVS tree. This file is obsolete and causes trouble for people who want to add contrib/emacs to their load-path. Signed-off-by: Alexandre Julliard --- contrib/emacs/Makefile | 2 +- contrib/emacs/vc-git.el | 216 ------------------------------------------------ 2 files changed, 1 insertion(+), 217 deletions(-) delete mode 100644 contrib/emacs/vc-git.el (limited to 'contrib') diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile index a48540a92b..24d9312941 100644 --- a/contrib/emacs/Makefile +++ b/contrib/emacs/Makefile @@ -2,7 +2,7 @@ EMACS = emacs -ELC = git.elc vc-git.elc git-blame.elc +ELC = git.elc git-blame.elc INSTALL ?= install INSTALL_ELC = $(INSTALL) -m 644 prefix ?= $(HOME) diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el deleted file mode 100644 index b8f6be5c0a..0000000000 --- a/contrib/emacs/vc-git.el +++ /dev/null @@ -1,216 +0,0 @@ -;;; vc-git.el --- VC backend for the git version control system - -;; Copyright (C) 2006 Alexandre Julliard - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License as -;; published by the Free Software Foundation; either version 2 of -;; the License, or (at your option) any later version. -;; -;; This program is distributed in the hope that it will be -;; useful, but WITHOUT ANY WARRANTY; without even the implied -;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -;; PURPOSE. See the GNU General Public License for more details. -;; -;; You should have received a copy of the GNU General Public -;; License along with this program; if not, write to the Free -;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, -;; MA 02111-1307 USA - -;;; Commentary: - -;; This file contains a VC backend for the git version control -;; system. -;; -;; To install: put this file on the load-path and add GIT to the list -;; of supported backends in `vc-handled-backends'; the following line, -;; placed in your ~/.emacs, will accomplish this: -;; -;; (add-to-list 'vc-handled-backends 'GIT) -;; -;; TODO -;; - changelog generation -;; - working with revisions other than HEAD -;; - -(eval-when-compile (require 'cl)) - -(defvar git-commits-coding-system 'utf-8 - "Default coding system for git commits.") - -(defun vc-git--run-command-string (file &rest args) - "Run a git command on FILE and return its output as string." - (let* ((ok t) - (str (with-output-to-string - (with-current-buffer standard-output - (unless (eq 0 (apply #'call-process "git" nil '(t nil) nil - (append args (list (file-relative-name file))))) - (setq ok nil)))))) - (and ok str))) - -(defun vc-git--run-command (file &rest args) - "Run a git command on FILE, discarding any output." - (let ((name (file-relative-name file))) - (eq 0 (apply #'call-process "git" nil (get-buffer "*Messages") nil (append args (list name)))))) - -(defun vc-git-registered (file) - "Check whether FILE is registered with git." - (with-temp-buffer - (let* ((dir (file-name-directory file)) - (name (file-relative-name file dir))) - (and (ignore-errors - (when dir (cd dir)) - (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name))) - (let ((str (buffer-string))) - (and (> (length str) (length name)) - (string= (substring str 0 (1+ (length name))) (concat name "\0")))))))) - -(defun vc-git-state (file) - "git-specific version of `vc-state'." - (let ((diff (vc-git--run-command-string file "diff-index" "-z" "HEAD" "--"))) - (if (and diff (string-match ":[0-7]\\{6\\} [0-7]\\{6\\} [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} [ADMU]\0[^\0]+\0" diff)) - 'edited - 'up-to-date))) - -(defun vc-git-workfile-version (file) - "git-specific version of `vc-workfile-version'." - (let ((str (with-output-to-string - (with-current-buffer standard-output - (call-process "git" nil '(t nil) nil "symbolic-ref" "HEAD"))))) - (if (string-match "^\\(refs/heads/\\)?\\(.+\\)$" str) - (match-string 2 str) - str))) - -(defun vc-git-symbolic-commit (commit) - "Translate COMMIT string into symbolic form. -Returns nil if not possible." - (and commit - (with-temp-buffer - (and - (zerop - (call-process "git" nil '(t nil) nil "name-rev" - "--name-only" "--tags" - commit)) - (goto-char (point-min)) - (= (forward-line 2) 1) - (bolp) - (buffer-substring-no-properties (point-min) (1- (point-max))))))) - -(defun vc-git-previous-version (file rev) - "git-specific version of `vc-previous-version'." - (let ((default-directory (file-name-directory (expand-file-name file))) - (file (file-name-nondirectory file))) - (vc-git-symbolic-commit - (with-temp-buffer - (and - (zerop - (call-process "git" nil '(t nil) nil "rev-list" - "-2" rev "--" file)) - (goto-char (point-max)) - (bolp) - (zerop (forward-line -1)) - (not (bobp)) - (buffer-substring-no-properties - (point) - (1- (point-max)))))))) - -(defun vc-git-next-version (file rev) - "git-specific version of `vc-next-version'." - (let* ((default-directory (file-name-directory - (expand-file-name file))) - (file (file-name-nondirectory file)) - (current-rev - (with-temp-buffer - (and - (zerop - (call-process "git" nil '(t nil) nil "rev-list" - "-1" rev "--" file)) - (goto-char (point-max)) - (bolp) - (zerop (forward-line -1)) - (bobp) - (buffer-substring-no-properties - (point) - (1- (point-max))))))) - (and current-rev - (vc-git-symbolic-commit - (with-temp-buffer - (and - (zerop - (call-process "git" nil '(t nil) nil "rev-list" - "HEAD" "--" file)) - (goto-char (point-min)) - (search-forward current-rev nil t) - (zerop (forward-line -1)) - (buffer-substring-no-properties - (point) - (progn (forward-line 1) (1- (point)))))))))) - -(defun vc-git-revert (file &optional contents-done) - "Revert FILE to the version stored in the git repository." - (if contents-done - (vc-git--run-command file "update-index" "--") - (vc-git--run-command file "checkout" "HEAD"))) - -(defun vc-git-checkout-model (file) - 'implicit) - -(defun vc-git-workfile-unchanged-p (file) - (let ((sha1 (vc-git--run-command-string file "hash-object" "--")) - (head (vc-git--run-command-string file "ls-tree" "-z" "HEAD" "--"))) - (and head - (string-match "[0-7]\\{6\\} blob \\([0-9a-f]\\{40\\}\\)\t[^\0]+\0" head) - (string= (car (split-string sha1 "\n")) (match-string 1 head))))) - -(defun vc-git-register (file &optional rev comment) - "Register FILE into the git version-control system." - (vc-git--run-command file "update-index" "--add" "--")) - -(defun vc-git-print-log (file &optional buffer) - (let ((name (file-relative-name file)) - (coding-system-for-read git-commits-coding-system)) - (vc-do-command buffer 'async "git" name "rev-list" "--pretty" "HEAD" "--"))) - -(defun vc-git-diff (file &optional rev1 rev2 buffer) - (let ((name (file-relative-name file)) - (buf (or buffer "*vc-diff*"))) - (if (and rev1 rev2) - (vc-do-command buf 0 "git" name "diff-tree" "-p" rev1 rev2 "--") - (vc-do-command buf 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--")) - ; git-diff-index doesn't set exit status like diff does - (if (vc-git-workfile-unchanged-p file) 0 1))) - -(defun vc-git-checkin (file rev comment) - (let ((coding-system-for-write git-commits-coding-system)) - (vc-git--run-command file "commit" "-m" comment "--only" "--"))) - -(defun vc-git-checkout (file &optional editable rev destfile) - (if destfile - (let ((fullname (substring - (vc-git--run-command-string file "ls-files" "-z" "--full-name" "--") - 0 -1)) - (coding-system-for-read 'no-conversion) - (coding-system-for-write 'no-conversion)) - (with-temp-file destfile - (eq 0 (call-process "git" nil t nil "cat-file" "blob" - (concat (or rev "HEAD") ":" fullname))))) - (vc-git--run-command file "checkout" (or rev "HEAD")))) - -(defun vc-git-annotate-command (file buf &optional rev) - ; FIXME: rev is ignored - (let ((name (file-relative-name file))) - (call-process "git" nil buf nil "blame" name))) - -(defun vc-git-annotate-time () - (and (re-search-forward "[0-9a-f]+ (.* \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\) +[0-9]+)" nil t) - (vc-annotate-convert-time - (apply #'encode-time (mapcar (lambda (match) (string-to-number (match-string match))) '(6 5 4 3 2 1 7)))))) - -;; Not really useful since we can't do anything with the revision yet -;;(defun vc-annotate-extract-revision-at-line () -;; (save-excursion -;; (move-beginning-of-line 1) -;; (and (looking-at "[0-9a-f]+") -;; (buffer-substring (match-beginning 0) (match-end 0))))) - -(provide 'vc-git) -- cgit v1.2.3 From cf9957875c3a27b6ae4593e1fa9d4dabbde68433 Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Fri, 6 Feb 2009 11:05:37 -0500 Subject: completion: Fix GIT_PS1_SHOWDIRTYSTATE to prevent unbound variable errors. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 307bf5d4f9..6e04985079 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -125,7 +125,7 @@ __git_ps1 () local w local i - if test -n "$GIT_PS1_SHOWDIRTYSTATE"; then + if test -n "${GIT_PS1_SHOWDIRTYSTATE-}"; then if test "$(git config --bool bash.showDirtyState)" != "false"; then git diff --no-ext-diff --ignore-submodules \ --quiet --exit-code || w="*" -- cgit v1.2.3 From a9ee90d7ff9f3854b3096b4abbdc2013708704f5 Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Fri, 6 Feb 2009 11:05:38 -0500 Subject: completion: Get rid of tabbed indentation in comments. Replace with spaces. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6e04985079..f44f63cfeb 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -34,11 +34,11 @@ # are currently in a git repository. The %s token will be # the name of the current branch. # -# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty -# value, unstaged (*) and staged (+) changes will be shown next -# to the branch name. You can configure this per-repository -# with the bash.showDirtyState variable, which defaults to true -# once GIT_PS1_SHOWDIRTYSTATE is enabled. +# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty +# value, unstaged (*) and staged (+) changes will be shown next +# to the branch name. You can configure this per-repository +# with the bash.showDirtyState variable, which defaults to true +# once GIT_PS1_SHOWDIRTYSTATE is enabled. # # To submit patches: # -- cgit v1.2.3 From e5f5050ed1481c3bc27658f625a87155aed0984f Mon Sep 17 00:00:00 2001 From: Pat Notz Date: Tue, 10 Feb 2009 09:43:30 -0700 Subject: Fix contrib/hooks/post-receive-email for new duplicate branch In the show_new_revisions function, the original code: git rev-parse --not --branches | grep -v $(git rev-parse $refname) | isn't quite right since one can create a new branch and push it without any new commits. In that case, two refs will have the same sha1 but both would get filtered by the 'grep'. In the end, we'll show ALL the history which is not what we want. Instead, we should list the branches by name and remove the branch being updated and THEN pass that list through rev-parse. Revised as suggested by Jakub Narebski and Junio C Hamano to use git-for-each-ref instead of git-branch. (Thanks!) Signed-off-by: Pat Notz Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 28a3c0e46e..60cbab65d3 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -615,7 +615,9 @@ show_new_revisions() revspec=$oldrev..$newrev fi - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + other_branches=$(git for-each-ref --format='%(refname)' refs/heads/ | + grep -F -v $refname) + git rev-parse --not $other_branches | if [ -z "$custom_showrev" ] then git rev-list --pretty --stdin $revspec -- cgit v1.2.3 From fa26a401bed5967d6118ac430c5c5f4707c54386 Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Wed, 11 Feb 2009 13:03:23 -0500 Subject: completion: For consistency, change "git rev-parse" to __gitdir calls Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f44f63cfeb..6bbe09ab9a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -80,7 +80,7 @@ __gitdir () # returns text to add to bash PS1 prompt (includes branch name) __git_ps1 () { - local g="$(git rev-parse --git-dir 2>/dev/null)" + local g="$(__gitdir)" if [ -n "$g" ]; then local r local b @@ -1797,7 +1797,7 @@ _gitk () __git_has_doubledash && return local cur="${COMP_WORDS[COMP_CWORD]}" - local g="$(git rev-parse --git-dir 2>/dev/null)" + local g="$(__gitdir)" local merge="" if [ -f $g/MERGE_HEAD ]; then merge="--merge" -- cgit v1.2.3 From ad244d256865c06804afffef32b753239a06119e Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Wed, 11 Feb 2009 13:03:24 -0500 Subject: completion: Use consistent if [...] convention, not "test" The local coding convention in bash completion is to use [...] rather than test. Additionally, if [...]; then is preferred over if [...] then and so matching "if [...]\nthen" were changed accordingly. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6bbe09ab9a..c61576fcaf 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -84,39 +84,30 @@ __git_ps1 () if [ -n "$g" ]; then local r local b - if [ -d "$g/rebase-apply" ] - then - if test -f "$g/rebase-apply/rebasing" - then + if [ -d "$g/rebase-apply" ]; then + if [ -f "$g/rebase-apply/rebasing" ]; then r="|REBASE" - elif test -f "$g/rebase-apply/applying" - then + elif [ -f "$g/rebase-apply/applying" ]; then r="|AM" else r="|AM/REBASE" fi b="$(git symbolic-ref HEAD 2>/dev/null)" - elif [ -f "$g/rebase-merge/interactive" ] - then + elif [ -f "$g/rebase-merge/interactive" ]; then r="|REBASE-i" b="$(cat "$g/rebase-merge/head-name")" - elif [ -d "$g/rebase-merge" ] - then + elif [ -d "$g/rebase-merge" ]; then r="|REBASE-m" b="$(cat "$g/rebase-merge/head-name")" - elif [ -f "$g/MERGE_HEAD" ] - then + elif [ -f "$g/MERGE_HEAD" ]; then r="|MERGING" b="$(git symbolic-ref HEAD 2>/dev/null)" else - if [ -f "$g/BISECT_LOG" ] - then + if [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi - if ! b="$(git symbolic-ref HEAD 2>/dev/null)" - then - if ! b="$(git describe --exact-match HEAD 2>/dev/null)" - then + if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then + if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then b="$(cut -c1-7 "$g/HEAD")..." fi fi @@ -125,8 +116,8 @@ __git_ps1 () local w local i - if test -n "${GIT_PS1_SHOWDIRTYSTATE-}"; then - if test "$(git config --bool bash.showDirtyState)" != "false"; then + if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then + if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then git diff --no-ext-diff --ignore-submodules \ --quiet --exit-code || w="*" if git rev-parse --quiet --verify HEAD >/dev/null; then -- cgit v1.2.3 From e5dd864adfeb8b0176b31a132e972d7f7beff32a Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Wed, 11 Feb 2009 13:03:25 -0500 Subject: completion: Better __git_ps1 support when not in working directory If .git/HEAD is not readable, __git_ps1 does nothing. If --is-in-git-dir, __git_ps1 returns " (GIT_DIR!)" as a cautionary note. The previous behavior would show the branch name (and would optionally attempt to determine the dirtyState of the directory, which was impossible because a "git diff" was used). If --is-in-work-tree, __git_ps1 returns the branch name. Additionally, if showDirtyState is on, the dirty state is displayed. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 36 +++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c61576fcaf..aa8eec24d9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -108,7 +108,9 @@ __git_ps1 () fi if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then - b="$(cut -c1-7 "$g/HEAD")..." + if [ -r "$g/HEAD" ]; then + b="$(cut -c1-7 "$g/HEAD")..." + fi fi fi fi @@ -116,23 +118,29 @@ __git_ps1 () local w local i - if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then - if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then - git diff --no-ext-diff --ignore-submodules \ - --quiet --exit-code || w="*" - if git rev-parse --quiet --verify HEAD >/dev/null; then - git diff-index --cached --quiet \ - --ignore-submodules HEAD -- || i="+" - else - i="#" + if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then + b="GIT_DIR!" + elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then + if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then + if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then + git diff --no-ext-diff --ignore-submodules \ + --quiet --exit-code || w="*" + if git rev-parse --quiet --verify HEAD >/dev/null; then + git diff-index --cached --quiet \ + --ignore-submodules HEAD -- || i="+" + else + i="#" + fi fi fi fi - if [ -n "${1-}" ]; then - printf "$1" "${b##refs/heads/}$w$i$r" - else - printf " (%s)" "${b##refs/heads/}$w$i$r" + if [ -n "$b" ]; then + if [ -n "${1-}" ]; then + printf "$1" "${b##refs/heads/}$w$i$r" + else + printf " (%s)" "${b##refs/heads/}$w$i$r" + fi fi fi } -- cgit v1.2.3 From 5c9cc64a4a608ab0bbd5eb5c8e405bfe050be309 Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Wed, 11 Feb 2009 13:03:26 -0500 Subject: completion: More fixes to prevent unbound variable errors Several functions make use of "[-n ...]" and "[-z ...]". In many cases, the variables being tested were declared with "local." However, several __variables are not, and so they must be replaced with their ${__-} equivalents. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index aa8eec24d9..6e8c5b91ac 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -62,7 +62,7 @@ esac __gitdir () { if [ -z "${1-}" ]; then - if [ -n "$__git_dir" ]; then + if [ -n "${__git_dir-}" ]; then echo "$__git_dir" elif [ -d .git ]; then echo .git @@ -298,7 +298,7 @@ __git_remotes () __git_merge_strategies () { - if [ -n "$__git_merge_strategylist" ]; then + if [ -n "${__git_merge_strategylist-}" ]; then echo "$__git_merge_strategylist" return fi @@ -384,7 +384,7 @@ __git_complete_revlist () __git_all_commands () { - if [ -n "$__git_all_commandlist" ]; then + if [ -n "${__git_all_commandlist-}" ]; then echo "$__git_all_commandlist" return fi @@ -402,7 +402,7 @@ __git_all_commandlist="$(__git_all_commands 2>/dev/null)" __git_porcelain_commands () { - if [ -n "$__git_porcelain_commandlist" ]; then + if [ -n "${__git_porcelain_commandlist-}" ]; then echo "$__git_porcelain_commandlist" return fi -- cgit v1.2.3 From 901d615c5d74bea20e0c8d7fcdf7585616306b79 Mon Sep 17 00:00:00 2001 From: Matt Kraai Date: Thu, 12 Feb 2009 07:55:54 -0800 Subject: bash-completion: Complete the values of color.interactive, color.ui, color.pager Signed-off-by: Matt Kraai Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f44f63cfeb..a7a10c0d79 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1196,10 +1196,14 @@ _git_config () __gitcomp "$(__git_merge_strategies)" return ;; - color.branch|color.diff|color.status) + color.branch|color.diff|color.interactive|color.status|color.ui) __gitcomp "always never auto" return ;; + color.pager) + __gitcomp "false true" + return + ;; color.*.*) __gitcomp " normal black red green yellow blue magenta cyan white -- cgit v1.2.3 From 48c9ab78f3c7a0cc51e8d17bb7e37a075996c223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sat, 14 Feb 2009 17:21:52 +0100 Subject: bash: fix misspelled 'git svn' option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit '--user-log-author' -> '--use-log-author' Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a7a10c0d79..412d2c0dab 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1610,7 +1610,7 @@ _git_svn () --follow-parent --authors-file= --repack= --no-metadata --use-svm-props --use-svnsync-props --log-window-size= --no-checkout --quiet - --repack-flags --user-log-author --localtime $remote_opts + --repack-flags --use-log-author --localtime $remote_opts " local init_opts=" --template= --shared= --trunk= --tags= -- cgit v1.2.3 From d532ebd5a799fe2c991a96004bf739434e6ecaf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sun, 15 Feb 2009 14:25:11 +0100 Subject: bash: add missing 'git merge' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Namely: '--commit', '--stat', '--no-squash', '--ff', '--no-ff'. One might wonder why add options that specify the default behaviour anyway (e.g. '--commit', '--no-squash', etc.). Users can override the default with config options (e.g. 'branch..mergeoptions', 'merge.log'), but sometimes might still need the default behaviour. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 412d2c0dab..0bb768f1c1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1037,6 +1037,7 @@ _git_merge () --*) __gitcomp " --no-commit --no-stat --log --no-log --squash --strategy + --commit --stat --no-squash --ff --no-ff " return esac -- cgit v1.2.3 From 4a5856cb249579845e24713225bc1749a9b20482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sat, 14 Feb 2009 17:21:53 +0100 Subject: bash: update 'git svn' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'git svn' got some new subcommands and otions in the last couple of months. This patch adds completion support for them. In particular: * 'fetch', 'clone', etc.: '--ignore-paths=' * 'init' and 'clone': '--prefix=', '--use-log-author', '--add-author-from' * 'dcommit': '--commit-url', '--revision' * 'log': '--color' * 'rebase': '--dry-run' * 'branch', 'tag', 'blame', 'migrate' subcommands and their options Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0bb768f1c1..003017ac1b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1600,7 +1600,8 @@ _git_svn () local subcommands=" init fetch clone rebase dcommit log find-rev set-tree commit-diff info create-ignore propget - proplist show-ignore show-externals + proplist show-ignore show-externals branch tag blame + migrate " local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then @@ -1611,13 +1612,15 @@ _git_svn () --follow-parent --authors-file= --repack= --no-metadata --use-svm-props --use-svnsync-props --log-window-size= --no-checkout --quiet - --repack-flags --use-log-author --localtime $remote_opts + --repack-flags --use-log-author --localtime + --ignore-paths= $remote_opts " local init_opts=" --template= --shared= --trunk= --tags= --branches= --stdlayout --minimize-url --no-metadata --use-svm-props --use-svnsync-props - --rewrite-root= $remote_opts + --rewrite-root= --prefix= --use-log-author + --add-author-from $remote_opts " local cmt_opts=" --edit --rmdir --find-copies-harder --copy-similarity= @@ -1637,7 +1640,8 @@ _git_svn () dcommit,--*) __gitcomp " --merge --strategy= --verbose --dry-run - --fetch-all --no-rebase $cmt_opts $fc_opts + --fetch-all --no-rebase --commit-url + --revision $cmt_opts $fc_opts " ;; set-tree,--*) @@ -1651,13 +1655,13 @@ _git_svn () __gitcomp " --limit= --revision= --verbose --incremental --oneline --show-commit --non-recursive - --authors-file= + --authors-file= --color " ;; rebase,--*) __gitcomp " --merge --verbose --strategy= --local - --fetch-all $fc_opts + --fetch-all --dry-run $fc_opts " ;; commit-diff,--*) @@ -1666,6 +1670,21 @@ _git_svn () info,--*) __gitcomp "--url" ;; + branch,--*) + __gitcomp "--dry-run --message --tag" + ;; + tag,--*) + __gitcomp "--dry-run --message" + ;; + blame,--*) + __gitcomp "--git-format" + ;; + migrate,--*) + __gitcomp " + --config-dir= --ignore-paths= --minimize + --no-auth-cache --username= + " + ;; *) COMPREPLY=() ;; -- cgit v1.2.3 From a393777ec9ca7c33cb76efa225ddacc53784c0dd Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 16 Feb 2009 17:34:56 +0100 Subject: bash completion: refactor common log, shortlog and gitk options Refactor options that are useful for more than one of them into a variable used by the relevant completions. This has the effect of adding the following options to git-log: --branches --tags --remotes --first-parent --dense --sparse --simplify-merges --simplify-by-decoration --first-parent --no-merges The following to git-shortlog: --branches --tags --remotes --first-parent And the following to gitk: --branches --tags --remotes --first-parent --no-merges --max-count= --max-age= --since= --after= --min-age= --until= --before= --dense --sparse --full-history --simplify-merges --simplify-by-decoration --left-right Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 49 +++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 003017ac1b..6e5260ee75 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -975,6 +975,27 @@ _git_ls_tree () __git_complete_file } +# Options that go well for log, shortlog and gitk +__git_log_common_options=" + --not --all + --branches --tags --remotes + --first-parent --no-merges + --max-count= + --max-age= --since= --after= + --min-age= --until= --before= +" +# Options that go well for log and gitk (not shortlog) +__git_log_gitk_options=" + --dense --sparse --full-history + --simplify-merges --simplify-by-decoration + --left-right +" +# Options that go well for log and shortlog (not gitk) +__git_log_shortlog_options=" + --author= --committer= --grep= + --all-match +" + __git_log_pretty_formats="oneline short medium full fuller email raw format:" _git_log () @@ -996,21 +1017,19 @@ _git_log () ;; --*) __gitcomp " - --max-count= --max-age= --since= --after= - --min-age= --before= --until= + $__git_log_common_options + $__git_log_shortlog_options + $__git_log_gitk_options --root --topo-order --date-order --reverse - --no-merges --follow + --follow --abbrev-commit --abbrev= --relative-date --date= - --author= --committer= --grep= - --all-match --pretty= - --not --all - --left-right --cherry-pick + --cherry-pick --graph --decorate --walk-reflogs - --parents --children --full-history + --parents --children --merge $__git_diff_common_options --pickaxe-all --pickaxe-regex @@ -1496,12 +1515,8 @@ _git_shortlog () case "$cur" in --*) __gitcomp " - --max-count= --max-age= --since= --after= - --min-age= --before= --until= - --no-merges - --author= --committer= --grep= - --all-match - --not --all + $__git_log_common_options + $__git_log_shortlog_options --numbered --summary " return @@ -1828,7 +1843,11 @@ _gitk () fi case "$cur" in --*) - __gitcomp "--not --all $merge" + __gitcomp " + $__git_log_common_options + $__git_log_gitk_options + $merge + " return ;; esac -- cgit v1.2.3 From bf3c20f6e855521fb92f455a9e70fbe8f107c53d Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 16 Feb 2009 17:34:57 +0100 Subject: bash completion: only show 'log --merge' if merging The gitk completion only shows --merge if MERGE_HEAD is present. Do it the same way for git-log completion. Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6e5260ee75..0a3092f646 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1003,6 +1003,11 @@ _git_log () __git_has_doubledash && return local cur="${COMP_WORDS[COMP_CWORD]}" + local g="$(git rev-parse --git-dir 2>/dev/null)" + local merge="" + if [ -f $g/MERGE_HEAD ]; then + merge="--merge" + fi case "$cur" in --pretty=*) __gitcomp "$__git_log_pretty_formats @@ -1030,7 +1035,7 @@ _git_log () --decorate --walk-reflogs --parents --children - --merge + $merge $__git_diff_common_options --pickaxe-all --pickaxe-regex " -- cgit v1.2.3 From 21ba0e84356cb73faecc4c5bf30df7b2222961d2 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 16 Feb 2009 11:39:11 +0100 Subject: git.el: Make sure that file lists are sorted as they are created. This avoids a possibly redundant sort in git-update-status-files and git-status-filenames-map, and allows callers to continue using the list without having to copy it. It also fixes the confusing success messages reported by Brent Goodrick. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index fcbe2d9cf5..c7d15eb4dc 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -530,9 +530,9 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (git-fileinfo->needs-refresh info) t))) (defun git-status-filenames-map (status func files &rest args) - "Apply FUNC to the status files names in the FILES list." + "Apply FUNC to the status files names in the FILES list. +The list must be sorted." (when files - (setq files (sort files #'string-lessp)) (let ((file (pop files)) (node (ewoc-nth status 0))) (while (and file node) @@ -545,7 +545,7 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (setq file (pop files)))))))) (defun git-set-filenames-state (status files state) - "Set the state of a list of named files." + "Set the state of a list of named files. The list must be sorted" (when files (git-status-filenames-map status #'git-set-fileinfo-state files state) (unless state ;; delete files whose state has been set to nil @@ -750,6 +750,7 @@ Return the list of files that haven't been handled." (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) (push (match-string 1) unmerged-files)) + (setq unmerged-files (nreverse unmerged-files)) ;; assume it is sorted already (git-set-filenames-state status unmerged-files 'unmerged)))) (defun git-get-exclude-files () @@ -770,17 +771,18 @@ Return the list of files that haven't been handled." (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) (defun git-update-status-files (&optional files mark-files) - "Update the status of FILES from the index." + "Update the status of FILES from the index. +The FILES list must be sorted." (unless git-status (error "Not in git-status buffer.")) ;; set the needs-update flag on existing files - (if (setq files (sort files #'string-lessp)) + (if files (git-status-filenames-map git-status (lambda (info) (setf (git-fileinfo->needs-update info) t)) files) (ewoc-map (lambda (info) (setf (git-fileinfo->needs-update info) t) nil) git-status) (git-call-process nil "update-index" "--refresh") (when git-show-uptodate (git-run-ls-files-cached git-status nil 'uptodate))) - (let* ((remaining-files + (let ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db (git-run-ls-files-cached git-status files 'added) (git-run-diff-index git-status files)))) @@ -825,13 +827,13 @@ Return the list of files that haven't been handled." (list (ewoc-data (ewoc-locate git-status))))) (defun git-marked-files-state (&rest states) - "Return marked files that are in the specified states." + "Return a sorted list of marked files that are in the specified states." (let ((files (git-marked-files)) result) (dolist (info files) (when (memq (git-fileinfo->state info) states) (push info result))) - result)) + (nreverse result))) (defun git-refresh-files () "Refresh all files that need it and clear the needs-refresh flag." @@ -1101,13 +1103,14 @@ Return the list of files that haven't been handled." (or (not added) (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added)) (or (not modified) - (apply 'git-call-process-display-error "checkout" "HEAD" modified))))) - (git-update-status-files (append added modified)) + (apply 'git-call-process-display-error "checkout" "HEAD" modified)))) + (names (git-get-filenames files))) + (git-update-status-files names) (when ok (dolist (file modified) (let ((buffer (get-file-buffer file))) (when buffer (with-current-buffer buffer (revert-buffer t t t))))) - (git-success-message "Reverted" (git-get-filenames files))))))) + (git-success-message "Reverted" names)))))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." @@ -1365,14 +1368,14 @@ Return the list of files that haven't been handled." (mapconcat #'identity msg "\n")))) (defun git-get-commit-files (commit) - "Retrieve the list of files modified by COMMIT." + "Retrieve a sorted list of files modified by COMMIT." (let (files) (with-temp-buffer (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" "--root" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) - files)) + (sort files #'string-lessp))) (defun git-read-commit-name (prompt &optional default) "Ask for a commit name, with completion for local branch, remote branch and tag." -- cgit v1.2.3 From 5b4e44104ea9f933e461ed7d3ce4a05a00d95dbb Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 16 Feb 2009 11:40:08 +0100 Subject: git.el: Improve the confirmation message on remove and revert. If there's only one file, print its name instead of just "1 file". Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c7d15eb4dc..eace9c18eb 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1068,7 +1068,9 @@ The FILES list must be sorted." (unless files (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files)) (if (yes-or-no-p - (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" ""))) + (if (cdr files) + (format "Remove %d files? " (length files)) + (format "Remove %s? " (car files)))) (progn (dolist (name files) (ignore-errors @@ -1087,7 +1089,9 @@ The FILES list must be sorted." added modified) (when (and files (yes-or-no-p - (format "Revert %d file%s? " (length files) (if (> (length files) 1) "s" "")))) + (if (cdr files) + (format "Revert %d files? " (length files)) + (format "Revert %s? " (git-fileinfo->name (car files)))))) (dolist (info files) (case (git-fileinfo->state info) ('added (push (git-fileinfo->name info) added)) -- cgit v1.2.3 From 6f3c504b54e93772e875b6210fe69ecbad978e78 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 16 Feb 2009 11:40:29 +0100 Subject: Add a README in the contrib/emacs directory. Signed-off-by: Alexandre Julliard --- contrib/emacs/README | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 contrib/emacs/README (limited to 'contrib') diff --git a/contrib/emacs/README b/contrib/emacs/README new file mode 100644 index 0000000000..82368bdbff --- /dev/null +++ b/contrib/emacs/README @@ -0,0 +1,39 @@ +This directory contains various modules for Emacs support. + +To make the modules available to Emacs, you should add this directory +to your load-path, and then require the modules you want. This can be +done by adding to your .emacs something like this: + + (add-to-list 'load-path ".../git/contrib/emacs") + (require 'git) + (require 'git-blame) + + +The following modules are available: + +* git.el: + + Status manager that displays the state of all the files of the + project, and provides easy access to the most frequently used git + commands. The user interface is as far as possible compatible with + the pcl-cvs mode. It can be started with `M-x git-status'. + +* git-blame.el: + + Emacs implementation of incremental git-blame. When you turn it on + while viewing a file, the editor buffer will be updated by setting + the background of individual lines to a color that reflects which + commit it comes from. And when you move around the buffer, a + one-line summary will be shown in the echo area. + +* vc-git.el: + + This file used to contain the VC-mode backend for git, but it is no + longer distributed with git. It is now maintained as part of Emacs + and included in standard Emacs distributions starting from version + 22.2. + + If you have an earlier Emacs version, upgrading to Emacs 22 is + recommended, since the VC mode in older Emacs is not generic enough + to be able to support git in a reasonable manner, and no attempt has + been made to backport vc-git.el. -- cgit v1.2.3 From f50edca56c40cbfe48734eacd5d79416ba3649eb Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Sat, 21 Feb 2009 15:48:43 +0100 Subject: Add bare repository indicator for __git_ps1 Prefixes the branch name with "BARE:" if you're in a bare repository. Signed-off-by: Marius Storm-Olsen Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6e8c5b91ac..a61d852a14 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -135,11 +135,17 @@ __git_ps1 () fi fi + local c + + if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then + c="BARE:" + fi + if [ -n "$b" ]; then if [ -n "${1-}" ]; then - printf "$1" "${b##refs/heads/}$w$i$r" + printf "$1" "$c${b##refs/heads/}$w$i$r" else - printf " (%s)" "${b##refs/heads/}$w$i$r" + printf " (%s)" "$c${b##refs/heads/}$w$i$r" fi fi fi -- cgit v1.2.3 From b4b0ba06f8748348039d56ffa5890590dd9776ee Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Wed, 18 Feb 2009 13:12:14 -0500 Subject: git-p4: avoid syncing duplicate changes When a particular changeset affects multiple depot paths, it will appear multiple times in the output of "p4 changes". Filter out the duplicates to avoid the extra empty commits that this otherwise would create. Signed-off-by: Pete Wyckoff Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a85a7b2a58..3832f60225 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -442,13 +442,14 @@ def p4ChangesForPaths(depotPaths, changeRange): output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange) for p in depotPaths])) - changes = [] + changes = {} for line in output: - changeNum = line.split(" ")[1] - changes.append(int(changeNum)) + changeNum = int(line.split(" ")[1]) + changes[changeNum] = True - changes.sort() - return changes + changelist = changes.keys() + changelist.sort() + return changelist class Command: def __init__(self): -- cgit v1.2.3 From 3ca936422212370481850d67cab80b1e517b2d80 Mon Sep 17 00:00:00 2001 From: Abhijit Menon-Sen Date: Wed, 25 Feb 2009 08:33:14 +0530 Subject: Convert git-* invocations to "git *" in the svnimport example. After these changes, git-svnimport worked fine for me. Signed-off-by: Abhijit Menon-Sen Signed-off-by: Junio C Hamano --- contrib/examples/git-svnimport.perl | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl index a13bb6afec..4576c4a862 100755 --- a/contrib/examples/git-svnimport.perl +++ b/contrib/examples/git-svnimport.perl @@ -287,9 +287,9 @@ my $last_rev = ""; my $last_branch; my $current_rev = $opt_s || 1; unless(-d $git_dir) { - system("git-init"); + system("git init"); die "Cannot init the GIT db at $git_tree: $?\n" if $?; - system("git-read-tree"); + system("git read-tree"); die "Cannot init an empty tree: $?\n" if $?; $last_branch = $opt_o; @@ -303,7 +303,7 @@ unless(-d $git_dir) { -f "$git_dir/svn2git" or die "'$git_dir/svn2git' does not exist.\n". "You need that file for incremental imports.\n"; - open(F, "git-symbolic-ref HEAD |") or + open(F, "git symbolic-ref HEAD |") or die "Cannot run git-symbolic-ref: $!\n"; chomp ($last_branch = ); $last_branch = basename($last_branch); @@ -331,7 +331,7 @@ EOM "$git_dir/refs/heads/$opt_o") == 0; # populate index - system('git-read-tree', $last_rev); + system('git', 'read-tree', $last_rev); die "read-tree failed: $?\n" if $?; # Get the last import timestamps @@ -399,7 +399,7 @@ sub get_file($$$) { my $pid = open(my $F, '-|'); die $! unless defined $pid; if (!$pid) { - exec("git-hash-object", "-w", $name) + exec("git", "hash-object", "-w", $name) or die "Cannot create object: $!\n"; } my $sha = <$F>; @@ -423,7 +423,7 @@ sub get_ignore($$$$$) { my $pid = open(my $F, '-|'); die $! unless defined $pid; if (!$pid) { - exec("git-hash-object", "-w", $name) + exec("git", "hash-object", "-w", $name) or die "Cannot create object: $!\n"; } my $sha = <$F>; @@ -547,7 +547,7 @@ sub copy_path($$$$$$$$) { my $pid = open my $f,'-|'; die $! unless defined $pid; if (!$pid) { - exec("git-ls-tree","-r","-z",$gitrev,$srcpath) + exec("git","ls-tree","-r","-z",$gitrev,$srcpath) or die $!; } local $/ = "\0"; @@ -634,7 +634,7 @@ sub commit { my $rev; if($revision > $opt_s and defined $parent) { - open(H,'-|',"git-rev-parse","--verify",$parent); + open(H,'-|',"git","rev-parse","--verify",$parent); $rev = ; close(H) or do { print STDERR "$revision: cannot find commit '$parent'!\n"; @@ -671,7 +671,7 @@ sub commit { unlink($git_index); } elsif ($rev ne $last_rev) { print "Switching from $last_rev to $rev ($branch)\n" if $opt_v; - system("git-read-tree", $rev); + system("git", "read-tree", $rev); die "read-tree failed for $rev: $?\n" if $?; $last_rev = $rev; } @@ -740,7 +740,7 @@ sub commit { my $pid = open my $F, "-|"; die "$!" unless defined $pid; if (!$pid) { - exec("git-ls-files", "-z", @o1) or die $!; + exec("git", "ls-files", "-z", @o1) or die $!; } @o1 = (); local $/ = "\0"; @@ -758,7 +758,7 @@ sub commit { @o2 = @o1; @o1 = (); } - system("git-update-index","--force-remove","--",@o2); + system("git","update-index","--force-remove","--",@o2); die "Cannot remove files: $?\n" if $?; } } @@ -770,7 +770,7 @@ sub commit { @n2 = @new; @new = (); } - system("git-update-index","--add", + system("git","update-index","--add", (map { ('--cacheinfo', @$_) } @n2)); die "Cannot add files: $?\n" if $?; } @@ -778,7 +778,7 @@ sub commit { my $pid = open(C,"-|"); die "Cannot fork: $!" unless defined $pid; unless($pid) { - exec("git-write-tree"); + exec("git","write-tree"); die "Cannot exec git-write-tree: $!\n"; } chomp(my $tree = ); @@ -830,7 +830,7 @@ sub commit { "GIT_COMMITTER_NAME=$committer_name", "GIT_COMMITTER_EMAIL=$committer_email", "GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), - "git-commit-tree", $tree,@par); + "git", "commit-tree", $tree,@par); die "Cannot exec git-commit-tree: $!\n"; } $pw->writer(); @@ -874,7 +874,7 @@ sub commit { $dest =~ tr/_/\./ if $opt_u; - system('git-tag', '-f', $dest, $cid) == 0 + system('git', 'tag', '-f', $dest, $cid) == 0 or die "Cannot create tag $dest: $!\n"; print "Created tag '$dest' on '$branch'\n" if $opt_v; @@ -937,7 +937,7 @@ while ($to_rev < $opt_l) { my $pid = fork(); die "Fork: $!\n" unless defined $pid; unless($pid) { - exec("git-repack", "-d") + exec("git", "repack", "-d") or die "Cannot repack: $!\n"; } waitpid($pid, 0); @@ -958,7 +958,7 @@ if($orig_branch) { system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") if $forward_master; unless ($opt_i) { - system('git-read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD'); + system('git', 'read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD'); die "read-tree failed: $?\n" if $?; } } else { @@ -966,7 +966,7 @@ if($orig_branch) { print "DONE; creating $orig_branch branch\n" if $opt_v and (not defined $opt_l or $opt_l > 0); system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") unless -f "$git_dir/refs/heads/master"; - system('git-update-ref', 'HEAD', "$orig_branch"); + system('git', 'update-ref', 'HEAD', "$orig_branch"); unless ($opt_i) { system('git checkout'); die "checkout failed: $?\n" if $?; -- cgit v1.2.3 From ddb6d010231432ba75cf109aa7cd282912c88d2d Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Sat, 21 Feb 2009 15:48:43 +0100 Subject: Fixup: Add bare repository indicator for __git_ps1 Signed-off-by: Marius Storm-Olsen Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a61d852a14..dd393cd004 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -117,9 +117,14 @@ __git_ps1 () local w local i + local c if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then - b="GIT_DIR!" + if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then + c="BARE:" + else + b="GIT_DIR!" + fi elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then @@ -135,12 +140,6 @@ __git_ps1 () fi fi - local c - - if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then - c="BARE:" - fi - if [ -n "$b" ]; then if [ -n "${1-}" ]; then printf "$1" "$c${b##refs/heads/}$w$i$r" -- cgit v1.2.3 From 3dbe1165e9facec3497b3da744b832788a47957e Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Wed, 25 Feb 2009 15:05:17 +0100 Subject: Fix typo in contrib/examples/git-svnimport.txt Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- contrib/examples/git-svnimport.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-svnimport.txt b/contrib/examples/git-svnimport.txt index 71aad8b45b..3bb871e42f 100644 --- a/contrib/examples/git-svnimport.txt +++ b/contrib/examples/git-svnimport.txt @@ -114,9 +114,9 @@ due to SVN memory leaks. (These have been worked around.) -R :: Specify how often git repository should be repacked. + -The default value is 1000. git-svnimport will do import in chunks of 1000 -revisions, after each chunk git repository will be repacked. To disable -this behavior specify some big value here which is mote than number of +The default value is 1000. git-svnimport will do imports in chunks of 1000 +revisions, after each chunk the git repository will be repacked. To disable +this behavior specify some large value here which is greater than the number of revisions to import. -P :: -- cgit v1.2.3 From 72de29c24f50dccc5f045a7756bb0b47e34a7a8e Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Tue, 24 Feb 2009 15:33:29 +0200 Subject: bash completion: add --format= and --oneline options for "git log" We also add --format= completion for "git show". Signed-off-by: Teemu Likonen Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0a3092f646..31608cb79f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1014,6 +1014,11 @@ _git_log () " "" "${cur##--pretty=}" return ;; + --format=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--format=}" + return + ;; --date=*) __gitcomp " relative iso8601 rfc2822 short local default @@ -1029,7 +1034,7 @@ _git_log () --follow --abbrev-commit --abbrev= --relative-date --date= - --pretty= + --pretty= --format= --oneline --cherry-pick --graph --decorate @@ -1541,8 +1546,13 @@ _git_show () " "" "${cur##--pretty=}" return ;; + --format=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--format=}" + return + ;; --*) - __gitcomp "--pretty= + __gitcomp "--pretty= --format= $__git_diff_common_options " return -- cgit v1.2.3 From bc14fac825d9728c311aaa9d0aecf4960d4a3103 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:25 -0500 Subject: builtin-remote: add set-head subcommand Provide a porcelain command for setting and deleting $GIT_DIR/remotes//HEAD. While we're at it, document what $GIT_DIR/remotes//HEAD is all about. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0a3092f646..15b938b902 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1443,7 +1443,7 @@ _git_config () _git_remote () { - local subcommands="add rename rm show prune update" + local subcommands="add rename rm show prune update set-head" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" -- cgit v1.2.3 From 52d5c3b5b22b6a672ace19f631768a63bb6a2250 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Thu, 5 Mar 2009 23:39:31 -0500 Subject: bash completion: fix completion issues with fetch, pull, and push Sverre Rabbelier noticed a completion issue with push: $ git push ori git push origin $ git push -f ori git push -f origin/ Markus Heidelberg pointed out that the issue extends to fetch and pull. The reason is that the current code naively assumes that if COMP_CWORD=2, it should complete a remote name, otherwise it should complete a refspec. This assumption fails if there are any --options. This patch fixes that issue by instead scanning COMP_CWORDS to see if the remote has been completed yet (we now assume the first non-dashed argument is the remote). The new logic is factored into a function, shared by fetch, pull, and push. The new function also properly handles '.' as the remote. Signed-off-by: Jay Soffian Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 109 ++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 49 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f234c34304..e8c4be2e81 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -387,6 +387,63 @@ __git_complete_revlist () esac } +__git_complete_remote_or_refspec () +{ + local cmd="${COMP_WORDS[1]}" + local cur="${COMP_WORDS[COMP_CWORD]}" + local i c=2 remote="" pfx="" lhs=1 + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + -*) ;; + *) remote="$i"; break ;; + esac + c=$((++c)) + done + if [ -z "$remote" ]; then + __gitcomp "$(__git_remotes)" + return + fi + [ "$remote" = "." ] && remote= + case "$cur" in + *:*) + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="${cur%%:*}:" ;; + esac + cur="${cur#*:}" + lhs=0 + ;; + +*) + pfx="+" + cur="${cur#+}" + ;; + esac + case "$cmd" in + fetch) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur" + else + __gitcomp "$(__git_refs)" "$pfx" "$cur" + fi + ;; + pull) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur" + else + __gitcomp "$(__git_refs)" "$pfx" "$cur" + fi + ;; + push) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs)" "$pfx" "$cur" + else + __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur" + fi + ;; + esac +} + __git_all_commands () { if [ -n "${__git_all_commandlist-}" ]; then @@ -832,25 +889,7 @@ _git_diff () _git_fetch () { - local cur="${COMP_WORDS[COMP_CWORD]}" - - if [ "$COMP_CWORD" = 2 ]; then - __gitcomp "$(__git_remotes)" - else - case "$cur" in - *:*) - local pfx="" - case "$COMP_WORDBREAKS" in - *:*) : great ;; - *) pfx="${cur%%:*}:" ;; - esac - __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}" - ;; - *) - __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")" - ;; - esac - fi + __git_complete_remote_or_refspec } _git_format_patch () @@ -1120,40 +1159,12 @@ _git_name_rev () _git_pull () { - local cur="${COMP_WORDS[COMP_CWORD]}" - - if [ "$COMP_CWORD" = 2 ]; then - __gitcomp "$(__git_remotes)" - else - __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" - fi + __git_complete_remote_or_refspec } _git_push () { - local cur="${COMP_WORDS[COMP_CWORD]}" - - if [ "$COMP_CWORD" = 2 ]; then - __gitcomp "$(__git_remotes)" - else - case "$cur" in - *:*) - local pfx="" - case "$COMP_WORDBREAKS" in - *:*) : great ;; - *) pfx="${cur%%:*}:" ;; - esac - - __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}" - ;; - +*) - __gitcomp "$(__git_refs)" + "${cur#+}" - ;; - *) - __gitcomp "$(__git_refs)" - ;; - esac - fi + __git_complete_remote_or_refspec } _git_rebase () -- cgit v1.2.3 From 3c7b480a1cf6e1a1c73b4edde5d8cf0ac0c8111c Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Fri, 6 Mar 2009 11:30:44 -0500 Subject: bash completion: refactor --strategy completion The code to complete --strategy was duplicated between _git_rebase and _git_merge, and is about to gain a third caller (_git_pull). This patch factors it into its own function. Signed-off-by: Jay Soffian Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 38 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e8c4be2e81..056e43e4ad 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -444,6 +444,23 @@ __git_complete_remote_or_refspec () esac } +__git_complete_strategy () +{ + case "${COMP_WORDS[COMP_CWORD-1]}" in + -s|--strategy) + __gitcomp "$(__git_merge_strategies)" + return 0 + esac + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --strategy=*) + __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" + return 0 + ;; + esac + return 1 +} + __git_all_commands () { if [ -n "${__git_all_commandlist-}" ]; then @@ -1095,17 +1112,10 @@ _git_log () _git_merge () { + __git_complete_strategy && return + local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[COMP_CWORD-1]}" in - -s|--strategy) - __gitcomp "$(__git_merge_strategies)" - return - esac case "$cur" in - --strategy=*) - __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" - return - ;; --*) __gitcomp " --no-commit --no-stat --log --no-log --squash --strategy @@ -1174,16 +1184,8 @@ _git_rebase () __gitcomp "--continue --skip --abort" return fi - case "${COMP_WORDS[COMP_CWORD-1]}" in - -s|--strategy) - __gitcomp "$(__git_merge_strategies)" - return - esac + __git_complete_strategy && return case "$cur" in - --strategy=*) - __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" - return - ;; --*) __gitcomp "--onto --merge --strategy --interactive" return -- cgit v1.2.3 From 0a4e14727f53ba2e8263622ba5de917b2f9d1575 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Thu, 5 Mar 2009 23:39:33 -0500 Subject: bash completion: teach fetch, pull, and push to complete their options fetch, pull, and push didn't know their options. They do now. merge's options are factored into a variable so they can be shared between _git_merge and _git_pull Signed-off-by: Jay Soffian Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 61 +++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 056e43e4ad..271b911f7a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -391,10 +391,11 @@ __git_complete_remote_or_refspec () { local cmd="${COMP_WORDS[1]}" local cur="${COMP_WORDS[COMP_CWORD]}" - local i c=2 remote="" pfx="" lhs=1 + local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0 while [ $c -lt $COMP_CWORD ]; do i="${COMP_WORDS[c]}" case "$i" in + --all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;; -*) ;; *) remote="$i"; break ;; esac @@ -404,6 +405,10 @@ __git_complete_remote_or_refspec () __gitcomp "$(__git_remotes)" return fi + if [ $no_complete_refspec = 1 ]; then + COMPREPLY=() + return + fi [ "$remote" = "." ] && remote= case "$cur" in *:*) @@ -904,8 +909,20 @@ _git_diff () __git_complete_file } +__git_fetch_options=" + --quiet --verbose --append --upload-pack --force --keep --depth= + --tags --no-tags +" + _git_fetch () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "$__git_fetch_options" + return + ;; + esac __git_complete_remote_or_refspec } @@ -1110,6 +1127,11 @@ _git_log () __git_complete_revlist } +__git_merge_options=" + --no-commit --no-stat --log --no-log --squash --strategy + --commit --stat --no-squash --ff --no-ff +" + _git_merge () { __git_complete_strategy && return @@ -1117,10 +1139,7 @@ _git_merge () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp " - --no-commit --no-stat --log --no-log --squash --strategy - --commit --stat --no-squash --ff --no-ff - " + __gitcomp "$__git_merge_options" return esac __gitcomp "$(__git_refs)" @@ -1169,11 +1188,43 @@ _git_name_rev () _git_pull () { + __git_complete_strategy && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --rebase --no-rebase + $__git_merge_options + $__git_fetch_options + " + return + ;; + esac __git_complete_remote_or_refspec } _git_push () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "${COMP_WORDS[COMP_CWORD-1]}" in + --repo) + __gitcomp "$(__git_remotes)" + return + esac + case "$cur" in + --repo=*) + __gitcomp "$(__git_remotes)" "" "${cur##--repo=}" + return + ;; + --*) + __gitcomp " + --all --mirror --tags --dry-run --force --verbose + --receive-pack= --repo= + " + return + ;; + esac __git_complete_remote_or_refspec } -- cgit v1.2.3 From 3b167396b416541f7559f3141392d56b93ea049c Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Fri, 27 Feb 2009 10:53:59 -0800 Subject: git-p4: remove tabs from usermap file Some users have tabs in their names, oddly enough. This causes problems when loading the usercache from disk, as split separates the fields on the wrong tabs. When fast-import's parse_ident() tries to parse the committer field, it is unhappy about the unbalanced <..> angle brackets. It is easy enough to convert the tabs to single spaces. Signed-off-by: Pete Wyckoff Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3832f60225..342529db30 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1142,7 +1142,7 @@ class P4Sync(Command): s = '' for (key, val) in self.users.items(): - s += "%s\t%s\n" % (key, val) + s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1)) open(self.getUserCacheFilename(), "wb").write(s) self.userMapFromPerforceServer = True -- cgit v1.2.3 From 2464456a6ac9216d59d9e2cf0d86fee072f63cf8 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Mon, 9 Mar 2009 02:12:36 -0700 Subject: contrib/difftool: use a separate config namespace for difftool commands Some users have different mergetool and difftool settings, so teach difftool to read config vars from the difftool.* namespace. This allows having distinct configurations for the diff and merge scenarios. We don't want to force existing users to set new values for no reason so difftool falls back to existing mergetool config variables when the difftool equivalents are not defined. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 6 +++--- contrib/difftool/git-difftool-helper | 19 ++++++++++++++----- contrib/difftool/git-difftool.txt | 30 +++++++++++++++--------------- 3 files changed, 32 insertions(+), 23 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool index 0cda3d2eea..0deda3a0e4 100755 --- a/contrib/difftool/git-difftool +++ b/contrib/difftool/git-difftool @@ -4,7 +4,7 @@ # This is a wrapper around the GIT_EXTERNAL_DIFF-compatible # git-difftool-helper script. This script exports # GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and -# GIT_DIFFTOOL_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper. +# GIT_DIFFTOOL_NO_PROMPT and GIT_DIFF_TOOL for use by git-difftool-helper. # Any arguments that are unknown to this script are forwarded to 'git diff'. use strict; @@ -49,12 +49,12 @@ sub generate_command } if ($arg eq '-t' or $arg eq '--tool') { usage() if $#ARGV <= $idx; - $ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1]; + $ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1]; $skip_next = 1; next; } if ($arg =~ /^--tool=/) { - $ENV{GIT_MERGE_TOOL} = substr($arg, 7); + $ENV{GIT_DIFF_TOOL} = substr($arg, 7); next; } if ($arg eq '--no-prompt') { diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index db3af6a833..9c0a13452a 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -128,8 +128,10 @@ launch_merge_tool () { cleanup_temp_files } -# Verifies that mergetool..cmd exists +# Verifies that (difftool|mergetool)..cmd exists valid_custom_tool() { + merge_tool_cmd="$(git config difftool.$1.cmd)" + test -z "$merge_tool_cmd" && merge_tool_cmd="$(git config mergetool.$1.cmd)" test -n "$merge_tool_cmd" } @@ -150,8 +152,11 @@ valid_tool() { } # Sets up the merge_tool_path variable. -# This handles the mergetool..path configuration. +# This handles the difftool..path configuration. +# This also falls back to mergetool defaults. init_merge_tool_path() { + merge_tool_path=$(git config difftool."$1".path) + test -z "$merge_tool_path" && merge_tool_path=$(git config mergetool."$1".path) if test -z "$merge_tool_path"; then case "$1" in @@ -165,15 +170,19 @@ init_merge_tool_path() { fi } -# Allow the GIT_MERGE_TOOL variable to provide a default value +# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL" +test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL" -# If not merge tool was specified then use the merge.tool +# If merge tool was not specified then use the diff.tool # configuration variable. If that's invalid then reset merge_tool. +# Fallback to merge.tool. if test -z "$merge_tool"; then + merge_tool=$(git config diff.tool) + test -z "$merge_tool" && merge_tool=$(git config merge.tool) if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then - echo >&2 "git config option merge.tool set to unknown tool: $merge_tool" + echo >&2 "git config option diff.tool set to unknown tool: $merge_tool" echo >&2 "Resetting to default..." unset merge_tool fi diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index 6e2610cda6..2b7bc03ec3 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -32,23 +32,23 @@ OPTIONS vimdiff, gvimdiff, ecmerge, and opendiff + If a merge resolution program is not specified, 'git-difftool' -will use the configuration variable `merge.tool`. If the -configuration variable `merge.tool` is not set, 'git difftool' +will use the configuration variable `diff.tool`. If the +configuration variable `diff.tool` is not set, 'git-difftool' will pick a suitable default. + You can explicitly provide a full path to the tool by setting the -configuration variable `mergetool..path`. For example, you +configuration variable `difftool..path`. For example, you can configure the absolute path to kdiff3 by setting -`mergetool.kdiff3.path`. Otherwise, 'git-difftool' assumes the +`difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the tool is available in PATH. + Instead of running one of the known merge tool programs, 'git-difftool' can be customized to run an alternative program by specifying the command line to invoke in a configuration -variable `mergetool..cmd`. +variable `difftool..cmd`. + When 'git-difftool' is invoked with this tool (either through the -`-t` or `--tool` option or the `merge.tool` configuration variable) +`-t` or `--tool` option or the `diff.tool` configuration variable) the configured command line will be invoked with the following variables available: `$LOCAL` is set to the name of the temporary file containing the contents of the diff pre-image and `$REMOTE` @@ -61,24 +61,24 @@ with custom merge tool commands and has the same value as `$LOCAL`. CONFIG VARIABLES ---------------- -merge.tool:: - The default merge tool to use. -+ -See the `--tool=` option above for more details. +'git-difftool' falls back to 'git-mergetool' config variables when the +difftool equivalents have not been defined. -merge.keepBackup:: - The original, unedited file content can be saved to a file with - a `.orig` extension. Defaults to `true` (i.e. keep the backup files). +diff.tool:: + The default merge tool to use. -mergetool..path:: +difftool..path:: Override the path for the given tool. This is useful in case your tool is not in the PATH. -mergetool..cmd:: +difftool..cmd:: Specify the command to invoke the specified merge tool. + See the `--tool=` option above for more details. +merge.keepBackup:: + The original, unedited file content can be saved to a file with + a `.orig` extension. Defaults to `true` (i.e. keep the backup files). SEE ALSO -------- -- cgit v1.2.3 From 6872f606d9bc9a0ab0b3252bd4175af7732b6135 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Fri, 20 Mar 2009 10:57:50 +0100 Subject: import-tars: separate author from committer The import-tars script is typically employed to (re)create the past history of a project from stored tars. Although assigning authorship in these cases can be a somewhat arbitrary process, it makes sense to set the author to whoever created the tars in the first place (if it's known), and (s)he can in general be different from the committer (whoever is running the script). Implement this by having separate author and committer data, making them settable from the usual GIT_* environment variables. Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/fast-import/import-tars.perl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 23aeb257b9..6309d146e7 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -14,13 +14,18 @@ die "usage: import-tars *.tar.{gz,bz2,Z}\n" unless @ARGV; my $branch_name = 'import-tars'; my $branch_ref = "refs/heads/$branch_name"; -my $committer_name = 'T Ar Creator'; -my $committer_email = 'tar@example.com'; +my $author_name = $ENV{'GIT_AUTHOR_NAME'} || 'T Ar Creator'; +my $author_email = $ENV{'GIT_AUTHOR_EMAIL'} || 'tar@example.com'; +my $committer_name = $ENV{'GIT_COMMITTER_NAME'} || `git config --get user.name`; +my $committer_email = $ENV{'GIT_COMMITTER_EMAIL'} || `git config --get user.email`; + +chomp($committer_name, $committer_email); open(FI, '|-', 'git', 'fast-import', '--quiet') or die "Unable to start git fast-import: $!\n"; foreach my $tar_file (@ARGV) { + my $commit_time = time; $tar_file =~ m,([^/]+)$,; my $tar_name = $1; @@ -39,7 +44,7 @@ foreach my $tar_file (@ARGV) die "Unrecognized compression format: $tar_file\n"; } - my $commit_time = 0; + my $author_time = 0; my $next_mark = 1; my $have_top_dir = 1; my ($top_dir, %files); @@ -92,7 +97,7 @@ foreach my $tar_file (@ARGV) } $files{$path} = [$next_mark++, $mode]; - $commit_time = $mtime if $mtime > $commit_time; + $author_time = $mtime if $mtime > $author_time; $path =~ m,^([^/]+)/,; $top_dir = $1 unless $top_dir; $have_top_dir = 0 if $top_dir ne $1; @@ -100,6 +105,7 @@ foreach my $tar_file (@ARGV) print FI < $author_time +0000 committer $committer_name <$committer_email> $commit_time +0000 data < $commit_time +0000 +tagger $author_name <$author_email> $author_time +0000 data < Date: Sat, 21 Mar 2009 16:29:27 -0700 Subject: Add --staged to bash completion for git diff The --staged option (synonym for --cached) isn't listed in the completion choices for git diff. This tiny patch adds it. Trivially-Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ed235f7596..6bc32df178 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -899,7 +899,7 @@ _git_diff () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--cached --pickaxe-all --pickaxe-regex + __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex --base --ours --theirs $__git_diff_common_options " -- cgit v1.2.3 From 4bca86367bff80ad3c04e282a1aa9ed66db26aa1 Mon Sep 17 00:00:00 2001 From: Arto Jonsson Date: Sun, 22 Mar 2009 20:49:07 +0200 Subject: bash completion: add options for 'git fsck' Signed-off-by: Arto Jonsson Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6bc32df178..10e36a7b0d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -952,6 +952,21 @@ _git_format_patch () __git_complete_revlist } +_git_fsck () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --tags --root --unreachable --cache --no-reflogs --full + --strict --verbose --lost-found + " + return + ;; + esac + COMPREPLY=() +} + _git_gc () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1880,6 +1895,7 @@ _git () diff) _git_diff ;; fetch) _git_fetch ;; format-patch) _git_format_patch ;; + fsck) _git_fsck ;; gc) _git_gc ;; grep) _git_grep ;; help) _git_help ;; -- cgit v1.2.3 From 77813151f983a77f5b5954fb7cb8094198db0567 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 23 Mar 2009 03:26:49 -0700 Subject: completion: add --annotate option to send-email Signed-off-by: Stephen Boyd Trivially-Acked-By: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 10e36a7b0d..8719242498 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1264,8 +1264,8 @@ _git_send_email () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--bcc --cc --cc-cmd --chain-reply-to --compose - --dry-run --envelope-sender --from --identity + __gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to + --compose --dry-run --envelope-sender --from --identity --in-reply-to --no-chain-reply-to --no-signed-off-by-cc --no-suppress-from --no-thread --quiet --signed-off-by-cc --smtp-pass --smtp-server -- cgit v1.2.3 From 3f7df3a71a9cbab3c27d84f2410e7d39407f9013 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 23 Mar 2009 03:26:50 -0700 Subject: completion: add --cc and --no-attachment option to format-patch Signed-off-by: Stephen Boyd Trivially-Acked-By: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8719242498..b96458f296 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -932,13 +932,13 @@ _git_format_patch () case "$cur" in --*) __gitcomp " - --stdout --attach --thread + --stdout --attach --no-attach --thread --output-directory --numbered --start-number --numbered-files --keep-subject --signoff - --in-reply-to= + --in-reply-to= --cc= --full-index --binary --not --all --cover-letter -- cgit v1.2.3 From e1d37937ac3c15898afdb6117349a30d0eae5e64 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 23 Mar 2009 03:26:51 -0700 Subject: completion: add --thread=deep/shallow to format-patch Signed-off-by: Stephen Boyd Trivially-Acked-By: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b96458f296..1c6b0e28ef 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -930,9 +930,15 @@ _git_format_patch () { local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in + --thread=*) + __gitcomp " + deep shallow + " "" "${cur##--thread=}" + return + ;; --*) __gitcomp " - --stdout --attach --no-attach --thread + --stdout --attach --no-attach --thread --thread= --output-directory --numbered --start-number --numbered-files -- cgit v1.2.3 From 63801da88d8638eb6cf26d6305a721ad3731e216 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 29 Mar 2009 22:42:27 +0200 Subject: import-zips: fix thinko Embarrassingly, the common prefix calculation did not work properly, due to a mistake in the assignment: instead of assigning the dirname of the current file name, the dirname of the current common prefix needs to be assigned to common prefix, when the current prefix does not match the current file name. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/fast-import/import-zips.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-zips.py b/contrib/fast-import/import-zips.py index c674fa2d1b..7051a83a59 100755 --- a/contrib/fast-import/import-zips.py +++ b/contrib/fast-import/import-zips.py @@ -44,7 +44,8 @@ for zipfile in argv[1:]: common_prefix = name[:name.rfind('/') + 1] else: while not name.startswith(common_prefix): - common_prefix = name[:name.rfind('/') + 1] + last_slash = common_prefix[:-1].rfind('/') + 1 + common_prefix = common_prefix[:last_slash] mark[name] = ':' + str(next_mark) next_mark += 1 -- cgit v1.2.3 From 67f1fe5f08d3f6146cf13f8a65ceeab1509581a8 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 16 Feb 2009 17:34:57 +0100 Subject: bash completion: only show 'log --merge' if merging The gitk completion only shows --merge if MERGE_HEAD is present. Do it the same way for git-log completion. Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 554a03ff4f..0bb74c05e5 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -937,6 +937,11 @@ _git_log () __git_has_doubledash && return local cur="${COMP_WORDS[COMP_CWORD]}" + local g="$(git rev-parse --git-dir 2>/dev/null)" + local merge="" + if [ -f $g/MERGE_HEAD ]; then + merge="--merge" + fi case "$cur" in --pretty=*) __gitcomp " @@ -968,7 +973,7 @@ _git_log () --decorate --diff-filter= --color-words --walk-reflogs --parents --children --full-history - --merge + $merge " return ;; -- cgit v1.2.3 From ba7906f2f4c332f814d270d2e16b0010516fc53e Mon Sep 17 00:00:00 2001 From: "Daniel Cheng (aka SDiZ)" Date: Mon, 30 Mar 2009 19:27:37 +0800 Subject: Fix bash completion in path with spaces Signed-off-by: Daniel Cheng (aka SDiZ) Trivially-acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0bb74c05e5..8fc01fb497 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -939,7 +939,7 @@ _git_log () local cur="${COMP_WORDS[COMP_CWORD]}" local g="$(git rev-parse --git-dir 2>/dev/null)" local merge="" - if [ -f $g/MERGE_HEAD ]; then + if [ -f "$g/MERGE_HEAD" ]; then merge="--merge" fi case "$cur" in @@ -1681,7 +1681,7 @@ _gitk () local cur="${COMP_WORDS[COMP_CWORD]}" local g="$(git rev-parse --git-dir 2>/dev/null)" local merge="" - if [ -f $g/MERGE_HEAD ]; then + if [ -f "$g/MERGE_HEAD" ]; then merge="--merge" fi case "$cur" in -- cgit v1.2.3 From 89a56bfbd3fd855cb0c2a381520e6255de41a55e Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Sun, 5 Apr 2009 04:15:16 +0200 Subject: add --html-path to get the location of installed HTML docs This can be used in GUIs to open installed HTML documentation in the browser. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e72ce2428d..fdc5a24b27 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1870,6 +1870,7 @@ _git () --bare --version --exec-path + --html-path --work-tree= --help " -- cgit v1.2.3 From 43acdf243ee8a8fa876bdd6659026fe5ed2d4c24 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Sun, 5 Apr 2009 12:33:38 -0400 Subject: bash completion: Update 'git am' options This adds --committer-date-is-author-date, --ignore-date, and --no-utf8 options. The --binary option is removed, as it was made a no-op by cb3a160. The option list is also sorted alphabetically. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e72ce2428d..d3d8203171 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -646,7 +646,8 @@ _git_am () ;; --*) __gitcomp " - --signoff --utf8 --binary --3way --interactive + --3way --committer-date-is-author-date --ignore-date + --interactive --keep --no-utf8 --signoff --utf8 --whitespace= " return -- cgit v1.2.3 From bad42732008cb0c1e77046d716e4446b1545d4d0 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Mon, 6 Apr 2009 01:31:17 -0700 Subject: git-mergetool/difftool: make (g)vimdiff workable under Windows Under Windows vimdiff and gvimdiff are not available as symbolic links, but as batch files vimdiff.bat and gvimdiff.bat. These files weren't found by 'type vimdiff' which led to the following error: The merge tool vimdiff is not available as 'vimdiff' Even if they were found, it wouldn't work to invoke these batch files from git-mergetool. To solve this, use vim and gvim (vim.exe and gvim.exe) and pass the -d command line switch over to them. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index 9c0a13452a..e481913c91 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -86,11 +86,11 @@ launch_merge_tool () { ;; vimdiff) - "$merge_tool_path" -c "wincmd l" "$LOCAL" "$REMOTE" + "$merge_tool_path" -d -c "wincmd l" "$LOCAL" "$REMOTE" ;; gvimdiff) - "$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$REMOTE" + "$merge_tool_path" -d -c "wincmd l" -f "$LOCAL" "$REMOTE" ;; xxdiff) @@ -160,6 +160,12 @@ init_merge_tool_path() { merge_tool_path=$(git config mergetool."$1".path) if test -z "$merge_tool_path"; then case "$1" in + vimdiff) + merge_tool_path=vim + ;; + gvimdiff) + merge_tool_path=gvim + ;; emerge) merge_tool_path=emacs ;; -- cgit v1.2.3 From 76ca653842057766773776bffc6a415b39d5a147 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Mon, 6 Apr 2009 01:31:19 -0700 Subject: difftool: remove merge options for opendiff, tkdiff, kdiff3 and xxdiff We shouldn't try to merge files when using difftool, so remove any merge-specific options. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index e481913c91..ef684b6f68 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -69,7 +69,7 @@ launch_merge_tool () { "$merge_tool_path" --auto \ --L1 "$basename (A)" \ --L2 "$basename (B)" \ - -o "$MERGED" "$LOCAL" "$REMOTE" \ + "$LOCAL" "$REMOTE" \ > /dev/null 2>&1 ;; @@ -78,7 +78,7 @@ launch_merge_tool () { ;; tkdiff) - "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE" + "$merge_tool_path" "$LOCAL" "$REMOTE" ;; meld) @@ -95,17 +95,13 @@ launch_merge_tool () { xxdiff) "$merge_tool_path" \ - -X \ - -R 'Accel.SaveAsMerged: "Ctrl-S"' \ -R 'Accel.Search: "Ctrl+F"' \ -R 'Accel.SearchForward: "Ctrl-G"' \ - --merged-file "$MERGED" \ "$LOCAL" "$REMOTE" ;; opendiff) - "$merge_tool_path" "$LOCAL" "$REMOTE" \ - -merge "$MERGED" | cat + "$merge_tool_path" "$LOCAL" "$REMOTE" | cat ;; ecmerge) -- cgit v1.2.3 From 2e8af7e42b15d4f2d573329ea2593a19f45f18d3 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Mon, 6 Apr 2009 01:31:20 -0700 Subject: difftool: remove the backup file feature Most users find the backup file feature annoying and there's no need for it since diff is supposed to be a read-only operation. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index ef684b6f68..e74a2747b6 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -9,31 +9,7 @@ # Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt. should_prompt () { - ! test -n "$GIT_DIFFTOOL_NO_PROMPT" -} - -# Should we keep the backup .orig file? -keep_backup_mode="$(git config --bool merge.keepBackup || echo true)" -keep_backup () { - test "$keep_backup_mode" = "true" -} - -# This function manages the backup .orig file. -# A backup $MERGED.orig file is created if changes are detected. -cleanup_temp_files () { - if test -n "$MERGED"; then - if keep_backup && test "$MERGED" -nt "$BACKUP"; then - test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig" - else - rm -f -- "$BACKUP" - fi - fi -} - -# This is called when users Ctrl-C out of git-difftool-helper -sigint_handler () { - cleanup_temp_files - exit 1 + test -z "$GIT_DIFFTOOL_NO_PROMPT" } # This function prepares temporary files and launches the appropriate @@ -47,12 +23,6 @@ launch_merge_tool () { LOCAL="$2" REMOTE="$3" BASE="$1" - ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" - BACKUP="$MERGED.BACKUP.$ext" - - # Create and ensure that we clean up $BACKUP - test -f "$MERGED" && cp -- "$MERGED" "$BACKUP" - trap sigint_handler INT # $LOCAL and $REMOTE are temporary files so prompt # the user with the real $MERGED name before launching $merge_tool. @@ -120,8 +90,6 @@ launch_merge_tool () { fi ;; esac - - cleanup_temp_files } # Verifies that (difftool|mergetool)..cmd exists -- cgit v1.2.3 From 46ae156d6c8c48d521e4d858ed84d93259cfc61f Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Mon, 6 Apr 2009 01:31:21 -0700 Subject: difftool: use perl built-ins when testing for msys I don't even know what $COMSPEC means so let's be safe and use the same perly $^O test add--interactive uses. While we're at it, make git-difftool match the prevalent git-perl style. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool index 0deda3a0e4..207dd50f2c 100755 --- a/contrib/difftool/git-difftool +++ b/contrib/difftool/git-difftool @@ -33,7 +33,10 @@ sub setup_environment sub exe { my $exe = shift; - return defined $ENV{COMSPEC} ? "$exe.exe" : $exe; + if ($^O eq 'MSWin32' || $^O eq 'msys') { + return "$exe.exe"; + } + return $exe; } sub generate_command @@ -47,7 +50,7 @@ sub generate_command $skip_next = 0; next; } - if ($arg eq '-t' or $arg eq '--tool') { + if ($arg eq '-t' || $arg eq '--tool') { usage() if $#ARGV <= $idx; $ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1]; $skip_next = 1; -- cgit v1.2.3 From 8b7332221db8522fe23bf8cf25d058acea6b9142 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Tue, 7 Apr 2009 01:21:19 -0700 Subject: difftool: add a -y shortcut for --no-prompt This is another consistency cleanup to make git-difftool's options match git-mergetool. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 6 +++--- contrib/difftool/git-difftool.txt | 36 ++++++++++++++---------------------- 2 files changed, 17 insertions(+), 25 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool index 207dd50f2c..8c160e5bb4 100755 --- a/contrib/difftool/git-difftool +++ b/contrib/difftool/git-difftool @@ -18,7 +18,7 @@ my $DIR = abs_path(dirname($0)); sub usage { print << 'USAGE'; -usage: git difftool [--tool=] [--no-prompt] ["git diff" options] +usage: git difftool [--tool=] [-y|--no-prompt] ["git diff" options] USAGE exit 1; } @@ -60,11 +60,11 @@ sub generate_command $ENV{GIT_DIFF_TOOL} = substr($arg, 7); next; } - if ($arg eq '--no-prompt') { + if ($arg eq '-y' || $arg eq '--no-prompt') { $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true'; next; } - if ($arg eq '-h' or $arg eq '--help') { + if ($arg eq '-h' || $arg eq '--help') { usage(); } push @command, $arg; diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index 2b7bc03ec3..a00e9431c5 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -3,35 +3,32 @@ git-difftool(1) NAME ---- -git-difftool - compare changes using common merge tools +git-difftool - Show changes using common diff tools SYNOPSIS -------- -'git difftool' [--tool=] [--no-prompt] ['git diff' options] +'git difftool' [--tool=] [-y|--no-prompt] [<'git diff' options>] DESCRIPTION ----------- 'git-difftool' is a git command that allows you to compare and edit files -between revisions using common merge tools. At its most basic level, -'git-difftool' does what 'git-mergetool' does but its use is for non-merge -situations such as when preparing commits or comparing changes against -the index. - -'git difftool' is a frontend to 'git diff' and accepts the same -arguments and options. - -See linkgit:git-diff[1] for the full list of supported options. +between revisions using common diff tools. 'git difftool' is a frontend +to 'git-diff' and accepts the same options and arguments. OPTIONS ------- +-y:: +--no-prompt:: + Do not prompt before launching a diff tool. + -t :: --tool=:: - Use the merge resolution program specified by . + Use the diff tool specified by . Valid merge tools are: kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff + -If a merge resolution program is not specified, 'git-difftool' +If a diff tool is not specified, 'git-difftool' will use the configuration variable `diff.tool`. If the configuration variable `diff.tool` is not set, 'git-difftool' will pick a suitable default. @@ -42,7 +39,7 @@ can configure the absolute path to kdiff3 by setting `difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the tool is available in PATH. + -Instead of running one of the known merge tool programs, +Instead of running one of the known diff tools, 'git-difftool' can be customized to run an alternative program by specifying the command line to invoke in a configuration variable `difftool..cmd`. @@ -56,8 +53,7 @@ is set to the name of the temporary file containing the contents of the diff post-image. `$BASE` is provided for compatibility with custom merge tool commands and has the same value as `$LOCAL`. ---no-prompt:: - Do not prompt before launching a diff tool. +See linkgit:git-diff[1] for the full list of supported options. CONFIG VARIABLES ---------------- @@ -65,21 +61,17 @@ CONFIG VARIABLES difftool equivalents have not been defined. diff.tool:: - The default merge tool to use. + The default diff tool to use. difftool..path:: Override the path for the given tool. This is useful in case your tool is not in the PATH. difftool..cmd:: - Specify the command to invoke the specified merge tool. + Specify the command to invoke the specified diff tool. + See the `--tool=` option above for more details. -merge.keepBackup:: - The original, unedited file content can be saved to a file with - a `.orig` extension. Defaults to `true` (i.e. keep the backup files). - SEE ALSO -------- linkgit:git-diff[1]:: -- cgit v1.2.3 From 1c0f3d224eff2ff8ca8442811edb5a6830adba1a Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Mon, 6 Apr 2009 01:31:23 -0700 Subject: difftool/mergetool: add diffuse as merge and diff tool This adds diffuse as a built-in merge tool. Signed-off-by: Sebastian Pipping Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 ++- contrib/difftool/git-difftool-helper | 10 ++++++---- contrib/difftool/git-difftool.txt | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d3d8203171..e099ed48ff 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1174,7 +1174,8 @@ _git_mergetool () --tool=*) __gitcomp " kdiff3 tkdiff meld xxdiff emerge - vimdiff gvimdiff ecmerge opendiff + vimdiff gvimdiff ecmerge diffuse + opendiff " "" "${cur##--tool=}" return ;; diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index e74a2747b6..4b0daec5a7 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -1,7 +1,5 @@ #!/bin/sh # git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher. -# It supports kdiff3, kompare, tkdiff, xxdiff, meld, opendiff, -# emerge, ecmerge, vimdiff, gvimdiff, and custom user-configurable tools. # This script is typically launched by using the 'git difftool' # convenience command. # @@ -55,6 +53,10 @@ launch_merge_tool () { "$merge_tool_path" "$LOCAL" "$REMOTE" ;; + diffuse) + "$merge_tool_path" "$LOCAL" "$REMOTE" | cat + ;; + vimdiff) "$merge_tool_path" -d -c "wincmd l" "$LOCAL" "$REMOTE" ;; @@ -164,9 +166,9 @@ if test -z "$merge_tool"; then if test -n "$DISPLAY"; then # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare if test -n "$GNOME_DESKTOP_SESSION_ID" ; then - merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff" + merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff diffuse" else - merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff" + merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff diffuse" fi fi if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index a00e9431c5..af68466ebc 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -25,8 +25,8 @@ OPTIONS --tool=:: Use the diff tool specified by . Valid merge tools are: - kdiff3, kompare, tkdiff, meld, xxdiff, emerge, - vimdiff, gvimdiff, ecmerge, and opendiff + kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, + ecmerge, diffuse and opendiff + If a diff tool is not specified, 'git-difftool' will use the configuration variable `diff.tool`. If the -- cgit v1.2.3 From afcbc8e7ecb18a3ee542e808f02f5df7d56d5bdc Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Tue, 7 Apr 2009 01:21:20 -0700 Subject: difftool: move 'git-difftool' out of contrib This prepares 'git-difftool' and its documentation for mainstream use. 'git-difftool-helper' became 'git-difftool--helper' since users should not use it directly. 'git-difftool' was added to the list of commands as an ancillaryinterrogator. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 76 ------------ contrib/difftool/git-difftool-helper | 221 ----------------------------------- contrib/difftool/git-difftool.txt | 97 --------------- 3 files changed, 394 deletions(-) delete mode 100755 contrib/difftool/git-difftool delete mode 100755 contrib/difftool/git-difftool-helper delete mode 100644 contrib/difftool/git-difftool.txt (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool deleted file mode 100755 index 8c160e5bb4..0000000000 --- a/contrib/difftool/git-difftool +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env perl -# Copyright (c) 2009 David Aguilar -# -# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible -# git-difftool-helper script. This script exports -# GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and -# GIT_DIFFTOOL_NO_PROMPT and GIT_DIFF_TOOL for use by git-difftool-helper. -# Any arguments that are unknown to this script are forwarded to 'git diff'. - -use strict; -use warnings; -use Cwd qw(abs_path); -use File::Basename qw(dirname); - -my $DIR = abs_path(dirname($0)); - - -sub usage -{ - print << 'USAGE'; -usage: git difftool [--tool=] [-y|--no-prompt] ["git diff" options] -USAGE - exit 1; -} - -sub setup_environment -{ - $ENV{PATH} = "$DIR:$ENV{PATH}"; - $ENV{GIT_PAGER} = ''; - $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper'; -} - -sub exe -{ - my $exe = shift; - if ($^O eq 'MSWin32' || $^O eq 'msys') { - return "$exe.exe"; - } - return $exe; -} - -sub generate_command -{ - my @command = (exe('git'), 'diff'); - my $skip_next = 0; - my $idx = -1; - for my $arg (@ARGV) { - $idx++; - if ($skip_next) { - $skip_next = 0; - next; - } - if ($arg eq '-t' || $arg eq '--tool') { - usage() if $#ARGV <= $idx; - $ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1]; - $skip_next = 1; - next; - } - if ($arg =~ /^--tool=/) { - $ENV{GIT_DIFF_TOOL} = substr($arg, 7); - next; - } - if ($arg eq '-y' || $arg eq '--no-prompt') { - $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true'; - next; - } - if ($arg eq '-h' || $arg eq '--help') { - usage(); - } - push @command, $arg; - } - return @command -} - -setup_environment(); -exec(generate_command()); diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper deleted file mode 100755 index 4b0daec5a7..0000000000 --- a/contrib/difftool/git-difftool-helper +++ /dev/null @@ -1,221 +0,0 @@ -#!/bin/sh -# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher. -# This script is typically launched by using the 'git difftool' -# convenience command. -# -# Copyright (c) 2009 David Aguilar - -# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt. -should_prompt () { - test -z "$GIT_DIFFTOOL_NO_PROMPT" -} - -# This function prepares temporary files and launches the appropriate -# merge tool. -launch_merge_tool () { - # Merged is the filename as it appears in the work tree - # Local is the contents of a/filename - # Remote is the contents of b/filename - # Custom merge tool commands might use $BASE so we provide it - MERGED="$1" - LOCAL="$2" - REMOTE="$3" - BASE="$1" - - # $LOCAL and $REMOTE are temporary files so prompt - # the user with the real $MERGED name before launching $merge_tool. - if should_prompt; then - printf "\nViewing: '$MERGED'\n" - printf "Hit return to launch '%s': " "$merge_tool" - read ans - fi - - # Run the appropriate merge tool command - case "$merge_tool" in - kdiff3) - basename=$(basename "$MERGED") - "$merge_tool_path" --auto \ - --L1 "$basename (A)" \ - --L2 "$basename (B)" \ - "$LOCAL" "$REMOTE" \ - > /dev/null 2>&1 - ;; - - kompare) - "$merge_tool_path" "$LOCAL" "$REMOTE" - ;; - - tkdiff) - "$merge_tool_path" "$LOCAL" "$REMOTE" - ;; - - meld) - "$merge_tool_path" "$LOCAL" "$REMOTE" - ;; - - diffuse) - "$merge_tool_path" "$LOCAL" "$REMOTE" | cat - ;; - - vimdiff) - "$merge_tool_path" -d -c "wincmd l" "$LOCAL" "$REMOTE" - ;; - - gvimdiff) - "$merge_tool_path" -d -c "wincmd l" -f "$LOCAL" "$REMOTE" - ;; - - xxdiff) - "$merge_tool_path" \ - -R 'Accel.Search: "Ctrl+F"' \ - -R 'Accel.SearchForward: "Ctrl-G"' \ - "$LOCAL" "$REMOTE" - ;; - - opendiff) - "$merge_tool_path" "$LOCAL" "$REMOTE" | cat - ;; - - ecmerge) - "$merge_tool_path" "$LOCAL" "$REMOTE" \ - --default --mode=merge2 --to="$MERGED" - ;; - - emerge) - "$merge_tool_path" -f emerge-files-command \ - "$LOCAL" "$REMOTE" "$(basename "$MERGED")" - ;; - - *) - if test -n "$merge_tool_cmd"; then - ( eval $merge_tool_cmd ) - fi - ;; - esac -} - -# Verifies that (difftool|mergetool)..cmd exists -valid_custom_tool() { - merge_tool_cmd="$(git config difftool.$1.cmd)" - test -z "$merge_tool_cmd" && - merge_tool_cmd="$(git config mergetool.$1.cmd)" - test -n "$merge_tool_cmd" -} - -# Verifies that the chosen merge tool is properly setup. -# Built-in merge tools are always valid. -valid_tool() { - case "$1" in - kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) - ;; # happy - *) - if ! valid_custom_tool "$1" - then - return 1 - fi - ;; - esac -} - -# Sets up the merge_tool_path variable. -# This handles the difftool..path configuration. -# This also falls back to mergetool defaults. -init_merge_tool_path() { - merge_tool_path=$(git config difftool."$1".path) - test -z "$merge_tool_path" && - merge_tool_path=$(git config mergetool."$1".path) - if test -z "$merge_tool_path"; then - case "$1" in - vimdiff) - merge_tool_path=vim - ;; - gvimdiff) - merge_tool_path=gvim - ;; - emerge) - merge_tool_path=emacs - ;; - *) - merge_tool_path="$1" - ;; - esac - fi -} - -# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values -test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL" -test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL" - -# If merge tool was not specified then use the diff.tool -# configuration variable. If that's invalid then reset merge_tool. -# Fallback to merge.tool. -if test -z "$merge_tool"; then - merge_tool=$(git config diff.tool) - test -z "$merge_tool" && - merge_tool=$(git config merge.tool) - if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then - echo >&2 "git config option diff.tool set to unknown tool: $merge_tool" - echo >&2 "Resetting to default..." - unset merge_tool - fi -fi - -# Try to guess an appropriate merge tool if no tool has been set. -if test -z "$merge_tool"; then - # We have a $DISPLAY so try some common UNIX merge tools - if test -n "$DISPLAY"; then - # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare - if test -n "$GNOME_DESKTOP_SESSION_ID" ; then - merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff diffuse" - else - merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff diffuse" - fi - fi - if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then - # $EDITOR is emacs so add emerge as a candidate - merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff" - elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then - # $EDITOR is vim so add vimdiff as a candidate - merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge" - else - merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" - fi - echo "merge tool candidates: $merge_tool_candidates" - - # Loop over each candidate and stop when a valid merge tool is found. - for i in $merge_tool_candidates - do - init_merge_tool_path $i - if type "$merge_tool_path" > /dev/null 2>&1; then - merge_tool=$i - break - fi - done - - if test -z "$merge_tool" ; then - echo "No known merge resolution program available." - exit 1 - fi - -else - # A merge tool has been set, so verify that it's valid. - if ! valid_tool "$merge_tool"; then - echo >&2 "Unknown merge tool $merge_tool" - exit 1 - fi - - init_merge_tool_path "$merge_tool" - - if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then - echo "The merge tool $merge_tool is not available as '$merge_tool_path'" - exit 1 - fi -fi - - -# Launch the merge tool on each path provided by 'git diff' -while test $# -gt 6 -do - launch_merge_tool "$1" "$2" "$5" - shift 7 -done diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt deleted file mode 100644 index af68466ebc..0000000000 --- a/contrib/difftool/git-difftool.txt +++ /dev/null @@ -1,97 +0,0 @@ -git-difftool(1) -=============== - -NAME ----- -git-difftool - Show changes using common diff tools - -SYNOPSIS --------- -'git difftool' [--tool=] [-y|--no-prompt] [<'git diff' options>] - -DESCRIPTION ------------ -'git-difftool' is a git command that allows you to compare and edit files -between revisions using common diff tools. 'git difftool' is a frontend -to 'git-diff' and accepts the same options and arguments. - -OPTIONS -------- --y:: ---no-prompt:: - Do not prompt before launching a diff tool. - --t :: ---tool=:: - Use the diff tool specified by . - Valid merge tools are: - kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, - ecmerge, diffuse and opendiff -+ -If a diff tool is not specified, 'git-difftool' -will use the configuration variable `diff.tool`. If the -configuration variable `diff.tool` is not set, 'git-difftool' -will pick a suitable default. -+ -You can explicitly provide a full path to the tool by setting the -configuration variable `difftool..path`. For example, you -can configure the absolute path to kdiff3 by setting -`difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the -tool is available in PATH. -+ -Instead of running one of the known diff tools, -'git-difftool' can be customized to run an alternative program -by specifying the command line to invoke in a configuration -variable `difftool..cmd`. -+ -When 'git-difftool' is invoked with this tool (either through the -`-t` or `--tool` option or the `diff.tool` configuration variable) -the configured command line will be invoked with the following -variables available: `$LOCAL` is set to the name of the temporary -file containing the contents of the diff pre-image and `$REMOTE` -is set to the name of the temporary file containing the contents -of the diff post-image. `$BASE` is provided for compatibility -with custom merge tool commands and has the same value as `$LOCAL`. - -See linkgit:git-diff[1] for the full list of supported options. - -CONFIG VARIABLES ----------------- -'git-difftool' falls back to 'git-mergetool' config variables when the -difftool equivalents have not been defined. - -diff.tool:: - The default diff tool to use. - -difftool..path:: - Override the path for the given tool. This is useful in case - your tool is not in the PATH. - -difftool..cmd:: - Specify the command to invoke the specified diff tool. -+ -See the `--tool=` option above for more details. - -SEE ALSO --------- -linkgit:git-diff[1]:: - Show changes between commits, commit and working tree, etc - -linkgit:git-mergetool[1]:: - Run merge conflict resolution tools to resolve merge conflicts - -linkgit:git-config[1]:: - Get and set repository or global options - - -AUTHOR ------- -Written by David Aguilar . - -Documentation --------------- -Documentation by David Aguilar and the git-list . - -GIT ---- -Part of the linkgit:git[1] suite -- cgit v1.2.3 From e2dc2de917778a0601564e238c3cd61614f55e5f Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Mon, 6 Apr 2009 01:31:27 -0700 Subject: bash completion: add git-difftool This adds completion for difftool's --tool flag. The known diff tool names were also consolidated into a single variable. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e099ed48ff..069e19e82a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -910,6 +910,26 @@ _git_diff () __git_complete_file } +__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff + tkdiff vimdiff gvimdiff xxdiff +" + +_git_difftool () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --tool=*) + __gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}" + return + ;; + --*) + __gitcomp "--tool=" + return + ;; + esac + COMPREPLY=() +} + __git_fetch_options=" --quiet --verbose --append --upload-pack --force --keep --depth= --tags --no-tags @@ -1172,11 +1192,7 @@ _git_mergetool () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --tool=*) - __gitcomp " - kdiff3 tkdiff meld xxdiff emerge - vimdiff gvimdiff ecmerge diffuse - opendiff - " "" "${cur##--tool=}" + __gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}" return ;; --*) @@ -1901,6 +1917,7 @@ _git () config) _git_config ;; describe) _git_describe ;; diff) _git_diff ;; + difftool) _git_difftool ;; fetch) _git_fetch ;; format-patch) _git_format_patch ;; fsck) _git_fsck ;; -- cgit v1.2.3 From 680ebc01806187b33cab9093c39102468298350f Mon Sep 17 00:00:00 2001 From: Mike Ralphson Date: Fri, 17 Apr 2009 19:13:28 +0100 Subject: Documentation: fix typos / spelling mistakes Signed-off-by: Mike Ralphson Signed-off-by: Junio C Hamano --- contrib/thunderbird-patch-inline/README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/thunderbird-patch-inline/README b/contrib/thunderbird-patch-inline/README index 39f96aa115..000147bbe4 100644 --- a/contrib/thunderbird-patch-inline/README +++ b/contrib/thunderbird-patch-inline/README @@ -1,12 +1,12 @@ appp.sh is a script that is supposed to be used together with ExternalEditor -for Mozilla Thundebird. It will let you include patches inline in e-mails +for Mozilla Thunderbird. It will let you include patches inline in e-mails in an easy way. Usage: - Generate the patch with git format-patch. - Start writing a new e-mail in Thunderbird. - Press the external editor button (or Ctrl-E) to run appp.sh -- Select the previosly generated patch file. +- Select the previously generated patch file. - Finish editing the e-mail. Any text that is entered into the message editor before appp.sh is called -- cgit v1.2.3 From 6123d7196fdd9ac9aed24b4aeed854813fe9a6b1 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Sat, 25 Apr 2009 13:46:14 +0200 Subject: bash completion: show-branch color support This implements completion of --color and --no-color for "git show-branch" and color.showbranch for "git config". Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1a90cb87f5..b588387262 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1333,7 +1333,8 @@ _git_config () __gitcomp "$(__git_merge_strategies)" return ;; - color.branch|color.diff|color.interactive|color.status|color.ui) + color.branch|color.diff|color.interactive|\ + color.showbranch|color.status|color.ui) __gitcomp "always never auto" return ;; @@ -1415,6 +1416,7 @@ _git_config () color.interactive.help color.interactive.prompt color.pager + color.showbranch color.status color.status.added color.status.changed @@ -1676,6 +1678,7 @@ _git_show_branch () __gitcomp " --all --remotes --topo-order --current --more= --list --independent --merge-base --no-name + --color --no-color --sha1-name --topics --reflog " return -- cgit v1.2.3 From 4b25d091ba53c758fae0096b8c0662371857b9d9 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Fri, 1 May 2009 12:06:36 +0300 Subject: Fix a bunch of pointer declarations (codestyle) Essentially; s/type* /type */ as per the coding guidelines. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- contrib/convert-objects/convert-objects.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/convert-objects/convert-objects.c b/contrib/convert-objects/convert-objects.c index 90e7900e6d..f3b57bf1d2 100644 --- a/contrib/convert-objects/convert-objects.c +++ b/contrib/convert-objects/convert-objects.c @@ -59,7 +59,7 @@ static void convert_ascii_sha1(void *buffer) struct entry *entry; if (get_sha1_hex(buffer, sha1)) - die("expected sha1, got '%s'", (char*) buffer); + die("expected sha1, got '%s'", (char *) buffer); entry = convert_entry(sha1); memcpy(buffer, sha1_to_hex(entry->new_sha1), 40); } @@ -100,7 +100,7 @@ static int write_subdirectory(void *buffer, unsigned long size, const char *base if (!slash) { newlen += sprintf(new + newlen, "%o %s", mode, path); new[newlen++] = '\0'; - hashcpy((unsigned char*)new + newlen, (unsigned char *) buffer + len - 20); + hashcpy((unsigned char *)new + newlen, (unsigned char *) buffer + len - 20); newlen += 20; used += len; @@ -271,7 +271,7 @@ static void convert_commit(void *buffer, unsigned long size, unsigned char *resu unsigned long orig_size = size; if (memcmp(buffer, "tree ", 5)) - die("Bad commit '%s'", (char*) buffer); + die("Bad commit '%s'", (char *) buffer); convert_ascii_sha1((char *) buffer + 5); buffer = (char *) buffer + 46; /* "tree " + "hex sha1" + "\n" */ while (!memcmp(buffer, "parent ", 7)) { -- cgit v1.2.3 From 226b343cde7624e4f273a45d83009799447c914b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 3 May 2009 23:25:31 -0700 Subject: completion: add missing configuration variables to _git_config() Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1a90cb87f5..28682a79be 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1393,6 +1393,7 @@ _git_config () ;; esac __gitcomp " + alias. apply.whitespace branch.autosetupmerge branch.autosetuprebase @@ -1410,6 +1411,9 @@ _git_config () color.diff.old color.diff.plain color.diff.whitespace + color.grep + color.grep.external + color.grep.match color.interactive color.interactive.header color.interactive.help @@ -1427,6 +1431,7 @@ _git_config () core.autocrlf core.bare core.compression + core.createObject core.deltaBaseCacheLimit core.editor core.excludesfile @@ -1457,11 +1462,20 @@ _git_config () diff.renameLimit diff.renameLimit. diff.renames + diff.suppressBlankEmpty + diff.tool + diff.wordRegex + difftool.prompt fetch.unpackLimit + format.attach + format.cc format.headers format.numbered format.pretty + format.signoff + format.subjectprefix format.suffix + format.thread gc.aggressiveWindow gc.auto gc.autopacklimit @@ -1472,6 +1486,7 @@ _git_config () gc.rerereresolved gc.rerereunresolved gitcvs.allbinary + gitcvs.commitmsgannotation gitcvs.dbTableNamePrefix gitcvs.dbdriver gitcvs.dbname @@ -1506,13 +1521,23 @@ _git_config () http.sslVerify i18n.commitEncoding i18n.logOutputEncoding + imap.folder + imap.host + imap.pass + imap.port + imap.preformattedHTML + imap.sslverify + imap.tunnel + imap.user instaweb.browser instaweb.httpd instaweb.local instaweb.modulepath instaweb.port + interactive.singlekey log.date log.showroot + mailmap.file man.viewer merge.conflictstyle merge.log @@ -1521,6 +1546,7 @@ _git_config () merge.tool merge.verbosity mergetool.keepBackup + mergetool.prompt pack.compression pack.deltaCacheLimit pack.deltaCacheSize @@ -1532,6 +1558,8 @@ _git_config () pack.windowMemory pull.octopus pull.twohead + push.default + rebase.stat receive.denyCurrentBranch receive.denyDeletes receive.denyNonFastForwards @@ -1540,6 +1568,26 @@ _git_config () repack.usedeltabaseoffset rerere.autoupdate rerere.enabled + sendemail.aliasesfile + sendemail.aliasesfiletype + sendemail.bcc + sendemail.cc + sendemail.cccmd + sendemail.chainreplyto + sendemail.confirm + sendemail.envelopesender + sendemail.multiedit + sendemail.signedoffbycc + sendemail.smtpencryption + sendemail.smtppass + sendemail.smtpserver + sendemail.smtpserverport + sendemail.smtpuser + sendemail.suppresscc + sendemail.suppressfrom + sendemail.thread + sendemail.to + sendemail.validate showbranch.default status.relativePaths status.showUntrackedFiles -- cgit v1.2.3 From 0aa62fd0414b0e8a6271d1d0dd80e5f640633473 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 3 May 2009 23:25:32 -0700 Subject: completion: add {gui,diff,merge}tool, man, and pager config variables Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 28682a79be..ec02b06cf2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1376,6 +1376,39 @@ _git_config () __gitcomp "$(__git_heads)" "$pfx" "$cur" "." return ;; + guitool.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp " + argprompt cmd confirm needsfile noconsole norescan + prompt revprompt revunmerged title + " "$pfx" "$cur" + return + ;; + difftool.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp "cmd path" "$pfx" "$cur" + return + ;; + man.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp "cmd path" "$pfx" "$cur" + return + ;; + mergetool.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp "cmd path trustExitCode" "$pfx" "$cur" + return + ;; + pager.*) + local pfx="${cur%.*}." + cur="${cur#*.}" + __gitcomp "$(__git_all_commands)" "$pfx" "$cur" + return + ;; remote.*.*) local pfx="${cur%.*}." cur="${cur##*.}" @@ -1391,6 +1424,12 @@ _git_config () __gitcomp "$(__git_remotes)" "$pfx" "$cur" "." return ;; + url.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp "insteadof" "$pfx" "$cur" + return + ;; esac __gitcomp " alias. @@ -1465,6 +1504,7 @@ _git_config () diff.suppressBlankEmpty diff.tool diff.wordRegex + difftool. difftool.prompt fetch.unpackLimit format.attach @@ -1495,6 +1535,7 @@ _git_config () gitcvs.enabled gitcvs.logfile gitcvs.usecrlfattr + guitool. gui.blamehistoryctx gui.commitmsgwidth gui.copyblamethreshold @@ -1538,6 +1579,7 @@ _git_config () log.date log.showroot mailmap.file + man. man.viewer merge.conflictstyle merge.log @@ -1545,6 +1587,7 @@ _git_config () merge.stat merge.tool merge.verbosity + mergetool. mergetool.keepBackup mergetool.prompt pack.compression @@ -1556,6 +1599,7 @@ _git_config () pack.threads pack.window pack.windowMemory + pager. pull.octopus pull.twohead push.default @@ -1593,6 +1637,7 @@ _git_config () status.showUntrackedFiles tar.umask transfer.unpackLimit + url. user.email user.name user.signingkey -- cgit v1.2.3 From 9b82d63b5a109112643843a8e6d1a201fdf2ec63 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 3 May 2009 23:25:33 -0700 Subject: completion: complete values for help.format Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ec02b06cf2..023b0c9974 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1348,6 +1348,10 @@ _git_config () " return ;; + help.format) + __gitcomp "man info web html" + return + ;; *.*) COMPREPLY=() return -- cgit v1.2.3 From 672c68cbb90921a133ddf71d002342a448b6dd38 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 3 May 2009 23:25:34 -0700 Subject: completion: complete values for log.date Add raw to the date formats too. Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 023b0c9974..d67ffd9384 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1116,6 +1116,7 @@ __git_log_shortlog_options=" " __git_log_pretty_formats="oneline short medium full fuller email raw format:" +__git_log_date_formats="relative iso8601 rfc2822 short local default raw" _git_log () { @@ -1139,9 +1140,7 @@ _git_log () return ;; --date=*) - __gitcomp " - relative iso8601 rfc2822 short local default - " "" "${cur##--date=}" + __gitcomp "$__git_log_date_formats" "" "${cur##--date=}" return ;; --*) @@ -1352,6 +1351,10 @@ _git_config () __gitcomp "man info web html" return ;; + log.date) + __gitcomp "$__git_log_date_formats" + return + ;; *.*) COMPREPLY=() return -- cgit v1.2.3 From ae616de6d53ef14ba11d2aa32f366086c1435dfa Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 3 May 2009 23:25:35 -0700 Subject: completion: complete values for send-email Add completion for --confirm, --suppress-cc, and --smtp-encryption command line arguments. Add completion for aliasfiletype and confirm configuration variables. Since --smtp-ssl is deprecated, replace it with --smtp-encryption and the two options ssl and tls. Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 39 +++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d67ffd9384..1683e6d7b8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1282,18 +1282,39 @@ _git_rebase () __gitcomp "$(__git_refs)" } +__git_send_email_confirm_options="always never auto cc compose" +__git_send_email_suppresscc_options="author self cc ccbody sob cccmd body all" + _git_send_email () { local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in + --confirm=*) + __gitcomp " + $__git_send_email_confirm_options + " "" "${cur##--confirm=}" + return + ;; + --suppress-cc=*) + __gitcomp " + $__git_send_email_suppresscc_options + " "" "${cur##--suppress-cc=}" + + return + ;; + --smtp-encryption=*) + __gitcomp "ssl tls" "" "${cur##--smtp-encryption=}" + return + ;; --*) __gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to - --compose --dry-run --envelope-sender --from --identity + --compose --confirm= --dry-run --envelope-sender + --from --identity --in-reply-to --no-chain-reply-to --no-signed-off-by-cc --no-suppress-from --no-thread --quiet --signed-off-by-cc --smtp-pass --smtp-server - --smtp-server-port --smtp-ssl --smtp-user --subject - --suppress-cc --suppress-from --thread --to + --smtp-server-port --smtp-encryption= --smtp-user + --subject --suppress-cc= --suppress-from --thread --to --validate --no-validate" return ;; @@ -1355,6 +1376,18 @@ _git_config () __gitcomp "$__git_log_date_formats" return ;; + sendemail.aliasesfiletype) + __gitcomp "mutt mailrc pine elm gnus" + return + ;; + sendemail.confirm) + __gitcomp "$__git_send_email_confirm_options" + return + ;; + sendemail.suppresscc) + __gitcomp "$__git_send_email_suppresscc_options" + return + ;; *.*) COMPREPLY=() return -- cgit v1.2.3 From 00652369ff285f84e440f5d41d708283e30825d7 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 8 May 2009 18:23:32 -0700 Subject: bash completion: complete variable names for "git config" with options This makes it easier for users to get and unset their configuration variables without having to open documentation or dig through their configuration file. __git_config_get_set_variables() retrieves the set configuration variables from the appropriate configuration file. For example, if the user has previously specified --global only the global variables are returned. The same applies for --system, and --file. If no location has been specified, all set variables are returned. Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1683e6d7b8..ad26b7c5ae 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1322,6 +1322,35 @@ _git_send_email () COMPREPLY=() } +__git_config_get_set_variables () +{ + local prevword word config_file= c=$COMP_CWORD + while [ $c -gt 1 ]; do + word="${COMP_WORDS[c]}" + case "$word" in + --global|--system|--file=*) + config_file="$word" + break + ;; + -f|--file) + config_file="$word $prevword" + break + ;; + esac + prevword=$word + c=$((--c)) + done + + for i in $(git --git-dir="$(__gitdir)" config $config_file --list \ + 2>/dev/null); do + case "$i" in + *.*) + echo "${i/=*/}" + ;; + esac + done +} + _git_config () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1388,6 +1417,10 @@ _git_config () __gitcomp "$__git_send_email_suppresscc_options" return ;; + --get|--get-all|--unset|--unset-all) + __gitcomp "$(__git_config_get_set_variables)" + return + ;; *.*) COMPREPLY=() return -- cgit v1.2.3 From 8763dbb1b24c260243f69130c734c13563a16db6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 16 May 2009 11:46:22 -0700 Subject: completion: fix PS1 display during a merge on detached HEAD If your merge stops in a conflict while on a detached HEAD, recent completion code fails to show anything. This was because various cases added to support the operation-in-progress markers (e.g. REBASE, MERGING) forgot that they need to set the variable "b" to something for the result they computed to be displayed at all. Probably not many people make trial merges on a detached HEAD (which is tremendously useful feature of git, by the way), and that may be why this was not noticed for a long time. Acked-By: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1683e6d7b8..c2f8ea3444 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -99,10 +99,10 @@ __git_ps1 () elif [ -d "$g/rebase-merge" ]; then r="|REBASE-m" b="$(cat "$g/rebase-merge/head-name")" - elif [ -f "$g/MERGE_HEAD" ]; then - r="|MERGING" - b="$(git symbolic-ref HEAD 2>/dev/null)" else + if [ -f "$g/MERGE_HEAD" ]; then + r="|MERGING" + fi if [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi -- cgit v1.2.3 From ff790b6a4bb7fa3bbccd5ea23cefd89da900aa2e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 May 2009 01:53:19 -0700 Subject: completion: simplify "current branch" in __git_ps1() As I very often work on a detached HEAD, I found it pretty confusing when __git_ps1() said 'some-name'. Did I create a branch with that name by mistake, or do I happen to be on a commit with that exact tag? This patch fixes the issue by enclosing non branch names in a pair of parentheses when used to substitute %s token in __git_ps1() argument. It also fixes a small bug where the branch part is left empty when .git/HEAD is unreadable for whatever reason. The output now says "(unknown)". Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c2f8ea3444..be591468db 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -106,13 +106,14 @@ __git_ps1 () if [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi - if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then - if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then - if [ -r "$g/HEAD" ]; then - b="$(cut -c1-7 "$g/HEAD")..." - fi - fi - fi + + b="$(git symbolic-ref HEAD 2>/dev/null)" || { + b="$(git describe --exact-match HEAD 2>/dev/null)" || + b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." || + b="unknown" + + b="($b)" + } fi local w -- cgit v1.2.3 From dd42c2f015102626562da05bb290f47862ea06fb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 May 2009 01:56:21 -0700 Subject: completion: enhance "current branch" display Introduce GIT_PS1_DESCRIBE option you can set to "contains", "branch", or "describe" to tweak the way how a detached HEAD is described. The default behaviour is to describe only exact match with some tag (otherwise use the first 7 hexdigits) as before. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index be591468db..dd6cd250e3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -108,10 +108,21 @@ __git_ps1 () fi b="$(git symbolic-ref HEAD 2>/dev/null)" || { - b="$(git describe --exact-match HEAD 2>/dev/null)" || + + b="$( + case "${GIT_PS1_DESCRIBE_STYLE-}" in + (contains) + git describe --contains HEAD ;; + (branch) + git describe --contains --all HEAD ;; + (describe) + git describe HEAD ;; + (* | default) + git describe --exact-match HEAD ;; + esac 2>/dev/null)" || + b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." || b="unknown" - b="($b)" } fi -- cgit v1.2.3 From 076c32370d8a6ac2fb57b2a55c674942e106f8ab Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 16 May 2009 20:42:43 -0700 Subject: completion: add missing options to show-branch and show Add --oneline and --abbrev-commit to show and --sparse to show-branch. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index dd6cd250e3..a0c5794828 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1804,7 +1804,7 @@ _git_show () return ;; --*) - __gitcomp "--pretty= --format= + __gitcomp "--pretty= --format= --abbrev-commit --oneline $__git_diff_common_options " return @@ -1821,7 +1821,7 @@ _git_show_branch () __gitcomp " --all --remotes --topo-order --current --more= --list --independent --merge-base --no-name - --sha1-name --topics --reflog + --sha1-name --sparse --topics --reflog " return ;; -- cgit v1.2.3 From 8dfb17e1fd7dec1d3a1978eb46743964c481cd08 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Mon, 18 May 2009 18:24:30 +0200 Subject: completion: use git rev-parse to detect bare repos Its check is more robust than a config check for core.bare Trivially-Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a0c5794828..f44152c433 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -132,7 +132,7 @@ __git_ps1 () local c if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then - if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then + if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then c="BARE:" else b="GIT_DIR!" -- cgit v1.2.3 From 5ffd3113d4109ae5d3595425af3ff4a781617631 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 23 May 2009 14:26:44 +0200 Subject: post-receive-email: hooks.showrev: show how to include both web link and patch Add a comment showing how to include a web link (i.e. gitweb/cgit) and a patch in the email that is sent for each pushed commit. The quoting was tricky enough that it's worth documenting. To add two blank lines (i.e. put \n\n in the printf), you would need to say \\\\n\\\\n, and in the end, the pair of "echo" statements seemed better. This is used in glibc.git repository: http://sources.redhat.com/git/gitweb.cgi?p=glibc.git;a=summary push-triggered messages have been sent to this list since May 21: http://sourceware.org/ml/glibc-cvs/2009-q2/ Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 4 ++++ 1 file changed, 4 insertions(+) mode change 100644 => 100755 contrib/hooks/post-receive-email (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email old mode 100644 new mode 100755 index 60cbab65d3..2a66063e44 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -44,6 +44,10 @@ # --pretty %s", displaying the commit id, author, date and log # message. To list full patches separated by a blank line, you # could set this to "git show -C %s; echo". +# To list a gitweb/cgit URL *and* a full patch for each change set, use this: +# "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo" +# Be careful if "..." contains things that will be expanded by shell "eval" +# or printf. # # Notes # ----- -- cgit v1.2.3 From b6f0621a462e4ff11999c8e2d8279c0ffd3be201 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sun, 24 May 2009 00:24:41 +0000 Subject: mergetool--lib: add support for araxis merge Araxis merge is now a built-in diff/merge tool. This adds araxis to git-completion and updates the documentation to mention araxis. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1683e6d7b8..ead530d3a3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -911,7 +911,7 @@ _git_diff () } __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff - tkdiff vimdiff gvimdiff xxdiff + tkdiff vimdiff gvimdiff xxdiff araxis " _git_difftool () -- cgit v1.2.3 From d7107ca65f79c2d95bc327bee44c09073b4de631 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 25 May 2009 22:56:03 -0700 Subject: completion: fix PS1 display during an AM on detached HEAD This is a companion patch to previous 8763dbb (completion: fix PS1 display during a merge on detached HEAD, 2009-05-16). While rebasing or running am on a detached HEAD, the code failed to set $b (branch description) that enables the whole status display business. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0c8bb536c8..c84d765ff9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -84,26 +84,24 @@ __git_ps1 () if [ -n "$g" ]; then local r local b - if [ -d "$g/rebase-apply" ]; then - if [ -f "$g/rebase-apply/rebasing" ]; then - r="|REBASE" - elif [ -f "$g/rebase-apply/applying" ]; then - r="|AM" - else - r="|AM/REBASE" - fi - b="$(git symbolic-ref HEAD 2>/dev/null)" - elif [ -f "$g/rebase-merge/interactive" ]; then + if [ -f "$g/rebase-merge/interactive" ]; then r="|REBASE-i" b="$(cat "$g/rebase-merge/head-name")" elif [ -d "$g/rebase-merge" ]; then r="|REBASE-m" b="$(cat "$g/rebase-merge/head-name")" else - if [ -f "$g/MERGE_HEAD" ]; then + if [ -d "$g/rebase-apply" ]; then + if [ -f "$g/rebase-apply/rebasing" ]; then + r="|REBASE" + elif [ -f "$g/rebase-apply/applying" ]; then + r="|AM" + else + r="|AM/REBASE" + fi + elif [ -f "$g/MERGE_HEAD" ]; then r="|MERGING" - fi - if [ -f "$g/BISECT_LOG" ]; then + elif [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi -- cgit v1.2.3 From ee6b71141f2803bd4d4f6e1a17ef826567dc9644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 29 May 2009 14:00:36 +0200 Subject: bash: remove always true if statement from __git_ps1() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recent commits 8763dbb1 (completion: fix PS1 display during a merge on detached HEAD, 2009-05-16), ff790b6a (completion: simplify "current branch" in __git_ps1(), 2009-05-10), and d7107ca6 (completion: fix PS1 display during an AM on detached HEAD, 2009-05-26) ensure that the branch name in __git_ps1() is always set to something sensible. Therefore, the condition for checking the non-empty branch name is always fulfilled, and can be removed. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c84d765ff9..98b9cbedc2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -150,12 +150,10 @@ __git_ps1 () fi fi - if [ -n "$b" ]; then - if [ -n "${1-}" ]; then - printf "$1" "$c${b##refs/heads/}$w$i$r" - else - printf " (%s)" "$c${b##refs/heads/}$w$i$r" - fi + if [ -n "${1-}" ]; then + printf "$1" "$c${b##refs/heads/}$w$i$r" + else + printf " (%s)" "$c${b##refs/heads/}$w$i$r" fi fi } -- cgit v1.2.3 From 2414b45ce0c56f7744e00e7fef3682a77375701e Mon Sep 17 00:00:00 2001 From: Daniel Trstenjak Date: Tue, 2 Jun 2009 20:03:22 +0200 Subject: Show presence of stashed changes in bash prompt. Add a '$' in the __git_ps1 output to show stashed changes are present, when GIT_PS1_SHOWSTASHSTATE is set to a nonempty value. The code for checking if the stash has entries is taken from 'git-stash.sh'. Signed-off-by: Daniel Trstenjak Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 98b9cbedc2..80190a6b16 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -40,6 +40,10 @@ # with the bash.showDirtyState variable, which defaults to true # once GIT_PS1_SHOWDIRTYSTATE is enabled. # +# You can also see if currently something is stashed, by setting +# GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed, +# then a '$' will be shown next to the branch name. +# # To submit patches: # # *) Read Documentation/SubmittingPatches @@ -127,6 +131,7 @@ __git_ps1 () local w local i + local s local c if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then @@ -148,12 +153,15 @@ __git_ps1 () fi fi fi + if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then + git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$" + fi fi if [ -n "${1-}" ]; then - printf "$1" "$c${b##refs/heads/}$w$i$r" + printf "$1" "$c${b##refs/heads/}$w$i$s$r" else - printf " (%s)" "$c${b##refs/heads/}$w$i$r" + printf " (%s)" "$c${b##refs/heads/}$w$i$s$r" fi fi } -- cgit v1.2.3 From 8513c54b2a4a821336d10fae6e02db70f0876abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 9 Jun 2009 00:57:39 +0200 Subject: bash: add support for 'git stash pop --index' option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 80190a6b16..cc94ef44b2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1881,10 +1881,10 @@ _git_stash () save,--*) __gitcomp "--keep-index" ;; - apply,--*) + apply,--*|pop,--*) __gitcomp "--index" ;; - show,--*|drop,--*|pop,--*|branch,--*) + show,--*|drop,--*|branch,--*) COMPREPLY=() ;; show,*|apply,*|drop,*|pop,*|branch,*) -- cgit v1.2.3 From 6fb37f86bce7cf19b4dbff2aa8a93df5ac5c4cbe Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 17 Jun 2009 14:49:39 +0200 Subject: import-tars: support symlinks Without this patch, symbolic links are turned into empty files. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/fast-import/import-tars.perl | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 6309d146e7..78e40d2a13 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -82,10 +82,16 @@ foreach my $tar_file (@ARGV) $mtime = oct $mtime; next if $typeflag == 5; # directory - print FI "blob\n", "mark :$next_mark\n", "data $size\n"; - while ($size > 0 && read(I, $_, 512) == 512) { - print FI substr($_, 0, $size); - $size -= 512; + print FI "blob\n", "mark :$next_mark\n"; + if ($typeflag == 2) { # symbolic link + print FI "data ", length($linkname), "\n", $linkname; + $mode = 0120000; + } else { + print FI "data $size\n"; + while ($size > 0 && read(I, $_, 512) == 512) { + print FI substr($_, 0, $size); + $size -= 512; + } } print FI "\n"; @@ -118,7 +124,8 @@ EOF { my ($mark, $mode) = @{$files{$path}}; $path =~ s,^([^/]+)/,, if $have_top_dir; - printf FI "M %o :%i %s\n", $mode & 0111 ? 0755 : 0644, $mark, $path; + $mode = $mode & 0111 ? 0755 : 0644 unless $mode == 0120000; + printf FI "M %o :%i %s\n", $mode, $mark, $path; } print FI "\n"; -- cgit v1.2.3 From cb8a9bd518002dd4fb693df6230b4976bafc15e0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 18 Jun 2009 14:31:32 +0200 Subject: Test cccmd in t9001-send-email.sh and fix some bugs For another patch series I'm working on I needed some tests for the cc-cmd feature of git-send-email. This patch adds 3 tests for the feature and for the possibility to specify --suppress-cc multiple times, and fixes two bugs. The first bug is that the --suppress-cc option for `cccmd' was misspelled as `ccmd' in the code. The second bug, which is actually found only with my other series, is that the argument to the cccmd is never quoted, so the cccmd would fail with patch file names containing a space. A third bug I fix (in the docs) is that the bodycc argument was actually spelled ccbody in the documentation and bash completion. Signed-off-by: Paolo Bonzini Cc: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 80ab4e45eb..b60cb68a8b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1299,7 +1299,7 @@ _git_rebase () } __git_send_email_confirm_options="always never auto cc compose" -__git_send_email_suppresscc_options="author self cc ccbody sob cccmd body all" +__git_send_email_suppresscc_options="author self cc bodycc sob cccmd body all" _git_send_email () { -- cgit v1.2.3 From 6fac1b83bdb9aee73363f93874ffaffd1bc3ad5c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 29 Jun 2009 21:24:24 -0700 Subject: completion: add missing config variables Update to include branch.*.rebase, remote.*.pushurl, and add.ignore-errors Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b60cb68a8b..ddb71e26fe 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1457,7 +1457,7 @@ _git_config () branch.*.*) local pfx="${cur%.*}." cur="${cur##*.}" - __gitcomp "remote merge mergeoptions" "$pfx" "$cur" + __gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur" return ;; branch.*) @@ -1504,7 +1504,7 @@ _git_config () cur="${cur##*.}" __gitcomp " url proxy fetch push mirror skipDefaultUpdate - receivepack uploadpack tagopt + receivepack uploadpack tagopt pushurl " "$pfx" "$cur" return ;; @@ -1522,6 +1522,7 @@ _git_config () ;; esac __gitcomp " + add.ignore-errors alias. apply.whitespace branch.autosetupmerge -- cgit v1.2.3 From 5d0e634343bf83735b59c611b96c1d0f8ff2b28a Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Wed, 3 Jun 2009 16:20:58 -0400 Subject: completion: Add --full-diff to log options Signed-off-by: Todd Zullinger Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ddb71e26fe..1f44ec2092 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1165,7 +1165,7 @@ _git_log () $__git_log_shortlog_options $__git_log_gitk_options --root --topo-order --date-order --reverse - --follow + --follow --full-diff --abbrev-commit --abbrev= --relative-date --date= --pretty= --format= --oneline -- cgit v1.2.3 From f581de1b7b9d17c83b188bf8ffe536fb8a9dd2a4 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 29 Jun 2009 22:08:38 -0700 Subject: completion: __git_config_get_set_variables() handle values with spaces Commit 0065236 (bash completion: complete variable names for "git config" with options 2009-05-08) implemented its config variable search wrong. When a config contains a value with a space and a period (.) in it, completion erroneously thinks that line in the configuration is multiple config variables. For example $ cat .git/config format.cc = Junio C Hamano $ git config --unset format.cc Instead of using a for loop splitting across spaces, pipe each line to a while read loop and beef up the case statement to match only 'config.variable=value'. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1f44ec2092..9c488646d0 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1357,11 +1357,12 @@ __git_config_get_set_variables () c=$((--c)) done - for i in $(git --git-dir="$(__gitdir)" config $config_file --list \ - 2>/dev/null); do - case "$i" in - *.*) - echo "${i/=*/}" + git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null | + while read line + do + case "$line" in + *.*=*) + echo "${line/=*/}" ;; esac done -- cgit v1.2.3 From 4fe1a61973c82c459ac0a25cb5342d00d347dfd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 13 Jul 2009 17:11:45 +0200 Subject: bash: add '--merges' to common 'git log' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... so it's available for git log, shortlog and gitk. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9c488646d0..887731e830 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1114,7 +1114,7 @@ _git_ls_tree () __git_log_common_options=" --not --all --branches --tags --remotes - --first-parent --no-merges + --first-parent --merges --no-merges --max-count= --max-age= --since= --after= --min-age= --until= --before= -- cgit v1.2.3 From a91f453f641ca9966a438bdd3896656b00423407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kiedrowicz?= Date: Wed, 22 Jul 2009 19:52:15 +0200 Subject: grep: Add --max-depth option. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is useful to grep directories non-recursively, e.g. when one wants to look for all files in the toplevel directory, but not in any subdirectory, or in Documentation/, but not in Documentation/technical/. This patch adds support for --max-depth option to git-grep. If it is given, git-grep descends at most levels of directories below paths specified on the command line. Note that if path specified on command line contains wildcards, this option makes no sense, e.g. $ git grep -l --max-depth 0 GNU -- 'contrib/*' (note the quotes) will search all files in contrib/, even in subdirectories, because '*' matches all files. Documentation updates, bash-completion and simple test cases are also provided. Signed-off-by: Michał Kiedrowicz Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 887731e830..fb05c4884c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1036,6 +1036,7 @@ _git_grep () --extended-regexp --basic-regexp --fixed-strings --files-with-matches --name-only --files-without-match + --max-depth --count --and --or --not --all-match " -- cgit v1.2.3 From 397f7c6371d309aa399bf32b773397f3c068d0c9 Mon Sep 17 00:00:00 2001 From: Daniel Trstenjak Date: Wed, 22 Jul 2009 10:31:34 +0200 Subject: Show the presence of untracked files in the bash prompt. Added the envvar GIT_PS1_SHOWUNTRACKEDFILES to 'git-completion.bash'. When set to a nonempty value, then the char '%' will be shown next to the branch name in the bash prompt. Signed-off-by: Daniel Trstenjak Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 887731e830..745b5fb78b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -44,6 +44,10 @@ # GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed, # then a '$' will be shown next to the branch name. # +# If you would like to see if there're untracked files, then you can +# set GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're +# untracked files, then a '%' will be shown next to the branch name. +# # To submit patches: # # *) Read Documentation/SubmittingPatches @@ -132,6 +136,7 @@ __git_ps1 () local w local i local s + local u local c if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then @@ -156,12 +161,18 @@ __git_ps1 () if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$" fi + + if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then + if [ -n "$(git ls-files --others --exclude-standard)" ]; then + u="%" + fi + fi fi if [ -n "${1-}" ]; then - printf "$1" "$c${b##refs/heads/}$w$i$s$r" + printf "$1" "$c${b##refs/heads/}$w$i$s$u$r" else - printf " (%s)" "$c${b##refs/heads/}$w$i$s$r" + printf " (%s)" "$c${b##refs/heads/}$w$i$s$u$r" fi fi } -- cgit v1.2.3 From b0c051d1a0939ed0013d359065b3afaebe3a717f Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 3 Aug 2009 18:41:34 +0200 Subject: hg-to-git: don't import the unused popen2 module Importing the popen2 module in Python-2.6 results in the "DeprecationWarning: The popen2 module is deprecated. Use the subprocess module." message. The module itself isn't used in fact, so just removing it solves the problem. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 7b03204ed1..2a6839d81e 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -20,7 +20,7 @@ """ import os, os.path, sys -import tempfile, popen2, pickle, getopt +import tempfile, pickle, getopt import re # Maps hg version -> git version -- cgit v1.2.3 From 86c91f91794cd6af8e19fbe68ab283d567d2b66f Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Tue, 4 Aug 2009 13:16:49 +0200 Subject: git apply: option to ignore whitespace differences Introduce --ignore-whitespace option and corresponding config bool to ignore whitespace differences while applying patches, akin to the 'patch' program. 'git am', 'git rebase' and the bash git completion are made aware of this option. Signed-off-by: Giuseppe Bilotta Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 745b5fb78b..dd7ec5dd66 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -674,6 +674,7 @@ _git_am () --*) __gitcomp " --3way --committer-date-is-author-date --ignore-date + --ignore-whitespace --ignore-space-change --interactive --keep --no-utf8 --signoff --utf8 --whitespace= " @@ -695,6 +696,7 @@ _git_apply () --stat --numstat --summary --check --index --cached --index-info --reverse --reject --unidiff-zero --apply --no-add --exclude= + --ignore-whitespace --ignore-space-change --whitespace= --inaccurate-eof --verbose " return @@ -1536,6 +1538,7 @@ _git_config () __gitcomp " add.ignore-errors alias. + apply.ignorewhitespace apply.whitespace branch.autosetupmerge branch.autosetuprebase -- cgit v1.2.3 From 8918f5cf962866c2bb36bdf6b417bfcdb523280a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Fri, 31 Jul 2009 09:23:09 +0200 Subject: git.el: Clarify documentation of git-commit-tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index eace9c18eb..8c70ad8b7f 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -429,16 +429,19 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (git-get-string-sha1 (git-call-process-string-display-error "write-tree")))) -(defun git-commit-tree (buffer tree head) - "Call git-commit-tree with buffer as input and return the resulting commit SHA1." +(defun git-commit-tree (buffer tree parent) + "Create a commit and possibly update HEAD. +Create a commit with the message in BUFFER using the tree with hash TREE. +Use PARENT as the parent of the new commit. If PARENT is the current \"HEAD\", +update the \"HEAD\" reference to the new commit." (let ((author-name (git-get-committer-name)) (author-email (git-get-committer-email)) (subject "commit (initial): ") author-date log-start log-end args coding-system-for-write) - (when head + (when parent (setq subject "commit: ") (push "-p" args) - (push head args)) + (push parent args)) (with-current-buffer buffer (goto-char (point-min)) (if @@ -474,7 +477,7 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (apply #'git-run-command-region buffer log-start log-end env "commit-tree" tree (nreverse args)))))) - (when commit (git-update-ref "HEAD" commit head subject)) + (when commit (git-update-ref "HEAD" commit parent subject)) commit))) (defun git-empty-db-p () -- cgit v1.2.3 From b932705b457ad191481a871292d71088bb23d244 Mon Sep 17 00:00:00 2001 From: Luke Diamand Date: Thu, 30 Jul 2009 00:13:46 +0100 Subject: git-p4: stream from perforce to speed up clones Change commit() to stream data from Perforce and into fast-import rather than reading into memory first, and then writing out. This hugely reduces the memory requirements when cloning non-incrementally. Signed-off-by: Luke Diamand Signed-off-by: Pete Wyckoff Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 169 +++++++++++++++++++++++++++------------------ 1 file changed, 102 insertions(+), 67 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 342529db30..38438f3c4a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -201,7 +201,7 @@ def isModeExec(mode): def isModeExecChanged(src_mode, dst_mode): return isModeExec(src_mode) != isModeExec(dst_mode) -def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): +def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None): cmd = p4_build_cmd("-G %s" % (cmd)) if verbose: sys.stderr.write("Opening pipe: %s\n" % cmd) @@ -224,7 +224,10 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): try: while True: entry = marshal.load(p4.stdout) - result.append(entry) + if cb is not None: + cb(entry) + else: + result.append(entry) except EOFError: pass exitCode = p4.wait() @@ -950,10 +953,84 @@ class P4Sync(Command): return branches - ## Should move this out, doesn't use SELF. - def readP4Files(self, files): + # output one file from the P4 stream + # - helper for streamP4Files + + def streamOneP4File(self, file, contents): + if file["type"] == "apple": + print "\nfile %s is a strange apple file that forks. Ignoring" % \ + file['depotFile'] + return + + relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes) + if verbose: + sys.stderr.write("%s\n" % relPath) + + mode = "644" + if isP4Exec(file["type"]): + mode = "755" + elif file["type"] == "symlink": + mode = "120000" + # p4 print on a symlink contains "target\n", so strip it off + last = contents.pop() + last = last[:-1] + contents.append(last) + + if self.isWindows and file["type"].endswith("text"): + mangled = [] + for data in contents: + data = data.replace("\r\n", "\n") + mangled.append(data) + contents = mangled + + if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): + contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text), contents) + elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): + contents = map(lambda text: re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text), contents) + + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) + + # total length... + length = 0 + for d in contents: + length = length + len(d) + + self.gitStream.write("data %d\n" % length) + for d in contents: + self.gitStream.write(d) + self.gitStream.write("\n") + + def streamOneP4Deletion(self, file): + relPath = self.stripRepoPath(file['path'], self.branchPrefixes) + if verbose: + sys.stderr.write("delete %s\n" % relPath) + self.gitStream.write("D %s\n" % relPath) + + # handle another chunk of streaming data + def streamP4FilesCb(self, marshalled): + + if marshalled.has_key('depotFile') and self.stream_have_file_info: + # start of a new file - output the old one first + self.streamOneP4File(self.stream_file, self.stream_contents) + self.stream_file = {} + self.stream_contents = [] + self.stream_have_file_info = False + + # pick up the new file information... for the + # 'data' field we need to append to our array + for k in marshalled.keys(): + if k == 'data': + self.stream_contents.append(marshalled['data']) + else: + self.stream_file[k] = marshalled[k] + + self.stream_have_file_info = True + + # Stream directly from "p4 files" into "git fast-import" + def streamP4Files(self, files): filesForCommit = [] filesToRead = [] + filesToDelete = [] for f in files: includeFile = True @@ -967,50 +1044,35 @@ class P4Sync(Command): filesForCommit.append(f) if f['action'] not in ('delete', 'purge'): filesToRead.append(f) + else: + filesToDelete.append(f) - filedata = [] - if len(filesToRead) > 0: - filedata = p4CmdList('-x - print', - stdin='\n'.join(['%s#%s' % (f['path'], f['rev']) - for f in filesToRead]), - stdin_mode='w+') - - if "p4ExitCode" in filedata[0]: - die("Problems executing p4. Error: [%d]." - % (filedata[0]['p4ExitCode'])); - - j = 0; - contents = {} - while j < len(filedata): - stat = filedata[j] - j += 1 - text = '' - while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'): - text += filedata[j]['data'] - del filedata[j]['data'] - j += 1 - - if not stat.has_key('depotFile'): - sys.stderr.write("p4 print fails with: %s\n" % repr(stat)) - continue + # deleted files... + for f in filesToDelete: + self.streamOneP4Deletion(f) - if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): - text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text) - elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): - text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text) + if len(filesToRead) > 0: + self.stream_file = {} + self.stream_contents = [] + self.stream_have_file_info = False - contents[stat['depotFile']] = text + # curry self argument + def streamP4FilesCbSelf(entry): + self.streamP4FilesCb(entry) - for f in filesForCommit: - path = f['path'] - if contents.has_key(path): - f['data'] = contents[path] + p4CmdList("-x - print", + '\n'.join(['%s#%s' % (f['path'], f['rev']) + for f in filesToRead]), + cb=streamP4FilesCbSelf) - return filesForCommit + # do the last chunk + if self.stream_file.has_key('depotFile'): + self.streamOneP4File(self.stream_file, self.stream_contents) def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] author = details["user"] + self.branchPrefixes = branchPrefixes if self.verbose: print "commit into %s" % branch @@ -1023,7 +1085,6 @@ class P4Sync(Command): new_files.append (f) else: sys.stderr.write("Ignoring file outside of prefix: %s\n" % path) - files = self.readP4Files(new_files) self.gitStream.write("commit %s\n" % branch) # gitStream.write("mark :%s\n" % details["change"]) @@ -1051,33 +1112,7 @@ class P4Sync(Command): print "parent %s" % parent self.gitStream.write("from %s\n" % parent) - for file in files: - if file["type"] == "apple": - print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path'] - continue - - relPath = self.stripRepoPath(file['path'], branchPrefixes) - if file["action"] in ("delete", "purge"): - self.gitStream.write("D %s\n" % relPath) - else: - data = file['data'] - - mode = "644" - if isP4Exec(file["type"]): - mode = "755" - elif file["type"] == "symlink": - mode = "120000" - # p4 print on a symlink contains "target\n", so strip it off - data = data[:-1] - - if self.isWindows and file["type"].endswith("text"): - data = data.replace("\r\n", "\n") - - self.gitStream.write("M %s inline %s\n" % (mode, relPath)) - self.gitStream.write("data %s\n" % len(data)) - self.gitStream.write(data) - self.gitStream.write("\n") - + self.streamP4Files(new_files) self.gitStream.write("\n") change = int(details["change"]) -- cgit v1.2.3 From 7e787953fb89fbe403060585f7c39f0c75f29318 Mon Sep 17 00:00:00 2001 From: Peter Krefting Date: Thu, 3 Sep 2009 13:15:00 +0100 Subject: import-tars: Allow per-tar author and commit message. If the "--metainfo=" option is given on the command line, a file called "." will be used to create the commit message for "", instead of using "Imported from filename.tar". The author and committer of the tar ball can also be overridden by embedding an "Author:" or "Committer:" header in the metainfo file. Signed-off-by: Peter Krefting Signed-off-by: Junio C Hamano --- contrib/fast-import/import-tars.perl | 50 +++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 78e40d2a13..a909716682 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -8,9 +8,20 @@ ## perl import-tars.perl *.tar.bz2 ## git whatchanged import-tars ## +## Use --metainfo to specify the extension for a meta data file, where +## import-tars can read the commit message and optionally author and +## committer information. +## +## echo 'This is the commit message' > myfile.tar.bz2.msg +## perl import-tars.perl --metainfo=msg myfile.tar.bz2 use strict; -die "usage: import-tars *.tar.{gz,bz2,Z}\n" unless @ARGV; +use Getopt::Long; + +my $metaext = ''; + +die "usage: import-tars [--metainfo=extension] *.tar.{gz,bz2,Z}\n" + unless GetOptions('metainfo=s' => \$metaext) && @ARGV; my $branch_name = 'import-tars'; my $branch_ref = "refs/heads/$branch_name"; @@ -109,12 +120,43 @@ foreach my $tar_file (@ARGV) $have_top_dir = 0 if $top_dir ne $1; } + my $commit_msg = "Imported from $tar_file."; + my $this_committer_name = $committer_name; + my $this_committer_email = $committer_email; + my $this_author_name = $author_name; + my $this_author_email = $author_email; + if ($metaext ne '') { + # Optionally read a commit message from .msg + # Add a line on the form "Committer: name " to override + # the committer and "Author: name " to override the + # author for this tar ball. + if (open MSG, '<', "${tar_file}.${metaext}") { + my $header_done = 0; + $commit_msg = ''; + while () { + if (!$header_done && /^Committer:\s+([^<>]*)\s+<(.*)>\s*$/i) { + $this_committer_name = $1; + $this_committer_email = $2; + } elsif (!$header_done && /^Author:\s+([^<>]*)\s+<(.*)>\s*$/i) { + $this_author_name = $1; + $this_author_email = $2; + } elsif (!$header_done && /^$/ { # empty line ends header. + $header_done = 1; + } else { + $commit_msg .= $_; + $header_done = 1; + } + } + close MSG; + } + } + print FI < $author_time +0000 -committer $committer_name <$committer_email> $commit_time +0000 +author $this_author_name <$this_author_email> $author_time +0000 +committer $this_committer_name <$this_committer_email> $commit_time +0000 data < Date: Thu, 3 Sep 2009 13:15:00 +0100 Subject: Add script for importing bits-and-pieces to Git. Allows the user to import version history that is stored in bits and pieces in the file system, for instance snapshots of old development trees, or day-by-day backups. A configuration file is used to describe the relationship between the different files and allow describing branches and merges, as well as authorship and commit messages. Output is created in a format compatible with git-fast-import. Full documentation is provided inline in perldoc format. Signed-off-by: Peter Krefting Signed-off-by: Junio C Hamano --- contrib/fast-import/import-directories.perl | 416 ++++++++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100755 contrib/fast-import/import-directories.perl (limited to 'contrib') diff --git a/contrib/fast-import/import-directories.perl b/contrib/fast-import/import-directories.perl new file mode 100755 index 0000000000..5782d80e26 --- /dev/null +++ b/contrib/fast-import/import-directories.perl @@ -0,0 +1,416 @@ +#!/usr/bin/perl -w +# +# Copyright 2008-2009 Peter Krefting +# +# ------------------------------------------------------------------------ +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# ------------------------------------------------------------------------ + +=pod + +=head1 NAME + +import-directories - Import bits and pieces to Git. + +=head1 SYNOPSIS + +B F F + +=head1 DESCRIPTION + +Script to import arbitrary projects version controlled by the "copy the +source directory to a new location and edit it there"-version controlled +projects into version control. Handles projects with arbitrary branching +and version trees, taking a file describing the inputs and generating a +file compatible with the L format. + +=head1 CONFIGURATION FILE + +=head2 Format + +The configuration file is based on the standard I<.ini> format. + + ; Comments start with semi-colons + [section] + key=value + +Please see below for information on how to escape special characters. + +=head2 Global configuration + +Global configuration is done in the B<[config]> section, which should be +the first section in the file. Configuration can be changed by +repeating configuration sections later on. + + [config] + ; configure conversion of CRLFs. "convert" means that all CRLFs + ; should be converted into LFs (suitable for the core.autocrlf + ; setting set to true in Git). "none" means that all data is + ; treated as binary. + crlf=convert + +=head2 Revision configuration + +Each revision that is to be imported is described in three +sections. Revisions should be defined in topological order, so +that a revision's parent has always been defined when a new revision +is introduced. All the sections for one revision must be defined +before defining the next revision. + +Each revision is assigned a unique numerical identifier. The +numbers do not need to be consecutive, nor monotonically +increasing. + +For instance, if your configuration file contains only the two +revisions 4711 and 42, where 4711 is the initial commit, the +only requirement is that 4711 is completely defined before 42. + +=pod + +=head3 Revision description section + +A section whose section name is just an integer gives meta-data +about the revision. + + [3] + ; author sets the author of the revisions + author=Peter Krefting + ; branch sets the branch that the revision should be committed to + branch=master + ; parent describes the revision that is the parent of this commit + ; (optional) + parent=1 + ; merges describes a revision that is merged into this commit + ; (optional; can be repeated) + merges=2 + ; selects one file to take the timestamp from + ; (optional; if unspecified, the most recent file from the .files + ; section is used) + timestamp=3/source.c + +=head3 Revision contents section + +A section whose section name is an integer followed by B<.files> +describe all the files included in this revision. If a file that +was available previously is not included in this revision, it will +be removed. + +If an on-disk revision is incomplete, you can point to files from +a previous revision. There are no restriction as to where the source +files are located, nor to the names of them. + + [3.files] + ; the key is the path inside the repository, the value is the path + ; as seen from the importer script. + source.c=ver-3.00/source.c + source.h=ver-2.99/source.h + readme.txt=ver-3.00/introduction to the project.txt + +File names are treated as byte strings (but please see below on +quoting rules), and should be stored in the configuration file in +the encoding that should be used in the generated repository. + +=head3 Revision commit message section + +A section whose section name is an integer followed by B<.message> +gives the commit message. This section is read verbatim, up until +the beginning of the next section. As such, a commit message may not +contain a line that begins with an opening square bracket ("[") and +ends with a closing square bracket ("]"), unless they are surrounded +by whitespace or other characters. + + [3.message] + Implement foobar. + ; trailing blank lines are ignored. + +=cut + +# Globals +use strict; +use integer; +my $crlfmode = 0; +my @revs; +my (%revmap, %message, %files, %author, %branch, %parent, %merges, %time, %timesource); +my $sectiontype = 0; +my $rev = 0; +my $mark = 1; + +# Check command line +if ($#ARGV < 1 || $ARGV[0] =~ /^--?h/) +{ + exec('perldoc', $0); + exit 1; +} + +# Open configuration +my $config = $ARGV[0]; +open CFG, '<', $config or die "Cannot open configuration file \"$config\": "; + +# Open output +my $output = $ARGV[1]; +open OUT, '>', $output or die "Cannot create output file \"$output\": "; +binmode OUT; + +LINE: while (my $line = ) +{ + $line =~ s/\r?\n$//; + next LINE if $sectiontype != 4 && $line eq ''; + next LINE if $line =~ /^;/; + my $oldsectiontype = $sectiontype; + my $oldrev = $rev; + + # Sections + if ($line =~ m"^\[(config|(\d+)(|\.files|\.message))\]$") + { + if ($1 eq 'config') + { + $sectiontype = 1; + } + elsif ($3 eq '') + { + $sectiontype = 2; + $rev = $2; + # Create a new revision + die "Duplicate rev: $line\n " if defined $revmap{$rev}; + print "Reading revision $rev\n"; + push @revs, $rev; + $revmap{$rev} = $mark ++; + $time{$revmap{$rev}} = 0; + } + elsif ($3 eq '.files') + { + $sectiontype = 3; + $rev = $2; + die "Revision mismatch: $line\n " unless $rev == $oldrev; + } + elsif ($3 eq '.message') + { + $sectiontype = 4; + $rev = $2; + die "Revision mismatch: $line\n " unless $rev == $oldrev; + } + else + { + die "Internal parse error: $line\n "; + } + next LINE; + } + + # Parse data + if ($sectiontype != 4) + { + # Key and value + if ($line =~ m"^\s*([^\s].*=.*[^\s])\s*$") + { + my ($key, $value) = &parsekeyvaluepair($1); + # Global configuration + if (1 == $sectiontype) + { + if ($key eq 'crlf') + { + $crlfmode = 1, next LINE if $value eq 'convert'; + $crlfmode = 0, next LINE if $value eq 'none'; + } + die "Unknown configuration option: $line\n "; + } + # Revision specification + if (2 == $sectiontype) + { + my $current = $revmap{$rev}; + $author{$current} = $value, next LINE if $key eq 'author'; + $branch{$current} = $value, next LINE if $key eq 'branch'; + $parent{$current} = $value, next LINE if $key eq 'parent'; + $timesource{$current} = $value, next LINE if $key eq 'timestamp'; + push(@{$merges{$current}}, $value), next LINE if $key eq 'merges'; + die "Unknown revision option: $line\n "; + } + # Filespecs + if (3 == $sectiontype) + { + # Add the file and create a marker + die "File not found: $line\n " unless -f $value; + my $current = $revmap{$rev}; + ${$files{$current}}{$key} = $mark; + my $time = &fileblob($value, $crlfmode, $mark ++); + + # Update revision timestamp if more recent than other + # files seen, or if this is the file we have selected + # to take the time stamp from using the "timestamp" + # directive. + if ((defined $timesource{$current} && $timesource{$current} eq $value) + || $time > $time{$current}) + { + $time{$current} = $time; + } + } + } + else + { + die "Parse error: $line\n "; + } + } + else + { + # Commit message + my $current = $revmap{$rev}; + if (defined $message{$current}) + { + $message{$current} .= "\n"; + } + $message{$current} .= $line; + } +} +close CFG; + +# Start spewing out data for git-fast-import +foreach my $commit (@revs) +{ + # Progress + print OUT "progress Creating revision $commit\n"; + + # Create commit header + my $mark = $revmap{$commit}; + + # Branch and commit id + print OUT "commit refs/heads/", $branch{$mark}, "\nmark :", $mark, "\n"; + + # Author and timestamp + die "No timestamp defined for $commit (no files?)\n" unless defined $time{$mark}; + print OUT "committer ", $author{$mark}, " ", $time{$mark}, " +0100\n"; + + # Commit message + die "No message defined for $commit\n" unless defined $message{$mark}; + my $message = $message{$mark}; + $message =~ s/\n$//; # Kill trailing empty line + print OUT "data ", length($message), "\n", $message, "\n"; + + # Parent and any merges + print OUT "from :", $revmap{$parent{$mark}}, "\n" if defined $parent{$mark}; + if (defined $merges{$mark}) + { + foreach my $merge (@{$merges{$mark}}) + { + print OUT "merge :", $revmap{$merge}, "\n"; + } + } + + # Output file marks + print OUT "deleteall\n"; # start from scratch + foreach my $file (sort keys %{$files{$mark}}) + { + print OUT "M 644 :", ${$files{$mark}}{$file}, " $file\n"; + } + print OUT "\n"; +} + +# Create one file blob +sub fileblob +{ + my ($filename, $crlfmode, $mark) = @_; + + # Import the file + print OUT "progress Importing $filename\nblob\nmark :$mark\n"; + open FILE, '<', $filename or die "Cannot read $filename\n "; + binmode FILE; + my ($size, $mtime) = (stat(FILE))[7,9]; + my $file; + read FILE, $file, $size; + close FILE; + $file =~ s/\r\n/\n/g if $crlfmode; + print OUT "data ", length($file), "\n", $file, "\n"; + + return $mtime; +} + +# Parse a key=value pair +sub parsekeyvaluepair +{ +=pod + +=head2 Escaping special characters + +Key and value strings may be enclosed in quotes, in which case +whitespace inside the quotes is preserved. Additionally, an equal +sign may be included in the key by preceeding it with a backslash. +For example: + + "key1 "=value1 + key2=" value2" + key\=3=value3 + key4=value=4 + "key5""=value5 + +Here the first key is "key1 " (note the trailing white-space) and the +second value is " value2" (note the leading white-space). The third +key contains an equal sign "key=3" and so does the fourth value, which +does not need to be escaped. The fifth key contains a trailing quote, +which does not need to be escaped since it is inside a surrounding +quote. + +=cut + my $pair = shift; + + # Separate key and value by the first non-quoted equal sign + my ($key, $value); + if ($pair =~ /^(.*[^\\])=(.*)$/) + { + ($key, $value) = ($1, $2) + } + else + { + die "Parse error: $pair\n "; + } + + # Unquote and unescape the key and value separately + return (&unescape($key), &unescape($value)); +} + +# Unquote and unescape +sub unescape +{ + my $string = shift; + + # First remove enclosing quotes. Backslash before the trailing + # quote leaves both. + if ($string =~ /^"(.*[^\\])"$/) + { + $string = $1; + } + + # Second remove any backslashes inside the unquoted string. + # For later: Handle special sequences like \t ? + $string =~ s/\\(.)/$1/g; + + return $string; +} + +__END__ + +=pod + +=head1 EXAMPLES + +B F + +=head1 AUTHOR + +Copyright 2008-2009 Peter Krefting Epeter@softwolves.pp.se> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation. + +=cut -- cgit v1.2.3 From 1c2eafb89bcaf2ddbf4dfb93df19673c0fadaaeb Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 7 Sep 2009 01:56:33 -0700 Subject: Add url..pushInsteadOf: URL rewriting for push only This configuration option allows systematically rewriting fetch-only URLs to push-capable URLs when used with push. For instance: [url "ssh://example.org/"] pushInsteadOf = "git://example.org/" This will allow clones of "git://example.org/path/to/repo" to subsequently push to "ssh://example.org/path/to/repo", without manually configuring pushurl for that remote. Includes documentation for the new option, bash completion updates, and test cases (both that pushInsteadOf applies to push, that it does not apply to fetch, and that it is ignored when pushURL is already defined). Signed-off-by: Josh Triplett Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index bf688e12e6..98592040d1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1532,7 +1532,7 @@ _git_config () url.*.*) local pfx="${cur%.*}." cur="${cur##*.}" - __gitcomp "insteadof" "$pfx" "$cur" + __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur" return ;; esac -- cgit v1.2.3 From aaa68dd50f7b4226fdfdfbbe8bbb2dfd9a9e6d66 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Thu, 3 Sep 2009 22:27:24 +0200 Subject: git.el: Use git-add-file for unmerged files, remove git-resolve-file Use `git-add-file' to mark unmerged files as resolved in the *git-status* buffer to be consistent with git's CLI instructions. Also remove `git-resolve-file' to make it clear that that "R" is a now a free keybinding. Signed-off-by: Martin Nordholts Acked-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 8c70ad8b7f..214930a021 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1046,7 +1046,7 @@ The FILES list must be sorted." (defun git-add-file () "Add marked file(s) to the index cache." (interactive) - (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored)))) + (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored 'unmerged)))) ;; FIXME: add support for directories (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) @@ -1119,15 +1119,6 @@ The FILES list must be sorted." (when buffer (with-current-buffer buffer (revert-buffer t t t))))) (git-success-message "Reverted" names)))))) -(defun git-resolve-file () - "Resolve conflicts in marked file(s)." - (interactive) - (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) - (when files - (when (apply 'git-call-process-display-error "update-index" "--" files) - (git-update-status-files files) - (git-success-message "Resolved" files))))) - (defun git-remove-handled () "Remove handled files from the status list." (interactive) @@ -1556,7 +1547,6 @@ amended version of it." (define-key map "P" 'git-prev-unmerged-file) (define-key map "q" 'git-status-quit) (define-key map "r" 'git-remove-file) - (define-key map "R" 'git-resolve-file) (define-key map "t" toggle-map) (define-key map "T" 'git-toggle-all-marks) (define-key map "u" 'git-unmark-file) @@ -1598,7 +1588,6 @@ amended version of it." ("Merge" ["Next Unmerged File" git-next-unmerged-file t] ["Prev Unmerged File" git-prev-unmerged-file t] - ["Mark as Resolved" git-resolve-file t] ["Interactive Merge File" git-find-file-imerge t] ["Diff Against Common Base File" git-diff-file-base t] ["Diff Combined" git-diff-file-combined t] -- cgit v1.2.3 From 1d7367dce7b55cbb1804c4863c56bd20ab12bda8 Mon Sep 17 00:00:00 2001 From: Reilly Grant Date: Thu, 10 Sep 2009 00:02:38 -0700 Subject: git-p4: Avoid modules deprecated in Python 2.6. The popen2, sha and sets modules are deprecated in Python 2.6 (sha in Python 2.5). Both popen2 and sha are not actually used in git-p4. Replace usage of sets.Set with the builtin set object. The built-in set object was added in Python 2.4 and is already used in other parts of this script, so this dependency is nothing new. Signed-off-by: Reilly Grant Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 38438f3c4a..e710219ca5 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -8,12 +8,10 @@ # License: MIT # -import optparse, sys, os, marshal, popen2, subprocess, shelve -import tempfile, getopt, sha, os.path, time, platform +import optparse, sys, os, marshal, subprocess, shelve +import tempfile, getopt, os.path, time, platform import re -from sets import Set; - verbose = False @@ -864,8 +862,8 @@ class P4Sync(Command): self.usage += " //depot/path[@revRange]" self.silent = False - self.createdBranches = Set() - self.committedChanges = Set() + self.createdBranches = set() + self.committedChanges = set() self.branch = "" self.detectBranches = False self.detectLabels = False @@ -1662,7 +1660,7 @@ class P4Sync(Command): if len(self.changesFile) > 0: output = open(self.changesFile).readlines() - changeSet = Set() + changeSet = set() for line in output: changeSet.add(int(line)) -- cgit v1.2.3 From 518ef8f07f72271dff4040c1e7452ab6de73d199 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Fri, 11 Sep 2009 19:23:45 -0400 Subject: completion: Replace config --list with --get-regexp James Bardin noted that the completion spewed warnings when no git config file is present. This is likely a bug to be fixed in git config, but it's also good to simplify the completion code by using the --get-regexp option as Jeff King pointed out. Signed-off-by: Todd Zullinger Trivially-acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 98592040d1..2d2d5794ad 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -318,13 +318,9 @@ __git_remotes () echo ${i#$d/remotes/} done [ "$ngoff" ] && shopt -u nullglob - for i in $(git --git-dir="$d" config --list); do - case "$i" in - remote.*.url=*) - i="${i#remote.}" - echo "${i/.url=*/}" - ;; - esac + for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do + i="${i#remote.}" + echo "${i/.url*/}" done } @@ -605,13 +601,9 @@ __git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)" __git_aliases () { local i IFS=$'\n' - for i in $(git --git-dir="$(__gitdir)" config --list); do - case "$i" in - alias.*) - i="${i#alias.}" - echo "${i/=*/}" - ;; - esac + for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do + i="${i#alias.}" + echo "${i/ */}" done } @@ -1769,13 +1761,9 @@ _git_remote () ;; update) local i c='' IFS=$'\n' - for i in $(git --git-dir="$(__gitdir)" config --list); do - case "$i" in - remotes.*) - i="${i#remotes.}" - c="$c ${i/=*/}" - ;; - esac + for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do + i="${i#remotes.}" + c="$c ${i/ */}" done __gitcomp "$c" ;; -- cgit v1.2.3 From 259d87c354954e8ee3b241dce1393c27186e8ee7 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Wed, 16 Sep 2009 10:20:30 +0200 Subject: Add scripts to generate projects for other buildsystems (MSVC vcproj, QMake) These scripts generate projects for the MSVC IDE (.vcproj files) or QMake (.pro files), based on the output of a 'make -n MSVC=1 V=1' run. This enables us to simply do the necesarry changes in the Makefile, and you can update the other buildsystems by regenerating the files. Keeping the other buildsystems up-to-date with main development. The generator system is designed to easily drop in pm's for other buildsystems as well, if someone has an itch. However, the focus has been Windows development, so the 'engine' might need patches to support any platform. Also add some .gitignore entries for MSVC files. Signed-off-by: Marius Storm-Olsen Acked-by: Johannes Sixt Signed-off-by: Junio C Hamano --- contrib/buildsystems/Generators.pm | 42 ++ contrib/buildsystems/Generators/QMake.pm | 189 +++++++++ contrib/buildsystems/Generators/Vcproj.pm | 639 ++++++++++++++++++++++++++++++ contrib/buildsystems/engine.pl | 353 +++++++++++++++++ contrib/buildsystems/generate | 29 ++ contrib/buildsystems/parse.pl | 228 +++++++++++ 6 files changed, 1480 insertions(+) create mode 100644 contrib/buildsystems/Generators.pm create mode 100644 contrib/buildsystems/Generators/QMake.pm create mode 100644 contrib/buildsystems/Generators/Vcproj.pm create mode 100644 contrib/buildsystems/engine.pl create mode 100644 contrib/buildsystems/generate create mode 100644 contrib/buildsystems/parse.pl (limited to 'contrib') diff --git a/contrib/buildsystems/Generators.pm b/contrib/buildsystems/Generators.pm new file mode 100644 index 0000000000..408ef714b8 --- /dev/null +++ b/contrib/buildsystems/Generators.pm @@ -0,0 +1,42 @@ +package Generators; +require Exporter; + +use strict; +use File::Basename; +no strict 'refs'; +use vars qw($VERSION @AVAILABLE); + +our $VERSION = '1.00'; +our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE); +@ISA = qw(Exporter); + +BEGIN { + local(*D); + my $me = $INC{"Generators.pm"}; + die "Couldn't find myself in \@INC, which is required to load the generators!" if ("$me" eq ""); + $me = dirname($me); + if (opendir(D,"$me/Generators")) { + foreach my $gen (readdir(D)) { + next if ($gen =~ /^\.\.?$/); + require "${me}/Generators/$gen"; + $gen =~ s,\.pm,,; + push(@AVAILABLE, $gen); + } + closedir(D); + my $gens = join(', ', @AVAILABLE); + } + + push @EXPORT_OK, qw(available); +} + +sub available { + return @AVAILABLE; +} + +sub generate { + my ($gen, $git_dir, $out_dir, $rel_dir, %build_structure) = @_; + return eval("Generators::${gen}::generate(\$git_dir, \$out_dir, \$rel_dir, \%build_structure)") if grep(/^$gen$/, @AVAILABLE); + die "Generator \"${gen}\" is not available!\nAvailable generators are: @AVAILABLE\n"; +} + +1; diff --git a/contrib/buildsystems/Generators/QMake.pm b/contrib/buildsystems/Generators/QMake.pm new file mode 100644 index 0000000000..ff3b657e61 --- /dev/null +++ b/contrib/buildsystems/Generators/QMake.pm @@ -0,0 +1,189 @@ +package Generators::QMake; +require Exporter; + +use strict; +use vars qw($VERSION); + +our $VERSION = '1.00'; +our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE); +@ISA = qw(Exporter); + +BEGIN { + push @EXPORT_OK, qw(generate); +} + +sub generate { + my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_; + + my @libs = @{$build_structure{"LIBS"}}; + foreach (@libs) { + createLibProject($_, $git_dir, $out_dir, $rel_dir, %build_structure); + } + + my @apps = @{$build_structure{"APPS"}}; + foreach (@apps) { + createAppProject($_, $git_dir, $out_dir, $rel_dir, %build_structure); + } + + createGlueProject($git_dir, $out_dir, $rel_dir, %build_structure); + return 0; +} + +sub createLibProject { + my ($libname, $git_dir, $out_dir, $rel_dir, %build_structure) = @_; + print "Generate $libname lib project\n"; + $rel_dir = "../$rel_dir"; + + my $sources = join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"LIBS_${libname}_SOURCES"}}))); + my $defines = join(" \\\n\t", sort(@{$build_structure{"LIBS_${libname}_DEFINES"}})); + my $includes= join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"LIBS_${libname}_INCLUDES"}}))); + my $cflags = join(" ", sort(@{$build_structure{"LIBS_${libname}_CFLAGS"}})); + + my $cflags_debug = $cflags; + $cflags_debug =~ s/-MT/-MTd/; + $cflags_debug =~ s/-O.//; + + my $cflags_release = $cflags; + $cflags_release =~ s/-MTd/-MT/; + + my @tmp = @{$build_structure{"LIBS_${libname}_LFLAGS"}}; + my @tmp2 = (); + foreach (@tmp) { + if (/^-LTCG/) { + } elsif (/^-L/) { + $_ =~ s/^-L/-LIBPATH:$rel_dir\//; + } + push(@tmp2, $_); + } + my $lflags = join(" ", sort(@tmp)); + + my $target = $libname; + $target =~ s/\//_/g; + $defines =~ s/-D//g; + $defines =~ s/"/\\\\"/g; + $includes =~ s/-I//g; + mkdir "$target" || die "Could not create the directory $target for lib project!\n"; + open F, ">$target/$target.pro" || die "Could not open $target/$target.pro for writing!\n"; + print F << "EOM"; +TEMPLATE = lib +TARGET = $target +DESTDIR = $rel_dir + +CONFIG -= qt +CONFIG += static + +QMAKE_CFLAGS = +QMAKE_CFLAGS_RELEASE = $cflags_release +QMAKE_CFLAGS_DEBUG = $cflags_debug +QMAKE_LIBFLAGS = $lflags + +DEFINES += \\ + $defines + +INCLUDEPATH += \\ + $includes + +SOURCES += \\ + $sources +EOM + close F; +} + +sub createAppProject { + my ($appname, $git_dir, $out_dir, $rel_dir, %build_structure) = @_; + print "Generate $appname app project\n"; + $rel_dir = "../$rel_dir"; + + my $sources = join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"APPS_${appname}_SOURCES"}}))); + my $defines = join(" \\\n\t", sort(@{$build_structure{"APPS_${appname}_DEFINES"}})); + my $includes= join(" \\\n\t", sort(map("$rel_dir/$_", @{$build_structure{"APPS_${appname}_INCLUDES"}}))); + my $cflags = join(" ", sort(@{$build_structure{"APPS_${appname}_CFLAGS"}})); + + my $cflags_debug = $cflags; + $cflags_debug =~ s/-MT/-MTd/; + $cflags_debug =~ s/-O.//; + + my $cflags_release = $cflags; + $cflags_release =~ s/-MTd/-MT/; + + my $libs; + foreach (sort(@{$build_structure{"APPS_${appname}_LIBS"}})) { + $_ =~ s/\//_/g; + $libs .= " $_"; + } + my @tmp = @{$build_structure{"APPS_${appname}_LFLAGS"}}; + my @tmp2 = (); + foreach (@tmp) { + # next if ($_ eq "-NODEFAULTLIB:MSVCRT.lib"); + if (/^-LTCG/) { + } elsif (/^-L/) { + $_ =~ s/^-L/-LIBPATH:$rel_dir\//; + } + push(@tmp2, $_); + } + my $lflags = join(" ", sort(@tmp)); + + my $target = $appname; + $target =~ s/\.exe//; + $target =~ s/\//_/g; + $defines =~ s/-D//g; + $defines =~ s/"/\\\\"/g; + $includes =~ s/-I//g; + mkdir "$target" || die "Could not create the directory $target for app project!\n"; + open F, ">$target/$target.pro" || die "Could not open $target/$target.pro for writing!\n"; + print F << "EOM"; +TEMPLATE = app +TARGET = $target +DESTDIR = $rel_dir + +CONFIG -= qt embed_manifest_exe +CONFIG += console + +QMAKE_CFLAGS = +QMAKE_CFLAGS_RELEASE = $cflags_release +QMAKE_CFLAGS_DEBUG = $cflags_debug +QMAKE_LFLAGS = $lflags +LIBS = $libs + +DEFINES += \\ + $defines + +INCLUDEPATH += \\ + $includes + +win32:QMAKE_LFLAGS += -LIBPATH:$rel_dir +else: QMAKE_LFLAGS += -L$rel_dir + +SOURCES += \\ + $sources +EOM + close F; +} + +sub createGlueProject { + my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_; + my $libs = join(" \\ \n", map("\t$_|$_.pro", @{$build_structure{"LIBS"}})); + my $apps = join(" \\ \n", map("\t$_|$_.pro", @{$build_structure{"APPS"}})); + $libs =~ s/\.a//g; + $libs =~ s/\//_/g; + $libs =~ s/\|/\//g; + $apps =~ s/\.exe//g; + $apps =~ s/\//_/g; + $apps =~ s/\|/\//g; + + my $filename = $out_dir; + $filename =~ s/.*\/([^\/]+)$/$1/; + $filename =~ s/\/$//; + print "Generate glue project $filename.pro\n"; + open F, ">$filename.pro" || die "Could not open $filename.pro for writing!\n"; + print F << "EOM"; +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS += \\ +$libs \\ +$apps +EOM + close F; +} + +1; diff --git a/contrib/buildsystems/Generators/Vcproj.pm b/contrib/buildsystems/Generators/Vcproj.pm new file mode 100644 index 0000000000..00ec0c1369 --- /dev/null +++ b/contrib/buildsystems/Generators/Vcproj.pm @@ -0,0 +1,639 @@ +package Generators::Vcproj; +require Exporter; + +use strict; +use vars qw($VERSION); + +our $VERSION = '1.00'; +our(@ISA, @EXPORT, @EXPORT_OK, @AVAILABLE); +@ISA = qw(Exporter); + +BEGIN { + push @EXPORT_OK, qw(generate); +} + +my $guid_index = 0; +my @GUIDS = ( + "{E07B9989-2BF7-4F21-8918-BE22BA467AC3}", + "{278FFB51-0296-4A44-A81A-22B87B7C3592}", + "{7346A2C4-F0FD-444F-9EBE-1AF23B2B5650}", + "{67F421AC-EB34-4D49-820B-3196807B423F}", + "{385DCFE1-CC8C-4211-A451-80FCFC31CA51}", + "{97CC46C5-D2CC-4D26-B634-E75792B79916}", + "{C7CE21FE-6EF8-4012-A5C7-A22BCEDFBA11}", + "{51575134-3FDF-42D1-BABD-3FB12669C6C9}", + "{0AE195E4-9823-4B87-8E6F-20C5614AF2FF}", + "{4B918255-67CA-43BB-A46C-26704B666E6B}", + "{18CCFEEF-C8EE-4CC1-A265-26F95C9F4649}", + "{5D5D90FA-01B7-4973-AFE5-CA88C53AC197}", + "{1F054320-036D-49E1-B384-FB5DF0BC8AC0}", + "{7CED65EE-F2D9-4171-825B-C7D561FE5786}", + "{8D341679-0F07-4664-9A56-3BA0DE88B9BC}", + "{C189FEDC-2957-4BD7-9FA4-7622241EA145}", + "{66844203-1B9F-4C53-9274-164FFF95B847}", + "{E4FEA145-DECC-440D-AEEA-598CF381FD43}", + "{73300A8E-C8AC-41B0-B555-4F596B681BA7}", + "{873FDEB1-D01D-40BF-A1BF-8BBC58EC0F51}", + "{7922C8BE-76C5-4AC6-8BF7-885C0F93B782}", + "{E245D370-308B-4A49-BFC1-1E527827975F}", + "{F6FA957B-66FC-4ED7-B260-E59BBE4FE813}", + "{E6055070-0198-431A-BC49-8DB6CEE770AE}", + "{54159234-C3EB-43DA-906B-CE5DA5C74654}", + "{594CFC35-0B60-46F6-B8EF-9983ACC1187D}", + "{D93FCAB7-1F01-48D2-B832-F761B83231A5}", + "{DBA5E6AC-E7BE-42D3-8703-4E787141526E}", + "{6171953F-DD26-44C7-A3BE-CC45F86FC11F}", + "{9E19DDBE-F5E4-4A26-A2FE-0616E04879B8}", + "{AE81A615-99E3-4885-9CE0-D9CAA193E867}", + "{FBF4067E-1855-4F6C-8BCD-4D62E801A04D}", + "{17007948-6593-4AEB-8106-F7884B4F2C19}", + "{199D4C8D-8639-4DA6-82EF-08668C35DEE0}", + "{E085E50E-C140-4CF3-BE4B-094B14F0DDD6}", + "{00785268-A9CC-4E40-AC29-BAC0019159CE}", + "{4C06F56A-DCDB-46A6-B67C-02339935CF12}", + "{3A62D3FD-519E-4EC9-8171-D2C1BFEA022F}", + "{3A62D3FD-519E-4EC9-8171-D2C1BFEA022F}", + "{9392EB58-D7BA-410B-B1F0-B2FAA6BC89A7}", + "{2ACAB2D5-E0CE-4027-BCA0-D78B2D7A6C66}", + "{86E216C3-43CE-481A-BCB2-BE5E62850635}", + "{FB631291-7923-4B91-9A57-7B18FDBB7A42}", + "{0A176EC9-E934-45B8-B87F-16C7F4C80039}", + "{DF55CA80-46E8-4C53-B65B-4990A23DD444}", + "{3A0F9895-55D2-4710-BE5E-AD7498B5BF44}", + "{294BDC5A-F448-48B6-8110-DD0A81820F8C}", + "{4B9F66E9-FAC9-47AB-B1EF-C16756FBFD06}", + "{72EA49C6-2806-48BD-B81B-D4905102E19C}", + "{5728EB7E-8929-486C-8CD5-3238D060E768}" +); + +sub generate { + my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_; + my @libs = @{$build_structure{"LIBS"}}; + foreach (@libs) { + createLibProject($_, $git_dir, $out_dir, $rel_dir, \%build_structure); + } + + my @apps = @{$build_structure{"APPS"}}; + foreach (@apps) { + createAppProject($_, $git_dir, $out_dir, $rel_dir, \%build_structure); + } + + createGlueProject($git_dir, $out_dir, $rel_dir, %build_structure); + return 0; +} + +sub createLibProject { + my ($libname, $git_dir, $out_dir, $rel_dir, $build_structure) = @_; + print "Generate $libname vcproj lib project\n"; + $rel_dir = "..\\$rel_dir"; + $rel_dir =~ s/\//\\/g; + + my $target = $libname; + $target =~ s/\//_/g; + $target =~ s/\.a//; + + my $uuid = $GUIDS[$guid_index]; + $$build_structure{"LIBS_${target}_GUID"} = $uuid; + $guid_index += 1; + + my @srcs = sort(map("$rel_dir\\$_", @{$$build_structure{"LIBS_${libname}_SOURCES"}})); + my @sources; + foreach (@srcs) { + $_ =~ s/\//\\/g; + push(@sources, $_); + } + my $defines = join(",", sort(@{$$build_structure{"LIBS_${libname}_DEFINES"}})); + my $includes= join(";", sort(map(""$rel_dir\\$_"", @{$$build_structure{"LIBS_${libname}_INCLUDES"}}))); + my $cflags = join(" ", sort(@{$$build_structure{"LIBS_${libname}_CFLAGS"}})); + $cflags =~ s/\"/"/g; + + my $cflags_debug = $cflags; + $cflags_debug =~ s/-MT/-MTd/; + $cflags_debug =~ s/-O.//; + + my $cflags_release = $cflags; + $cflags_release =~ s/-MTd/-MT/; + + my @tmp = @{$$build_structure{"LIBS_${libname}_LFLAGS"}}; + my @tmp2 = (); + foreach (@tmp) { + if (/^-LTCG/) { + } elsif (/^-L/) { + $_ =~ s/^-L/-LIBPATH:$rel_dir\//; + } + push(@tmp2, $_); + } + my $lflags = join(" ", sort(@tmp)); + + $defines =~ s/-D//g; + $defines =~ s/\"/\\"/g; + $defines =~ s/\'//g; + $includes =~ s/-I//g; + mkdir "$target" || die "Could not create the directory $target for lib project!\n"; + open F, ">$target/$target.vcproj" || die "Could not open $target/$target.pro for writing!\n"; + print F << "EOM"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +EOM + foreach(@sources) { + print F << "EOM"; + +EOM + } + print F << "EOM"; + + + + + +EOM + close F; +} + +sub createAppProject { + my ($appname, $git_dir, $out_dir, $rel_dir, $build_structure) = @_; + print "Generate $appname vcproj app project\n"; + $rel_dir = "..\\$rel_dir"; + $rel_dir =~ s/\//\\/g; + + my $target = $appname; + $target =~ s/\//_/g; + $target =~ s/\.exe//; + + my $uuid = $GUIDS[$guid_index]; + $$build_structure{"APPS_${target}_GUID"} = $uuid; + $guid_index += 1; + + my @srcs = sort(map("$rel_dir\\$_", @{$$build_structure{"APPS_${appname}_SOURCES"}})); + my @sources; + foreach (@srcs) { + $_ =~ s/\//\\/g; + push(@sources, $_); + } + my $defines = join(",", sort(@{$$build_structure{"APPS_${appname}_DEFINES"}})); + my $includes= join(";", sort(map(""$rel_dir\\$_"", @{$$build_structure{"APPS_${appname}_INCLUDES"}}))); + my $cflags = join(" ", sort(@{$$build_structure{"APPS_${appname}_CFLAGS"}})); + $cflags =~ s/\"/"/g; + + my $cflags_debug = $cflags; + $cflags_debug =~ s/-MT/-MTd/; + $cflags_debug =~ s/-O.//; + + my $cflags_release = $cflags; + $cflags_release =~ s/-MTd/-MT/; + + my $libs; + foreach (sort(@{$$build_structure{"APPS_${appname}_LIBS"}})) { + $_ =~ s/\//_/g; + $libs .= " $_"; + } + my @tmp = @{$$build_structure{"APPS_${appname}_LFLAGS"}}; + my @tmp2 = (); + foreach (@tmp) { + if (/^-LTCG/) { + } elsif (/^-L/) { + $_ =~ s/^-L/-LIBPATH:$rel_dir\//; + } + push(@tmp2, $_); + } + my $lflags = join(" ", sort(@tmp)) . " -LIBPATH:$rel_dir"; + + $defines =~ s/-D//g; + $defines =~ s/\"/\\"/g; + $defines =~ s/\'//g; + $defines =~ s/\\\\/\\/g; + $includes =~ s/-I//g; + mkdir "$target" || die "Could not create the directory $target for lib project!\n"; + open F, ">$target/$target.vcproj" || die "Could not open $target/$target.pro for writing!\n"; + print F << "EOM"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +EOM + foreach(@sources) { + print F << "EOM"; + +EOM + } + print F << "EOM"; + + + + + +EOM + close F; +} + +sub createGlueProject { + my ($git_dir, $out_dir, $rel_dir, %build_structure) = @_; + print "Generate solutions file\n"; + $rel_dir = "..\\$rel_dir"; + $rel_dir =~ s/\//\\/g; + my $SLN_HEAD = "Microsoft Visual Studio Solution File, Format Version 10.00\n"; + my $SLN_PRE = "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = "; + my $SLN_POST = "\nEndProject\n"; + + my @libs = @{$build_structure{"LIBS"}}; + my @tmp; + foreach (@libs) { + $_ =~ s/\//_/g; + $_ =~ s/\.a//; + push(@tmp, $_); + } + @libs = @tmp; + + my @apps = @{$build_structure{"APPS"}}; + @tmp = (); + foreach (@apps) { + $_ =~ s/\//_/g; + $_ =~ s/\.exe//; + push(@tmp, $_); + } + @apps = @tmp; + + open F, ">git.sln" || die "Could not open git.sln for writing!\n"; + print F "$SLN_HEAD"; + foreach (@libs) { + my $libname = $_; + my $uuid = $build_structure{"LIBS_${libname}_GUID"}; + print F "$SLN_PRE"; + print F "\"${libname}\", \"${libname}\\${libname}.vcproj\", \"${uuid}\""; + print F "$SLN_POST"; + } + foreach (@apps) { + my $appname = $_; + my $uuid = $build_structure{"APPS_${appname}_GUID"}; + print F "$SLN_PRE"; + print F "\"${appname}\", \"${appname}\\${appname}.vcproj\", \"${uuid}\""; + print F "$SLN_POST"; + } + + print F << "EOM"; +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Debug|Win32 + ConfigName.1 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution +EOM + foreach (@{$build_structure{"APPS"}}) { + my $appname = $_; + my $appname_clean = $_; + $appname_clean =~ s/\//_/g; + $appname_clean =~ s/\.exe//; + + my $uuid = $build_structure{"APPS_${appname_clean}_GUID"}; + my $dep_index = 0; + foreach(@{$build_structure{"APPS_${appname}_LIBS"}}) { + my $libname = $_; + $libname =~ s/\//_/g; + $libname =~ s/\.(a|lib)//; + my $libuuid = $build_structure{"LIBS_${libname}_GUID"}; + if (defined $libuuid) { + print F "\t\t${uuid}.${dep_index} = ${libuuid}\n"; + $dep_index += 1; + } + } + } + + print F << "EOM"; + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution +EOM + foreach (@libs) { + my $libname = $_; + my $uuid = $build_structure{"LIBS_${libname}_GUID"}; + print F "\t\t${uuid}.Debug|Win32.ActiveCfg = Debug|Win32\n"; + print F "\t\t${uuid}.Debug|Win32.Build.0 = Debug|Win32\n"; + print F "\t\t${uuid}.Release|Win32.ActiveCfg = Release|Win32\n"; + print F "\t\t${uuid}.Release|Win32.Build.0 = Release|Win32\n"; + } + foreach (@apps) { + my $appname = $_; + my $uuid = $build_structure{"APPS_${appname}_GUID"}; + print F "\t\t${uuid}.Debug|Win32.ActiveCfg = Debug|Win32\n"; + print F "\t\t${uuid}.Debug|Win32.Build.0 = Debug|Win32\n"; + print F "\t\t${uuid}.Release|Win32.ActiveCfg = Release|Win32\n"; + print F "\t\t${uuid}.Release|Win32.Build.0 = Release|Win32\n"; + } + + print F << "EOM"; + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal +EOM + close F; +} + +1; diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl new file mode 100644 index 0000000000..20bd061b3e --- /dev/null +++ b/contrib/buildsystems/engine.pl @@ -0,0 +1,353 @@ +#!/usr/bin/perl -w +###################################################################### +# Do not call this script directly! +# +# The generate script ensures that @INC is correct before the engine +# is executed. +# +# Copyright (C) 2009 Marius Storm-Olsen +###################################################################### +use strict; +use File::Basename; +use File::Spec; +use Cwd; +use Generators; + +my (%build_structure, %compile_options, @makedry); +my $out_dir = getcwd(); +my $git_dir = $out_dir; +$git_dir =~ s=\\=/=g; +$git_dir = dirname($git_dir) while (!-e "$git_dir/git.c" && "$git_dir" ne ""); +die "Couldn't find Git repo" if ("$git_dir" eq ""); + +my @gens = Generators::available(); +my $gen = "Vcproj"; + +sub showUsage +{ + my $genlist = join(', ', @gens); + print << "EOM"; +generate usage: + -g --gen Specify the buildsystem generator (default: $gen) + Available: $genlist + -o --out Specify output directory generation (default: .) + -i --in Specify input file, instead of running GNU Make + -h,-? --help This help +EOM + exit 0; +} + +# Parse command-line options +while (@ARGV) { + my $arg = shift @ARGV; + if ("$arg" eq "-h" || "$arg" eq "--help" || "$arg" eq "-?") { + showUsage(); + exit(0); + } elsif("$arg" eq "--out" || "$arg" eq "-o") { + $out_dir = shift @ARGV; + } elsif("$arg" eq "--gen" || "$arg" eq "-g") { + $gen = shift @ARGV; + } elsif("$arg" eq "--in" || "$arg" eq "-i") { + my $infile = shift @ARGV; + open(F, "<$infile") || die "Couldn't open file $infile"; + @makedry = ; + close(F); + } +} + +# NOT using File::Spec->rel2abs($path, $base) here, as +# it fails badly for me in the msysgit environment +$git_dir = File::Spec->rel2abs($git_dir); +$out_dir = File::Spec->rel2abs($out_dir); +my $rel_dir = makeOutRel2Git($git_dir, $out_dir); + +# Print some information so the user feels informed +print << "EOM"; +----- +Generator: $gen +Git dir: $git_dir +Out dir: $out_dir +----- +Running GNU Make to figure out build structure... +EOM + +# Pipe a make --dry-run into a variable, if not already loaded from file +@makedry = `cd $git_dir && make -n MSVC=1 V=1 2>/dev/null` if !@makedry; + +# Parse the make output into usable info +parseMakeOutput(); + +# Finally, ask the generator to start generating.. +Generators::generate($gen, $git_dir, $out_dir, $rel_dir, %build_structure); + +# main flow ends here +# ------------------------------------------------------------------------------------------------- + + +# 1) path: /foo/bar/baz 2) path: /foo/bar/baz 3) path: /foo/bar/baz +# base: /foo/bar/baz/temp base: /foo/bar base: /tmp +# rel: .. rel: baz rel: ../foo/bar/baz +sub makeOutRel2Git +{ + my ($path, $base) = @_; + my $rel; + if ("$path" eq "$base") { + return "."; + } elsif ($base =~ /^$path/) { + # case 1 + my $tmp = $base; + $tmp =~ s/^$path//; + foreach (split('/', $tmp)) { + $rel .= "../" if ("$_" ne ""); + } + } elsif ($path =~ /^$base/) { + # case 2 + $rel = $path; + $rel =~ s/^$base//; + $rel = "./$rel"; + } else { + my $tmp = $base; + foreach (split('/', $tmp)) { + $rel .= "../" if ("$_" ne ""); + } + $rel .= $path; + } + $rel =~ s/\/\//\//g; # simplify + $rel =~ s/\/$//; # don't end with / + return $rel; +} + +sub parseMakeOutput +{ + print "Parsing GNU Make output to figure out build structure...\n"; + my $line = 0; + while (my $text = shift @makedry) { + my $ate_next; + do { + $ate_next = 0; + $line++; + chomp $text; + chop $text if ($text =~ /\r$/); + if ($text =~ /\\$/) { + $text =~ s/\\$//; + $text .= shift @makedry; + $ate_next = 1; + } + } while($ate_next); + + if($text =~ / -c /) { + # compilation + handleCompileLine($text, $line); + + } elsif ($text =~ / -o /) { + # linking executable + handleLinkLine($text, $line); + + } elsif ($text =~ /\.o / && $text =~ /\.a /) { + # libifying + handleLibLine($text, $line); +# +# } elsif ($text =~ /^cp /) { +# # copy file around +# +# } elsif ($text =~ /^rm -f /) { +# # shell command +# +# } elsif ($text =~ /^make[ \[]/) { +# # make output +# +# } elsif ($text =~ /^echo /) { +# # echo to file +# +# } elsif ($text =~ /^if /) { +# # shell conditional +# +# } elsif ($text =~ /^tclsh /) { +# # translation stuff +# +# } elsif ($text =~ /^umask /) { +# # handling boilerplates +# +# } elsif ($text =~ /\$\(\:\)/) { +# # ignore +# +# } elsif ($text =~ /^FLAGS=/) { +# # flags check for dependencies +# +# } elsif ($text =~ /^'\/usr\/bin\/perl' -MError -e/) { +# # perl commands for copying files +# +# } elsif ($text =~ /generate-cmdlist\.sh/) { +# # command for generating list of commands +# +# } elsif ($text =~ /^test / && $text =~ /|| rm -f /) { +# # commands removing executables, if they exist +# +# } elsif ($text =~ /new locations or Tcl/) { +# # command for detecting Tcl/Tk changes +# +# } elsif ($text =~ /mkdir -p/) { +# # command creating path +# +# } elsif ($text =~ /: no custom templates yet/) { +# # whatever +# +# } else { +# print "Unhandled (line: $line): $text\n"; + } + } + +# use Data::Dumper; +# print "Parsed build structure:\n"; +# print Dumper(%build_structure); +} + +# variables for the compilation part of each step +my (@defines, @incpaths, @cflags, @sources); + +sub clearCompileStep +{ + @defines = (); + @incpaths = (); + @cflags = (); + @sources = (); +} + +sub removeDuplicates +{ + my (%dupHash, $entry); + %dupHash = map { $_, 1 } @defines; + @defines = keys %dupHash; + + %dupHash = map { $_, 1 } @incpaths; + @incpaths = keys %dupHash; + + %dupHash = map { $_, 1 } @cflags; + @cflags = keys %dupHash; +} + +sub handleCompileLine +{ + my ($line, $lineno) = @_; + my @parts = split(' ', $line); + my $sourcefile; + shift(@parts); # ignore cmd + while (my $part = shift @parts) { + if ("$part" eq "-o") { + # ignore object file + shift @parts; + } elsif ("$part" eq "-c") { + # ignore compile flag + } elsif ("$part" eq "-c") { + } elsif ($part =~ /^.?-I/) { + push(@incpaths, $part); + } elsif ($part =~ /^.?-D/) { + push(@defines, $part); + } elsif ($part =~ /^-/) { + push(@cflags, $part); + } elsif ($part =~ /\.(c|cc|cpp)$/) { + $sourcefile = $part; + } else { + die "Unhandled compiler option @ line $lineno: $part"; + } + } + @{$compile_options{"${sourcefile}_CFLAGS"}} = @cflags; + @{$compile_options{"${sourcefile}_DEFINES"}} = @defines; + @{$compile_options{"${sourcefile}_INCPATHS"}} = @incpaths; + clearCompileStep(); +} + +sub handleLibLine +{ + my ($line, $lineno) = @_; + my (@objfiles, @lflags, $libout, $part); + # kill cmd and rm 'prefix' + $line =~ s/^rm -f .* && .* rcs //; + my @parts = split(' ', $line); + while ($part = shift @parts) { + if ($part =~ /^-/) { + push(@lflags, $part); + } elsif ($part =~ /\.(o|obj)$/) { + push(@objfiles, $part); + } elsif ($part =~ /\.(a|lib)$/) { + $libout = $part; + $libout =~ s/\.a$//; + } else { + die "Unhandled lib option @ line $lineno: $part"; + } + } +# print "LibOut: '$libout'\nLFlags: @lflags\nOfiles: @objfiles\n"; +# exit(1); + foreach (@objfiles) { + my $sourcefile = $_; + $sourcefile =~ s/\.o/.c/; + push(@sources, $sourcefile); + push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}}); + push(@defines, @{$compile_options{"${sourcefile}_DEFINES"}}); + push(@incpaths, @{$compile_options{"${sourcefile}_INCPATHS"}}); + } + removeDuplicates(); + + push(@{$build_structure{"LIBS"}}, $libout); + @{$build_structure{"LIBS_${libout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_SOURCES", + "_OBJECTS"); + @{$build_structure{"LIBS_${libout}_DEFINES"}} = @defines; + @{$build_structure{"LIBS_${libout}_INCLUDES"}} = @incpaths; + @{$build_structure{"LIBS_${libout}_CFLAGS"}} = @cflags; + @{$build_structure{"LIBS_${libout}_LFLAGS"}} = @lflags; + @{$build_structure{"LIBS_${libout}_SOURCES"}} = @sources; + @{$build_structure{"LIBS_${libout}_OBJECTS"}} = @objfiles; + clearCompileStep(); +} + +sub handleLinkLine +{ + my ($line, $lineno) = @_; + my (@objfiles, @lflags, @libs, $appout, $part); + my @parts = split(' ', $line); + shift(@parts); # ignore cmd + while ($part = shift @parts) { + if ($part =~ /^-IGNORE/) { + push(@lflags, $part); + } elsif ($part =~ /^-[GRIMDO]/) { + # eat compiler flags + } elsif ("$part" eq "-o") { + $appout = shift @parts; + } elsif ("$part" eq "-lz") { + push(@libs, "zlib.lib"); + } elsif ($part =~ /^-/) { + push(@lflags, $part); + } elsif ($part =~ /\.(a|lib)$/) { + $part =~ s/\.a$/.lib/; + push(@libs, $part); + } elsif ($part =~ /\.(o|obj)$/) { + push(@objfiles, $part); + } else { + die "Unhandled lib option @ line $lineno: $part"; + } + } +# print "AppOut: '$appout'\nLFlags: @lflags\nLibs : @libs\nOfiles: @objfiles\n"; +# exit(1); + foreach (@objfiles) { + my $sourcefile = $_; + $sourcefile =~ s/\.o/.c/; + push(@sources, $sourcefile); + push(@cflags, @{$compile_options{"${sourcefile}_CFLAGS"}}); + push(@defines, @{$compile_options{"${sourcefile}_DEFINES"}}); + push(@incpaths, @{$compile_options{"${sourcefile}_INCPATHS"}}); + } + removeDuplicates(); + + removeDuplicates(); + push(@{$build_structure{"APPS"}}, $appout); + @{$build_structure{"APPS_${appout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_LFLAGS", + "_SOURCES", "_OBJECTS", "_LIBS"); + @{$build_structure{"APPS_${appout}_DEFINES"}} = @defines; + @{$build_structure{"APPS_${appout}_INCLUDES"}} = @incpaths; + @{$build_structure{"APPS_${appout}_CFLAGS"}} = @cflags; + @{$build_structure{"APPS_${appout}_LFLAGS"}} = @lflags; + @{$build_structure{"APPS_${appout}_SOURCES"}} = @sources; + @{$build_structure{"APPS_${appout}_OBJECTS"}} = @objfiles; + @{$build_structure{"APPS_${appout}_LIBS"}} = @libs; + clearCompileStep(); +} diff --git a/contrib/buildsystems/generate b/contrib/buildsystems/generate new file mode 100644 index 0000000000..bc10f25ff2 --- /dev/null +++ b/contrib/buildsystems/generate @@ -0,0 +1,29 @@ +#!/usr/bin/perl -w +###################################################################### +# Generate buildsystem files +# +# This script generate buildsystem files based on the output of a +# GNU Make --dry-run, enabling Windows users to develop Git with their +# trusted IDE with native projects. +# +# Note: +# It is not meant as *the* way of building Git with MSVC, but merely a +# convenience. The correct way of building Git with MSVC is to use the +# GNU Make tool to build with the maintained Makefile in the root of +# the project. If you have the msysgit environment installed and +# available in your current console, together with the Visual Studio +# environment you wish to build for, all you have to do is run the +# command: +# make MSVC=1 +# +# Copyright (C) 2009 Marius Storm-Olsen +###################################################################### +use strict; +use File::Basename; +use Cwd; + +my $git_dir = getcwd(); +$git_dir =~ s=\\=/=g; +$git_dir = dirname($git_dir) while (!-e "$git_dir/git.c" && "$git_dir" ne ""); +die "Couldn't find Git repo" if ("$git_dir" eq ""); +exec join(" ", ("PERL5LIB=${git_dir}/contrib/buildsystems ${git_dir}/contrib/buildsystems/engine.pl", @ARGV)); diff --git a/contrib/buildsystems/parse.pl b/contrib/buildsystems/parse.pl new file mode 100644 index 0000000000..c9656ece99 --- /dev/null +++ b/contrib/buildsystems/parse.pl @@ -0,0 +1,228 @@ +#!/usr/bin/perl -w +###################################################################### +# Do not call this script directly! +# +# The generate script ensures that @INC is correct before the engine +# is executed. +# +# Copyright (C) 2009 Marius Storm-Olsen +###################################################################### +use strict; +use File::Basename; +use Cwd; + +my $file = $ARGV[0]; +die "No file provided!" if !defined $file; + +my ($cflags, $target, $type, $line); + +open(F, "<$file") || die "Couldn't open file $file"; +my @data = ; +close(F); + +while (my $text = shift @data) { + my $ate_next; + do { + $ate_next = 0; + $line++; + chomp $text; + chop $text if ($text =~ /\r$/); + if ($text =~ /\\$/) { + $text =~ s/\\$//; + $text .= shift @data; + $ate_next = 1; + } + } while($ate_next); + + if($text =~ / -c /) { + # compilation + handleCompileLine($text, $line); + + } elsif ($text =~ / -o /) { + # linking executable + handleLinkLine($text, $line); + + } elsif ($text =~ /\.o / && $text =~ /\.a /) { + # libifying + handleLibLine($text, $line); + +# } elsif ($text =~ /^cp /) { +# # copy file around +# +# } elsif ($text =~ /^rm -f /) { +# # shell command +# +# } elsif ($text =~ /^make[ \[]/) { +# # make output +# +# } elsif ($text =~ /^echo /) { +# # echo to file +# +# } elsif ($text =~ /^if /) { +# # shell conditional +# +# } elsif ($text =~ /^tclsh /) { +# # translation stuff +# +# } elsif ($text =~ /^umask /) { +# # handling boilerplates +# +# } elsif ($text =~ /\$\(\:\)/) { +# # ignore +# +# } elsif ($text =~ /^FLAGS=/) { +# # flags check for dependencies +# +# } elsif ($text =~ /^'\/usr\/bin\/perl' -MError -e/) { +# # perl commands for copying files +# +# } elsif ($text =~ /generate-cmdlist\.sh/) { +# # command for generating list of commands +# +# } elsif ($text =~ /^test / && $text =~ /|| rm -f /) { +# # commands removing executables, if they exist +# +# } elsif ($text =~ /new locations or Tcl/) { +# # command for detecting Tcl/Tk changes +# +# } elsif ($text =~ /mkdir -p/) { +# # command creating path +# +# } elsif ($text =~ /: no custom templates yet/) { +# # whatever + + } else { +# print "Unhandled (line: $line): $text\n"; + } +} +close(F); + +# use Data::Dumper; +# print "Parsed build structure:\n"; +# print Dumper(%build_structure); + +# ------------------------------------------------------------------- +# Functions under here +# ------------------------------------------------------------------- +my (%build_structure, @defines, @incpaths, @cflags, @sources); + +sub clearCompileStep +{ + @defines = (); + @incpaths = (); + @cflags = (); + @sources = (); +} + +sub removeDuplicates +{ + my (%dupHash, $entry); + %dupHash = map { $_, 1 } @defines; + @defines = keys %dupHash; + + %dupHash = map { $_, 1 } @incpaths; + @incpaths = keys %dupHash; + + %dupHash = map { $_, 1 } @cflags; + @cflags = keys %dupHash; + + %dupHash = map { $_, 1 } @sources; + @sources = keys %dupHash; +} + +sub handleCompileLine +{ + my ($line, $lineno) = @_; + my @parts = split(' ', $line); + shift(@parts); # ignore cmd + while (my $part = shift @parts) { + if ("$part" eq "-o") { + # ignore object file + shift @parts; + } elsif ("$part" eq "-c") { + # ignore compile flag + } elsif ("$part" eq "-c") { + } elsif ($part =~ /^.?-I/) { + push(@incpaths, $part); + } elsif ($part =~ /^.?-D/) { + push(@defines, $part); + } elsif ($part =~ /^-/) { + push(@cflags, $part); + } elsif ($part =~ /\.(c|cc|cpp)$/) { + push(@sources, $part); + } else { + die "Unhandled compiler option @ line $lineno: $part"; + } + } + #print "Sources: @sources\nCFlags: @cflags\nDefine: @defines\nIncpat: @incpaths\n"; + #exit(1); +} + +sub handleLibLine +{ + my ($line, $lineno) = @_; + my (@objfiles, @lflags, $libout, $part); + # kill cmd and rm 'prefix' + $line =~ s/^rm -f .* && .* rcs //; + my @parts = split(' ', $line); + while ($part = shift @parts) { + if ($part =~ /^-/) { + push(@lflags, $part); + } elsif ($part =~ /\.(o|obj)$/) { + push(@objfiles, $part); + } elsif ($part =~ /\.(a|lib)$/) { + $libout = $part; + } else { + die "Unhandled lib option @ line $lineno: $part"; + } + } + #print "LibOut: '$libout'\nLFlags: @lflags\nOfiles: @objfiles\n"; + #exit(1); + removeDuplicates(); + push(@{$build_structure{"LIBS"}}, $libout); + @{$build_structure{"LIBS_${libout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_SOURCES", + "_OBJECTS"); + @{$build_structure{"LIBS_${libout}_DEFINES"}} = @defines; + @{$build_structure{"LIBS_${libout}_INCLUDES"}} = @incpaths; + @{$build_structure{"LIBS_${libout}_CFLAGS"}} = @cflags; + @{$build_structure{"LIBS_${libout}_SOURCES"}} = @sources; + @{$build_structure{"LIBS_${libout}_OBJECTS"}} = @objfiles; + clearCompileStep(); +} + +sub handleLinkLine +{ + my ($line, $lineno) = @_; + my (@objfiles, @lflags, @libs, $appout, $part); + my @parts = split(' ', $line); + shift(@parts); # ignore cmd + while ($part = shift @parts) { + if ($part =~ /^-[GRIDO]/) { + # eat compiler flags + } elsif ("$part" eq "-o") { + $appout = shift @parts; + } elsif ($part =~ /^-/) { + push(@lflags, $part); + } elsif ($part =~ /\.(a|lib)$/) { + push(@libs, $part); + } elsif ($part =~ /\.(o|obj)$/) { + push(@objfiles, $part); + } else { + die "Unhandled lib option @ line $lineno: $part"; + } + } + #print "AppOut: '$appout'\nLFlags: @lflags\nLibs : @libs\nOfiles: @objfiles\n"; + #exit(1); + removeDuplicates(); + push(@{$build_structure{"APPS"}}, $appout); + @{$build_structure{"APPS_${appout}"}} = ("_DEFINES", "_INCLUDES", "_CFLAGS", "_LFLAGS", + "_SOURCES", "_OBJECTS", "_LIBS"); + @{$build_structure{"APPS_${appout}_DEFINES"}} = @defines; + @{$build_structure{"APPS_${appout}_INCLUDES"}} = @incpaths; + @{$build_structure{"APPS_${appout}_CFLAGS"}} = @cflags; + @{$build_structure{"APPS_${appout}_LFLAGS"}} = @lflags; + @{$build_structure{"APPS_${appout}_SOURCES"}} = @sources; + @{$build_structure{"APPS_${appout}_OBJECTS"}} = @objfiles; + @{$build_structure{"APPS_${appout}_LIBS"}} = @libs; + clearCompileStep(); +} -- cgit v1.2.3 From 918c03c2a7c8730dcfd413098b3dc05fa0f3166d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 15 Sep 2009 12:21:43 +0200 Subject: bash: rename __git_find_subcommand() to __git_find_on_cmdline() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __git_find_subcommand() was originally meant to check whether subcommands are already present on the command line. But the code is general enough to be used for checking the presence of command line options as well, and the next commit will use it for that purpose, so let's give it a more general name. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2d2d5794ad..277af5e861 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -620,8 +620,8 @@ __git_aliased_command () done } -# __git_find_subcommand requires 1 argument -__git_find_subcommand () +# __git_find_on_cmdline requires 1 argument +__git_find_on_cmdline () { local word subcommand c=1 @@ -740,7 +740,7 @@ _git_bisect () __git_has_doubledash && return local subcommands="start bad good skip reset visualize replay log run" - local subcommand="$(__git_find_subcommand "$subcommands")" + local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" return @@ -1749,7 +1749,7 @@ _git_config () _git_remote () { local subcommands="add rename rm show prune update set-head" - local subcommand="$(__git_find_subcommand "$subcommands")" + local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" return @@ -1877,7 +1877,7 @@ _git_show_branch () _git_stash () { local subcommands='save list show apply clear drop pop create branch' - local subcommand="$(__git_find_subcommand "$subcommands")" + local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" else @@ -1908,7 +1908,7 @@ _git_submodule () __git_has_doubledash && return local subcommands="add status init update summary foreach sync" - if [ -z "$(__git_find_subcommand "$subcommands")" ]; then + if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -1930,7 +1930,7 @@ _git_svn () proplist show-ignore show-externals branch tag blame migrate " - local subcommand="$(__git_find_subcommand "$subcommands")" + local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" else -- cgit v1.2.3 From 59d5eeee9f7e0cb71c10bde3da0fa01c3b3dbf8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 15 Sep 2009 12:21:44 +0200 Subject: bash: update 'git stash' completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This update adds 'git stash (apply|pop) --quiet' and all options known to 'git stash save', and handles the DWIMery from 3c2eb80f (stash: simplify defaulting to "save" and reject unknown options, 2009-08-18). Care is taken to avoid offering subcommands in the DWIM case. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 277af5e861..7ebc61bf11 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1876,18 +1876,30 @@ _git_show_branch () _git_stash () { + local cur="${COMP_WORDS[COMP_CWORD]}" + local save_opts='--keep-index --no-keep-index --quiet --patch' local subcommands='save list show apply clear drop pop create branch' local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then - __gitcomp "$subcommands" + case "$cur" in + --*) + __gitcomp "$save_opts" + ;; + *) + if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then + __gitcomp "$subcommands" + else + COMPREPLY=() + fi + ;; + esac else - local cur="${COMP_WORDS[COMP_CWORD]}" case "$subcommand,$cur" in save,--*) - __gitcomp "--keep-index" + __gitcomp "$save_opts" ;; apply,--*|pop,--*) - __gitcomp "--index" + __gitcomp "--index --quiet" ;; show,--*|drop,--*|branch,--*) COMPREPLY=() -- cgit v1.2.3 From 9f040e9524c45a2f862b427ec78204625e09f50e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 15 Sep 2009 12:21:45 +0200 Subject: bash: teach 'git reset --patch' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7ebc61bf11..6fd7e1d329 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1780,7 +1780,7 @@ _git_reset () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--merge --mixed --hard --soft" + __gitcomp "--merge --mixed --hard --soft --patch" return ;; esac -- cgit v1.2.3 From e648f8b6d0dc6fb7e8c8e3ed47eec6b12aaf450a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 24 Sep 2009 14:23:15 +0200 Subject: bash: teach 'git checkout' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6fd7e1d329..2c2a0d4614 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -810,7 +810,21 @@ _git_checkout () { __git_has_doubledash && return - __gitcomp "$(__git_refs)" + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --conflict=*) + __gitcomp "diff3 merge" "" "${cur##--conflict=}" + ;; + --*) + __gitcomp " + --quiet --ours --theirs --track --no-track --merge + --conflict= --patch + " + ;; + *) + __gitcomp "$(__git_refs)" + ;; + esac } _git_cherry () -- cgit v1.2.3 From 6f798b9590ca3a26d6c8e9318929eefef9b640f2 Mon Sep 17 00:00:00 2001 From: Michael Wookey Date: Mon, 28 Sep 2009 20:46:52 +1000 Subject: generators/vcproj.pm: remove UNICODE from build Defining UNICODE for MSVC IDE builds results in certain Win32 WIDE API's receiving ANSI strings. The result of which is an invalid use of the API and will end in either data corruption or an application crash. Prevent the use of WIDE API's when building with the MSVC IDE for compatibility with msysGit. Signed-off-by: Michael Wookey Acked-by: Marius Storm-Olsen Signed-off-by: Shawn O. Pearce --- contrib/buildsystems/Generators/Vcproj.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/buildsystems/Generators/Vcproj.pm b/contrib/buildsystems/Generators/Vcproj.pm index 00ec0c1369..a21591109e 100644 --- a/contrib/buildsystems/Generators/Vcproj.pm +++ b/contrib/buildsystems/Generators/Vcproj.pm @@ -173,7 +173,7 @@ sub createLibProject { Optimization="0" InlineFunctionExpansion="1" AdditionalIncludeDirectories="$includes" - PreprocessorDefinitions="UNICODE,WIN32,_DEBUG,$defines" + PreprocessorDefinitions="WIN32,_DEBUG,$defines" MinimalRebuild="true" RuntimeLibrary="1" UsePrecompiledHeader="0" @@ -239,7 +239,7 @@ sub createLibProject { InlineFunctionExpansion="1" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="$includes" - PreprocessorDefinitions="UNICODE,WIN32,NDEBUG,$defines" + PreprocessorDefinitions="WIN32,NDEBUG,$defines" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" @@ -395,7 +395,7 @@ sub createAppProject { Optimization="0" InlineFunctionExpansion="1" AdditionalIncludeDirectories="$includes" - PreprocessorDefinitions="UNICODE,WIN32,_DEBUG,$defines" + PreprocessorDefinitions="WIN32,_DEBUG,$defines" MinimalRebuild="true" RuntimeLibrary="1" UsePrecompiledHeader="0" @@ -466,7 +466,7 @@ sub createAppProject { InlineFunctionExpansion="1" EnableIntrinsicFunctions="true" AdditionalIncludeDirectories="$includes" - PreprocessorDefinitions="UNICODE,WIN32,NDEBUG,$defines" + PreprocessorDefinitions="WIN32,NDEBUG,$defines" RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" -- cgit v1.2.3 From 76031f191e5ea340f0a1501125891ae0fed76ff7 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Mon, 28 Sep 2009 13:34:20 +0200 Subject: Make generated MSVC solution file open from Windows Explorer In order to be able to open the generated solution file by double- clicking it in Windows Explorer, all project files need to use DOS line-endings and a comment about the Visual Studio version needs to be added to the header of the solution file. This also fixes the icon that is displayed for the solution file in Windows Explorer. Note that opening the solution file from a running instance of Visual Studio already worked before. Signed-off-by: Sebastian Schuberth Acked-by: Marius Storm-Olsen Signed-off-by: Shawn O. Pearce --- contrib/buildsystems/Generators/Vcproj.pm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/buildsystems/Generators/Vcproj.pm b/contrib/buildsystems/Generators/Vcproj.pm index a21591109e..37f72e53ac 100644 --- a/contrib/buildsystems/Generators/Vcproj.pm +++ b/contrib/buildsystems/Generators/Vcproj.pm @@ -131,6 +131,7 @@ sub createLibProject { $includes =~ s/-I//g; mkdir "$target" || die "Could not create the directory $target for lib project!\n"; open F, ">$target/$target.vcproj" || die "Could not open $target/$target.pro for writing!\n"; + binmode F, ":crlf"; print F << "EOM"; $target/$target.vcproj" || die "Could not open $target/$target.pro for writing!\n"; + binmode F, ":crlf"; print F << "EOM"; git.sln" || die "Could not open git.sln for writing!\n"; + binmode F, ":crlf"; print F "$SLN_HEAD"; foreach (@libs) { my $libname = $_; -- cgit v1.2.3 From e0ab002b5093e87f06871d7e25ac03e26841d355 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Mon, 28 Sep 2009 13:34:21 +0200 Subject: Make just opening the generated MSVC solution file not modify it The format of the generated MSVC solution file is fixed in a way that just opening it in Visual Studio and immediately closing it again without performing any modifications does not trigger a prompt to save the solution file. This behavior was caused by several minor incompatibilities between the generated file and what Visual Studio 2008 expected, so Visual Studio transparently fixed the file format, marking it internally as modified. Signed-off-by: Sebastian Schuberth Acked-by: Marius Storm-Olsen Signed-off-by: Shawn O. Pearce --- contrib/buildsystems/Generators/Vcproj.pm | 42 ++++++++----------------------- 1 file changed, 11 insertions(+), 31 deletions(-) (limited to 'contrib') diff --git a/contrib/buildsystems/Generators/Vcproj.pm b/contrib/buildsystems/Generators/Vcproj.pm index 37f72e53ac..be94ba18d2 100644 --- a/contrib/buildsystems/Generators/Vcproj.pm +++ b/contrib/buildsystems/Generators/Vcproj.pm @@ -571,45 +571,29 @@ sub createGlueProject { print F "\"${libname}\", \"${libname}\\${libname}.vcproj\", \"${uuid}\""; print F "$SLN_POST"; } + my $uuid_libgit = $build_structure{"LIBS_libgit_GUID"}; + my $uuid_xdiff_lib = $build_structure{"LIBS_xdiff_lib_GUID"}; foreach (@apps) { my $appname = $_; my $uuid = $build_structure{"APPS_${appname}_GUID"}; print F "$SLN_PRE"; - print F "\"${appname}\", \"${appname}\\${appname}.vcproj\", \"${uuid}\""; + print F "\"${appname}\", \"${appname}\\${appname}.vcproj\", \"${uuid}\"\n"; + print F " ProjectSection(ProjectDependencies) = postProject\n"; + print F " ${uuid_libgit} = ${uuid_libgit}\n"; + print F " ${uuid_xdiff_lib} = ${uuid_xdiff_lib}\n"; + print F " EndProjectSection"; print F "$SLN_POST"; } print F << "EOM"; Global - GlobalSection(SolutionConfiguration) = preSolution - ConfigName.0 = Debug|Win32 - ConfigName.1 = Release|Win32 + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 EndGlobalSection - GlobalSection(ProjectDependencies) = postSolution EOM - foreach (@{$build_structure{"APPS"}}) { - my $appname = $_; - my $appname_clean = $_; - $appname_clean =~ s/\//_/g; - $appname_clean =~ s/\.exe//; - - my $uuid = $build_structure{"APPS_${appname_clean}_GUID"}; - my $dep_index = 0; - foreach(@{$build_structure{"APPS_${appname}_LIBS"}}) { - my $libname = $_; - $libname =~ s/\//_/g; - $libname =~ s/\.(a|lib)//; - my $libuuid = $build_structure{"LIBS_${libname}_GUID"}; - if (defined $libuuid) { - print F "\t\t${uuid}.${dep_index} = ${libuuid}\n"; - $dep_index += 1; - } - } - } - print F << "EOM"; - EndGlobalSection - GlobalSection(ProjectConfiguration) = postSolution + GlobalSection(ProjectConfigurationPlatforms) = postSolution EOM foreach (@libs) { my $libname = $_; @@ -630,10 +614,6 @@ EOM print F << "EOM"; EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - EndGlobalSection - GlobalSection(ExtensibilityAddIns) = postSolution - EndGlobalSection EndGlobal EOM close F; -- cgit v1.2.3 From c5022f576aa583429c245054d8600564b788ff33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Tue, 29 Sep 2009 17:12:57 +0200 Subject: git-blame.el: Change how blame information is shown. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is more customizable, and uses a line prefix to show the commit. Signed-off-by: David Kågedal Signed-off-by: Shawn O. Pearce --- contrib/emacs/git-blame.el | 156 +++++++++++++++++++++++++++++---------------- 1 file changed, 102 insertions(+), 54 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index 4fa70c5ad4..7f4c792978 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -80,6 +80,57 @@ (eval-when-compile (require 'cl)) ; to use `push', `pop' +(defface git-blame-prefix-face + '((((background dark)) (:foreground "gray" + :background "black")) + (((background light)) (:foreground "gray" + :background "white")) + (t (:weight bold))) + "The face used for the hash prefix." + :group 'git-blame) + +(defgroup git-blame nil + "A minor mode showing Git blame information." + :group 'git + :link '(function-link git-blame-mode)) + + +(defcustom git-blame-use-colors t + "Use colors to indicate commits in `git-blame-mode'." + :type 'boolean + :group 'git-blame) + +(defcustom git-blame-prefix-format + "%h %20A:" + "The format of the prefix added to each line in `git-blame' +mode. The format is passed to `format-spec' with the following format keys: + + %h - the abbreviated hash + %H - the full hash + %a - the author name + %A - the author email + %c - the committer name + %C - the committer email + %s - the commit summary +" + :group 'git-blame) + +(defcustom git-blame-mouseover-format + "%h %a %A: %s" + "The format of the description shown when pointing at a line in +`git-blame' mode. The format string is passed to `format-spec' +with the following format keys: + + %h - the abbreviated hash + %H - the full hash + %a - the author name + %A - the author email + %c - the committer name + %C - the committer email + %s - the commit summary +" + :group 'git-blame) + (defun git-blame-color-scale (&rest elements) "Given a list, returns a list of triples formed with each @@ -302,72 +353,69 @@ See also function `git-blame-mode'." (src-line (string-to-number (match-string 2))) (res-line (string-to-number (match-string 3))) (num-lines (string-to-number (match-string 4)))) - (setq git-blame-current - (if (string= hash "0000000000000000000000000000000000000000") - nil - (git-blame-new-commit - hash src-line res-line num-lines)))) - (delete-region (point) (match-end 0)) - t) - ((looking-at "filename \\(.+\\)\n") - (let ((filename (match-string 1))) - (git-blame-add-info "filename" filename)) - (delete-region (point) (match-end 0)) + (delete-region (point) (match-end 0)) + (setq git-blame-current (list (git-blame-new-commit hash) + src-line res-line num-lines))) t) ((looking-at "\\([a-z-]+\\) \\(.+\\)\n") (let ((key (match-string 1)) (value (match-string 2))) - (git-blame-add-info key value)) - (delete-region (point) (match-end 0)) - t) - ((looking-at "boundary\n") - (setq git-blame-current nil) - (delete-region (point) (match-end 0)) + (delete-region (point) (match-end 0)) + (git-blame-add-info (car git-blame-current) key value) + (when (string= key "filename") + (git-blame-create-overlay (car git-blame-current) + (caddr git-blame-current) + (cadddr git-blame-current)) + (setq git-blame-current nil))) t) (t nil))) -(defun git-blame-new-commit (hash src-line res-line num-lines) +(defun git-blame-new-commit (hash) + (with-current-buffer git-blame-file + (or (gethash hash git-blame-cache) + ;; Assign a random color to each new commit info + ;; Take care not to select the same color multiple times + (let* ((color (if git-blame-colors + (git-blame-random-pop git-blame-colors) + git-blame-ancient-color)) + (info `(,hash (color . ,color)))) + (puthash hash info git-blame-cache) + info)))) + +(defun git-blame-create-overlay (info start-line num-lines) (save-excursion (set-buffer git-blame-file) - (let ((info (gethash hash git-blame-cache)) - (inhibit-point-motion-hooks t) + (let ((inhibit-point-motion-hooks t) (inhibit-modification-hooks t)) - (when (not info) - ;; Assign a random color to each new commit info - ;; Take care not to select the same color multiple times - (let ((color (if git-blame-colors - (git-blame-random-pop git-blame-colors) - git-blame-ancient-color))) - (setq info (list hash src-line res-line num-lines - (git-describe-commit hash) - (cons 'color color)))) - (puthash hash info git-blame-cache)) - (goto-line res-line) - (while (> num-lines 0) - (if (get-text-property (point) 'git-blame) - (forward-line) - (let* ((start (point)) - (end (progn (forward-line 1) (point))) - (ovl (make-overlay start end))) - (push ovl git-blame-overlays) - (overlay-put ovl 'git-blame info) - (overlay-put ovl 'help-echo hash) + (goto-line start-line) + (let* ((start (point)) + (end (progn (forward-line num-lines) (point))) + (ovl (make-overlay start end)) + (hash (car info)) + (spec `((?h . ,(substring hash 0 6)) + (?H . ,hash) + (?a . ,(git-blame-get-info info 'author)) + (?A . ,(git-blame-get-info info 'author-mail)) + (?c . ,(git-blame-get-info info 'committer)) + (?C . ,(git-blame-get-info info 'committer-mail)) + (?s . ,(git-blame-get-info info 'summary))))) + (push ovl git-blame-overlays) + (overlay-put ovl 'git-blame info) + (overlay-put ovl 'help-echo + (format-spec git-blame-mouseover-format spec)) + (if git-blame-use-colors (overlay-put ovl 'face (list :background - (cdr (assq 'color (nthcdr 5 info))))) - ;; the point-entered property doesn't seem to work in overlays - ;;(overlay-put ovl 'point-entered - ;; `(lambda (x y) (git-blame-identify ,hash))) - (let ((modified (buffer-modified-p))) - (put-text-property (if (= start 1) start (1- start)) (1- end) - 'point-entered - `(lambda (x y) (git-blame-identify ,hash))) - (set-buffer-modified-p modified)))) - (setq num-lines (1- num-lines)))))) - -(defun git-blame-add-info (key value) - (if git-blame-current - (nconc git-blame-current (list (cons (intern key) value))))) + (cdr (assq 'color (cdr info)))))) + (overlay-put ovl 'line-prefix + (propertize (format-spec git-blame-prefix-format spec) + 'face 'git-blame-prefix-face)))))) + +(defun git-blame-add-info (info key value) + (nconc info (list (cons (intern key) value)))) + +(defun git-blame-get-info (info key) + (cdr (assq key (cdr info)))) (defun git-blame-current-commit () (let ((info (get-char-property (point) 'git-blame))) -- cgit v1.2.3 From af4e9e8c87d3e73056c4758279c4c8462fe5e573 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 7 Oct 2009 01:48:50 -0700 Subject: completion: update am, commit, and log git am learned --scissors, git commit learned --dry-run and git log learned --decorate=long|short recently. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2c2a0d4614..daccbcc33e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -668,7 +668,7 @@ _git_am () --3way --committer-date-is-author-date --ignore-date --ignore-whitespace --ignore-space-change --interactive --keep --no-utf8 --signoff --utf8 - --whitespace= + --whitespace= --scissors " return esac @@ -894,6 +894,7 @@ _git_commit () __gitcomp " --all --author= --signoff --verify --no-verify --edit --amend --include --only --interactive + --dry-run " return esac @@ -1179,6 +1180,10 @@ _git_log () __gitcomp "$__git_log_date_formats" "" "${cur##--date=}" return ;; + --decorate=*) + __gitcomp "long short" "" "${cur##--decorate=}" + return + ;; --*) __gitcomp " $__git_log_common_options @@ -1191,7 +1196,7 @@ _git_log () --pretty= --format= --oneline --cherry-pick --graph - --decorate + --decorate --decorate= --walk-reflogs --parents --children $merge -- cgit v1.2.3 From 8fd2cfa7accd6b8f877014bf3e4bf8547b8e0d2a Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 7 Oct 2009 01:48:51 -0700 Subject: completion: add dirstat and friends to diff options Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index daccbcc33e..88b1b3c7a0 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -927,6 +927,8 @@ __git_diff_common_options="--stat --numstat --shortstat --summary --inter-hunk-context= --patience --raw + --dirstat --dirstat= --dirstat-by-file + --dirstat-by-file= --cumulative " _git_diff () -- cgit v1.2.3 From b6aaaa4470ef578add390bb60292ed9b4cb4af3c Mon Sep 17 00:00:00 2001 From: Ingmar Vanhassel Date: Fri, 9 Oct 2009 14:08:31 +0200 Subject: import-tars: Add missing closing bracket This fixes an obvious syntax error that snuck in commit 7e787953: syntax error at /home/ingmar/bin//git-import-tars line 143, near "/^$/ { " syntax error at /home/ingmar/bin//git-import-tars line 145, near "} else" syntax error at /home/ingmar/bin//git-import-tars line 152, near "}" Signed-off-by: Ingmar Vanhassel Acked-and-Tested-by: Peter Krefting Signed-off-by: Junio C Hamano --- contrib/fast-import/import-tars.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index a909716682..7001862bfd 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -140,7 +140,7 @@ foreach my $tar_file (@ARGV) } elsif (!$header_done && /^Author:\s+([^<>]*)\s+<(.*)>\s*$/i) { $this_author_name = $1; $this_author_email = $2; - } elsif (!$header_done && /^$/ { # empty line ends header. + } elsif (!$header_done && /^$/) { # empty line ends header. $header_done = 1; } else { $commit_msg .= $_; -- cgit v1.2.3 From 427e586b197c94961b548c8a25ed721651fc335e Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 8 Oct 2009 23:21:43 -0700 Subject: completion: fix completion of git After commit 511a3fc (wrap git's main usage string., 2009-09-12), the bash completion for git commands includes COMMAND and [ARGS] when it shouldn't. Fix this by grepping more strictly for a line with git commands. It's doubtful whether git will ever have commands starting with anything besides numbers and letters so this should be fine. At least by being stricter we'll know when we break the completion earlier. Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 88b1b3c7a0..652a47c772 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -496,7 +496,7 @@ __git_all_commands () return fi local i IFS=" "$'\n' - for i in $(git help -a|egrep '^ ') + for i in $(git help -a|egrep '^ [a-zA-Z0-9]') do case $i in *--*) : helper pattern;; -- cgit v1.2.3 From e0d78059540aab2e6ff720e7c940d7244cc12c18 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 8 Oct 2009 23:21:44 -0700 Subject: completion: fix alias listings with newlines Aliases with newlines have been a problem since commit 56fc25f (Bash completion support for remotes in .git/config., 2006-11-05). The chance of the problem occurring has been slim at best, until commit 518ef8f (completion: Replace config --list with --get-regexp, 2009-09-11) removed the case statement introduced by commit 56fc25f. Before removing the case statement, most aliases with newlines would work unless they were specially crafted as follows [alias] foo = "log -1 --pretty='format:%s\nalias.error=broken'" After removing the case statement, a more benign alias like [alias] whowhat = "log -1 --pretty='format:%an <%ae>\n%s'" wont-complete = ... would cause the completion to break badly. For now, revert the removal of the case statement until someone comes up with a better way to get keys from git-config. Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 652a47c772..e482c8d076 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -602,8 +602,12 @@ __git_aliases () { local i IFS=$'\n' for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do - i="${i#alias.}" - echo "${i/ */}" + case "$i" in + alias.*) + i="${i#alias.}" + echo "${i/ */}" + ;; + esac done } -- cgit v1.2.3 From e1c1a0674b5848eb3207a29d5513c9112c72d10c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 9 Oct 2009 22:49:06 +0200 Subject: bash: add support for 'git replace' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Gustavsson Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e482c8d076..7cf8557468 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1798,6 +1798,11 @@ _git_remote () esac } +_git_replace () +{ + __gitcomp "$(__git_refs)" +} + _git_reset () { __git_has_doubledash && return @@ -2166,6 +2171,7 @@ _git () push) _git_push ;; rebase) _git_rebase ;; remote) _git_remote ;; + replace) _git_replace ;; reset) _git_reset ;; revert) _git_revert ;; rm) _git_rm ;; -- cgit v1.2.3 From 17225c49d501a304a0ae9ff370ca5cc3ae4afe4d Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 12 Oct 2009 11:00:09 +0200 Subject: bash completion: complete refs for git-grep Before the --, always attempt ref completion. This helps with entering the arguments to git-grep. As a bonus, you can work around git-grep's current lack of --all by hitting M-*, ugly as the resulting command line may be. Strictly speaking, completing the regular expression argument (or option argument) makes no sense. However, we cannot prevent _all_ completion (it will fall back to filenames), so we dispense with any additional complication to detect whether the user still has to enter a regular expression. Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7cf8557468..d3fec32997 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1069,7 +1069,8 @@ _git_grep () return ;; esac - COMPREPLY=() + + __gitcomp "$(__git_refs)" } _git_help () -- cgit v1.2.3 From 2a94552887ecbaa8b848a92eb607e032d77c8a2c Mon Sep 17 00:00:00 2001 From: Ingmar Vanhassel Date: Tue, 20 Oct 2009 12:29:32 +0200 Subject: import-tars: Add support for tarballs compressed with lzma, xz Also handle the extensions .tlz and .txz, aliases for .tar.lzma and .tar.xz respectively. Signed-off-by: Ingmar Vanhassel Liked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/fast-import/import-tars.perl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 7001862bfd..95438e1ed4 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -20,7 +20,7 @@ use Getopt::Long; my $metaext = ''; -die "usage: import-tars [--metainfo=extension] *.tar.{gz,bz2,Z}\n" +die "usage: import-tars [--metainfo=extension] *.tar.{gz,bz2,lzma,xz,Z}\n" unless GetOptions('metainfo=s' => \$metaext) && @ARGV; my $branch_name = 'import-tars'; @@ -49,6 +49,9 @@ foreach my $tar_file (@ARGV) } elsif ($tar_name =~ s/\.tar\.Z$//) { open(I, '-|', 'uncompress', '-c', $tar_file) or die "Unable to uncompress -c $tar_file: $!\n"; + } elsif ($tar_name =~ s/\.(tar\.(lzma|xz)|(tlz|txz))$//) { + open(I, '-|', 'xz', '-dc', $tar_file) + or die "Unable to xz -dc $tar_file: $!\n"; } elsif ($tar_name =~ s/\.tar$//) { open(I, $tar_file) or die "Unable to open $tar_file: $!\n"; } else { -- cgit v1.2.3 From c36e16385be363a11abaa4a43edd5a2b2b2dff81 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Wed, 21 Oct 2009 19:04:51 +0200 Subject: MSVC: Enable OpenSSL, and translate -lcrypto We don't use crypto, but rather require libeay32 and ssleay32. handle it in both the Makefile msvc linker script, and the buildsystem generator. Signed-off-by: Marius Storm-Olsen Signed-off-by: Erik Faye-Lund Signed-off-by: Junio C Hamano --- contrib/buildsystems/engine.pl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/buildsystems/engine.pl b/contrib/buildsystems/engine.pl index 20bd061b3e..d506717bfd 100644 --- a/contrib/buildsystems/engine.pl +++ b/contrib/buildsystems/engine.pl @@ -315,6 +315,9 @@ sub handleLinkLine $appout = shift @parts; } elsif ("$part" eq "-lz") { push(@libs, "zlib.lib"); + } elsif ("$part" eq "-lcrypto") { + push(@libs, "libeay32.lib"); + push(@libs, "ssleay32.lib"); } elsif ($part =~ /^-/) { push(@lflags, $part); } elsif ($part =~ /\.(a|lib)$/) { -- cgit v1.2.3 From a75d7b54097ef0d0945cbe673a9940d6c561f95c Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Sat, 24 Oct 2009 11:31:32 +0300 Subject: Use 'fast-forward' all over the place It's a compound word. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- contrib/examples/git-merge.sh | 8 ++++---- contrib/examples/git-resolve.sh | 2 +- contrib/hooks/post-receive-email | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh index e9588eec33..500635fe4b 100755 --- a/contrib/examples/git-merge.sh +++ b/contrib/examples/git-merge.sh @@ -14,7 +14,7 @@ summary (synonym to --stat) log add list of one-line log to merge commit message squash create a single commit instead of doing a merge commit perform a commit if the merge succeeds (default) -ff allow fast forward (default) +ff allow fast-forward (default) s,strategy= merge strategy to use m,message= message to be used for the merge commit (if any) " @@ -353,7 +353,7 @@ t,1,"$head",*) # Again the most common case of merging one remote. echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)" git update-index --refresh 2>/dev/null - msg="Fast forward" + msg="Fast-forward" if test -n "$have_message" then msg="$msg (no commit created; -m option ignored)" @@ -365,11 +365,11 @@ t,1,"$head",*) exit 0 ;; ?,1,?*"$LF"?*,*) - # We are not doing octopus and not fast forward. Need a + # We are not doing octopus and not fast-forward. Need a # real merge. ;; ?,1,*,) - # We are not doing octopus, not fast forward, and have only + # We are not doing octopus, not fast-forward, and have only # one common. git update-index --refresh 2>/dev/null case "$allow_trivial_merge" in diff --git a/contrib/examples/git-resolve.sh b/contrib/examples/git-resolve.sh index 0ee1bd898e..8f98142f77 100755 --- a/contrib/examples/git-resolve.sh +++ b/contrib/examples/git-resolve.sh @@ -48,7 +48,7 @@ case "$common" in "$head") echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $merge)" git read-tree -u -m $head $merge || exit 1 - git update-ref -m "resolve $merge_name: Fast forward" \ + git update-ref -m "resolve $merge_name: Fast-forward" \ HEAD "$merge" "$head" git diff-tree -p $head $merge | git apply --stat dropheads diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 2a66063e44..58a35c8287 100755 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -315,8 +315,8 @@ generate_update_branch_email() # "remotes/" will be ignored as well. # List all of the revisions that were removed by this update, in a - # fast forward update, this list will be empty, because rev-list O - # ^N is empty. For a non fast forward, O ^N is the list of removed + # fast-forward update, this list will be empty, because rev-list O + # ^N is empty. For a non-fast-forward, O ^N is the list of removed # revisions fast_forward="" rev="" @@ -411,7 +411,7 @@ generate_update_branch_email() # revision because the base is effectively a random revision at this # point - the user will be interested in what this revision changed # - including the undoing of previous revisions in the case of - # non-fast forward updates. + # non-fast-forward updates. echo "" echo "Summary of changes:" git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev -- cgit v1.2.3 From 93cf50a412e3ed988cbce07e2ba3a80841d3884b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Sat, 17 Oct 2009 11:33:38 +0200 Subject: bash: complete more options for 'git rebase' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete all long options for 'git rebase' except --no-verify (probably used very seldom) and the long options corresponding to -v, -q, and -f. Signed-off-by: Björn Gustavsson Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d3fec32997..7c7318c436 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1323,8 +1323,18 @@ _git_rebase () fi __git_complete_strategy && return case "$cur" in + --whitespace=*) + __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}" + return + ;; --*) - __gitcomp "--onto --merge --strategy --interactive" + __gitcomp " + --onto --merge --strategy --interactive + --preserve-merges --stat --no-stat + --committer-date-is-author-date --ignore-date + --ignore-whitespace --whitespace= + " + return esac __gitcomp "$(__git_refs)" -- cgit v1.2.3 From f7ad96cfaaec3b32458ca69d44c50d1397d11d38 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Wed, 28 Oct 2009 10:45:38 +0100 Subject: bash completion: difftool accepts the same options as diff So complete refs, files after the double-dash and some diff options that make sense for difftool. Signed-off-by: Markus Heidelberg Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7c7318c436..e3ddecc995 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -958,6 +958,8 @@ __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff _git_difftool () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --tool=*) @@ -965,11 +967,15 @@ _git_difftool () return ;; --*) - __gitcomp "--tool=" + __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex + --base --ours --theirs + --no-renames --diff-filter= --find-copies-harder + --relative --ignore-submodules + --tool=" return ;; esac - COMPREPLY=() + __git_complete_file } __git_fetch_options=" -- cgit v1.2.3 From c8998b4823cbccd6bd49c2034e242ae7d5873eae Mon Sep 17 00:00:00 2001 From: Scott Chacon Date: Wed, 28 Oct 2009 14:39:32 -0700 Subject: mergetool--lib: add p4merge as a pre-configured mergetool option Add p4merge to the set of built-in diff/merge tools, and update bash completion and documentation. Signed-off-by: Scott Chacon Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e3ddecc995..2a9a88968f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -953,7 +953,7 @@ _git_diff () } __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff - tkdiff vimdiff gvimdiff xxdiff araxis + tkdiff vimdiff gvimdiff xxdiff araxis p4merge " _git_difftool () -- cgit v1.2.3 From 3fe9747b94ca81d29894debdb371cff3b1d89af1 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Mon, 19 Oct 2009 18:40:47 +0200 Subject: Make the MSVC projects use PDB/IDB files named after the project Instead of having all PDB files for all projects named "vc90.pdb", name them after the respective project to make the relation more clear (and to avoid name clashes when copying files around). Signed-off-by: Sebastian Schuberth Acked-by: Marius Storm-Olsen Signed-off-by: Junio C Hamano --- contrib/buildsystems/Generators/Vcproj.pm | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/buildsystems/Generators/Vcproj.pm b/contrib/buildsystems/Generators/Vcproj.pm index be94ba18d2..cfa74adcc2 100644 --- a/contrib/buildsystems/Generators/Vcproj.pm +++ b/contrib/buildsystems/Generators/Vcproj.pm @@ -178,6 +178,7 @@ sub createLibProject { MinimalRebuild="true" RuntimeLibrary="1" UsePrecompiledHeader="0" + ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb" WarningLevel="3" DebugInformationFormat="3" /> @@ -244,6 +245,7 @@ sub createLibProject { RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" + ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb" WarningLevel="3" DebugInformationFormat="3" /> @@ -401,6 +403,7 @@ sub createAppProject { MinimalRebuild="true" RuntimeLibrary="1" UsePrecompiledHeader="0" + ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb" WarningLevel="3" DebugInformationFormat="3" /> @@ -472,6 +475,7 @@ sub createAppProject { RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" + ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb" WarningLevel="3" DebugInformationFormat="3" /> -- cgit v1.2.3 From fe9a215214acd2cf9132aec70e0758786a6e3e8b Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Mon, 9 Nov 2009 09:04:41 -0600 Subject: Retire fetch--tool helper to contrib/examples When git-fetch was builtin-ized, the previous script was moved to contrib/examples. Now, it is the sole remaining user for 'git fetch--tool'. The fetch--tool code is still worth keeping around so people can try out the old git-fetch.sh, for example when investigating regressions from the builtinifaction. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- contrib/examples/builtin-fetch--tool.c | 574 +++++++++++++++++++++++++++++++++ 1 file changed, 574 insertions(+) create mode 100644 contrib/examples/builtin-fetch--tool.c (limited to 'contrib') diff --git a/contrib/examples/builtin-fetch--tool.c b/contrib/examples/builtin-fetch--tool.c new file mode 100644 index 0000000000..3dbdf7a288 --- /dev/null +++ b/contrib/examples/builtin-fetch--tool.c @@ -0,0 +1,574 @@ +#include "builtin.h" +#include "cache.h" +#include "refs.h" +#include "commit.h" +#include "sigchain.h" + +static char *get_stdin(void) +{ + struct strbuf buf = STRBUF_INIT; + if (strbuf_read(&buf, 0, 1024) < 0) { + die_errno("error reading standard input"); + } + return strbuf_detach(&buf, NULL); +} + +static void show_new(enum object_type type, unsigned char *sha1_new) +{ + fprintf(stderr, " %s: %s\n", typename(type), + find_unique_abbrev(sha1_new, DEFAULT_ABBREV)); +} + +static int update_ref_env(const char *action, + const char *refname, + unsigned char *sha1, + unsigned char *oldval) +{ + char msg[1024]; + const char *rla = getenv("GIT_REFLOG_ACTION"); + + if (!rla) + rla = "(reflog update)"; + if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg)) + warning("reflog message too long: %.*s...", 50, msg); + return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR); +} + +static int update_local_ref(const char *name, + const char *new_head, + const char *note, + int verbose, int force) +{ + unsigned char sha1_old[20], sha1_new[20]; + char oldh[41], newh[41]; + struct commit *current, *updated; + enum object_type type; + + if (get_sha1_hex(new_head, sha1_new)) + die("malformed object name %s", new_head); + + type = sha1_object_info(sha1_new, NULL); + if (type < 0) + die("object %s not found", new_head); + + if (!*name) { + /* Not storing */ + if (verbose) { + fprintf(stderr, "* fetched %s\n", note); + show_new(type, sha1_new); + } + return 0; + } + + if (get_sha1(name, sha1_old)) { + const char *msg; + just_store: + /* new ref */ + if (!strncmp(name, "refs/tags/", 10)) + msg = "storing tag"; + else + msg = "storing head"; + fprintf(stderr, "* %s: storing %s\n", + name, note); + show_new(type, sha1_new); + return update_ref_env(msg, name, sha1_new, NULL); + } + + if (!hashcmp(sha1_old, sha1_new)) { + if (verbose) { + fprintf(stderr, "* %s: same as %s\n", name, note); + show_new(type, sha1_new); + } + return 0; + } + + if (!strncmp(name, "refs/tags/", 10)) { + fprintf(stderr, "* %s: updating with %s\n", name, note); + show_new(type, sha1_new); + return update_ref_env("updating tag", name, sha1_new, NULL); + } + + current = lookup_commit_reference(sha1_old); + updated = lookup_commit_reference(sha1_new); + if (!current || !updated) + goto just_store; + + strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV)); + strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV)); + + if (in_merge_bases(current, &updated, 1)) { + fprintf(stderr, "* %s: fast forward to %s\n", + name, note); + fprintf(stderr, " old..new: %s..%s\n", oldh, newh); + return update_ref_env("fast forward", name, sha1_new, sha1_old); + } + if (!force) { + fprintf(stderr, + "* %s: not updating to non-fast forward %s\n", + name, note); + fprintf(stderr, + " old...new: %s...%s\n", oldh, newh); + return 1; + } + fprintf(stderr, + "* %s: forcing update to non-fast forward %s\n", + name, note); + fprintf(stderr, " old...new: %s...%s\n", oldh, newh); + return update_ref_env("forced-update", name, sha1_new, sha1_old); +} + +static int append_fetch_head(FILE *fp, + const char *head, const char *remote, + const char *remote_name, const char *remote_nick, + const char *local_name, int not_for_merge, + int verbose, int force) +{ + struct commit *commit; + int remote_len, i, note_len; + unsigned char sha1[20]; + char note[1024]; + const char *what, *kind; + + if (get_sha1(head, sha1)) + return error("Not a valid object name: %s", head); + commit = lookup_commit_reference_gently(sha1, 1); + if (!commit) + not_for_merge = 1; + + if (!strcmp(remote_name, "HEAD")) { + kind = ""; + what = ""; + } + else if (!strncmp(remote_name, "refs/heads/", 11)) { + kind = "branch"; + what = remote_name + 11; + } + else if (!strncmp(remote_name, "refs/tags/", 10)) { + kind = "tag"; + what = remote_name + 10; + } + else if (!strncmp(remote_name, "refs/remotes/", 13)) { + kind = "remote branch"; + what = remote_name + 13; + } + else { + kind = ""; + what = remote_name; + } + + remote_len = strlen(remote); + for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--) + ; + remote_len = i + 1; + if (4 < i && !strncmp(".git", remote + i - 3, 4)) + remote_len = i - 3; + + note_len = 0; + if (*what) { + if (*kind) + note_len += sprintf(note + note_len, "%s ", kind); + note_len += sprintf(note + note_len, "'%s' of ", what); + } + note_len += sprintf(note + note_len, "%.*s", remote_len, remote); + fprintf(fp, "%s\t%s\t%s\n", + sha1_to_hex(commit ? commit->object.sha1 : sha1), + not_for_merge ? "not-for-merge" : "", + note); + return update_local_ref(local_name, head, note, verbose, force); +} + +static char *keep; +static void remove_keep(void) +{ + if (keep && *keep) + unlink(keep); +} + +static void remove_keep_on_signal(int signo) +{ + remove_keep(); + sigchain_pop(signo); + raise(signo); +} + +static char *find_local_name(const char *remote_name, const char *refs, + int *force_p, int *not_for_merge_p) +{ + const char *ref = refs; + int len = strlen(remote_name); + + while (ref) { + const char *next; + int single_force, not_for_merge; + + while (*ref == '\n') + ref++; + if (!*ref) + break; + next = strchr(ref, '\n'); + + single_force = not_for_merge = 0; + if (*ref == '+') { + single_force = 1; + ref++; + } + if (*ref == '.') { + not_for_merge = 1; + ref++; + if (*ref == '+') { + single_force = 1; + ref++; + } + } + if (!strncmp(remote_name, ref, len) && ref[len] == ':') { + const char *local_part = ref + len + 1; + int retlen; + + if (!next) + retlen = strlen(local_part); + else + retlen = next - local_part; + *force_p = single_force; + *not_for_merge_p = not_for_merge; + return xmemdupz(local_part, retlen); + } + ref = next; + } + return NULL; +} + +static int fetch_native_store(FILE *fp, + const char *remote, + const char *remote_nick, + const char *refs, + int verbose, int force) +{ + char buffer[1024]; + int err = 0; + + sigchain_push_common(remove_keep_on_signal); + atexit(remove_keep); + + while (fgets(buffer, sizeof(buffer), stdin)) { + int len; + char *cp; + char *local_name; + int single_force, not_for_merge; + + for (cp = buffer; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = 0; + len = strlen(cp); + if (len && cp[len-1] == '\n') + cp[--len] = 0; + if (!strcmp(buffer, "failed")) + die("Fetch failure: %s", remote); + if (!strcmp(buffer, "pack")) + continue; + if (!strcmp(buffer, "keep")) { + char *od = get_object_directory(); + int len = strlen(od) + strlen(cp) + 50; + keep = xmalloc(len); + sprintf(keep, "%s/pack/pack-%s.keep", od, cp); + continue; + } + + local_name = find_local_name(cp, refs, + &single_force, ¬_for_merge); + if (!local_name) + continue; + err |= append_fetch_head(fp, + buffer, remote, cp, remote_nick, + local_name, not_for_merge, + verbose, force || single_force); + } + return err; +} + +static int parse_reflist(const char *reflist) +{ + const char *ref; + + printf("refs='"); + for (ref = reflist; ref; ) { + const char *next; + while (*ref && isspace(*ref)) + ref++; + if (!*ref) + break; + for (next = ref; *next && !isspace(*next); next++) + ; + printf("\n%.*s", (int)(next - ref), ref); + ref = next; + } + printf("'\n"); + + printf("rref='"); + for (ref = reflist; ref; ) { + const char *next, *colon; + while (*ref && isspace(*ref)) + ref++; + if (!*ref) + break; + for (next = ref; *next && !isspace(*next); next++) + ; + if (*ref == '.') + ref++; + if (*ref == '+') + ref++; + colon = strchr(ref, ':'); + putchar('\n'); + printf("%.*s", (int)((colon ? colon : next) - ref), ref); + ref = next; + } + printf("'\n"); + return 0; +} + +static int expand_refs_wildcard(const char *ls_remote_result, int numrefs, + const char **refs) +{ + int i, matchlen, replacelen; + int found_one = 0; + const char *remote = *refs++; + numrefs--; + + if (numrefs == 0) { + fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n", + remote); + printf("empty\n"); + } + + for (i = 0; i < numrefs; i++) { + const char *ref = refs[i]; + const char *lref = ref; + const char *colon; + const char *tail; + const char *ls; + const char *next; + + if (*lref == '+') + lref++; + colon = strchr(lref, ':'); + tail = lref + strlen(lref); + if (!(colon && + 2 < colon - lref && + colon[-1] == '*' && + colon[-2] == '/' && + 2 < tail - (colon + 1) && + tail[-1] == '*' && + tail[-2] == '/')) { + /* not a glob */ + if (!found_one++) + printf("explicit\n"); + printf("%s\n", ref); + continue; + } + + /* glob */ + if (!found_one++) + printf("glob\n"); + + /* lref to colon-2 is remote hierarchy name; + * colon+1 to tail-2 is local. + */ + matchlen = (colon-1) - lref; + replacelen = (tail-1) - (colon+1); + for (ls = ls_remote_result; ls; ls = next) { + const char *eol; + unsigned char sha1[20]; + int namelen; + + while (*ls && isspace(*ls)) + ls++; + next = strchr(ls, '\n'); + eol = !next ? (ls + strlen(ls)) : next; + if (!memcmp("^{}", eol-3, 3)) + continue; + if (eol - ls < 40) + continue; + if (get_sha1_hex(ls, sha1)) + continue; + ls += 40; + while (ls < eol && isspace(*ls)) + ls++; + /* ls to next (or eol) is the name. + * is it identical to lref to colon-2? + */ + if ((eol - ls) <= matchlen || + strncmp(ls, lref, matchlen)) + continue; + + /* Yes, it is a match */ + namelen = eol - ls; + if (lref != ref) + putchar('+'); + printf("%.*s:%.*s%.*s\n", + namelen, ls, + replacelen, colon + 1, + namelen - matchlen, ls + matchlen); + } + } + return 0; +} + +static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_result) +{ + int err = 0; + int lrr_count = lrr_count, i, pass; + const char *cp; + struct lrr { + const char *line; + const char *name; + int namelen; + int shown; + } *lrr_list = lrr_list; + + for (pass = 0; pass < 2; pass++) { + /* pass 0 counts and allocates, pass 1 fills... */ + cp = ls_remote_result; + i = 0; + while (1) { + const char *np; + while (*cp && isspace(*cp)) + cp++; + if (!*cp) + break; + np = strchrnul(cp, '\n'); + if (pass) { + lrr_list[i].line = cp; + lrr_list[i].name = cp + 41; + lrr_list[i].namelen = np - (cp + 41); + } + i++; + cp = np; + } + if (!pass) { + lrr_count = i; + lrr_list = xcalloc(lrr_count, sizeof(*lrr_list)); + } + } + + while (1) { + const char *next; + int rreflen; + int i; + + while (*rref && isspace(*rref)) + rref++; + if (!*rref) + break; + next = strchrnul(rref, '\n'); + rreflen = next - rref; + + for (i = 0; i < lrr_count; i++) { + struct lrr *lrr = &(lrr_list[i]); + + if (rreflen == lrr->namelen && + !memcmp(lrr->name, rref, rreflen)) { + if (!lrr->shown) + printf("%.*s\n", + sha1_only ? 40 : lrr->namelen + 41, + lrr->line); + lrr->shown = 1; + break; + } + } + if (lrr_count <= i) { + error("pick-rref: %.*s not found", rreflen, rref); + err = 1; + } + rref = next; + } + free(lrr_list); + return err; +} + +int cmd_fetch__tool(int argc, const char **argv, const char *prefix) +{ + int verbose = 0; + int force = 0; + int sopt = 0; + + while (1 < argc) { + const char *arg = argv[1]; + if (!strcmp("-v", arg)) + verbose = 1; + else if (!strcmp("-f", arg)) + force = 1; + else if (!strcmp("-s", arg)) + sopt = 1; + else + break; + argc--; + argv++; + } + + if (argc <= 1) + return error("Missing subcommand"); + + if (!strcmp("append-fetch-head", argv[1])) { + int result; + FILE *fp; + char *filename; + + if (argc != 8) + return error("append-fetch-head takes 6 args"); + filename = git_path("FETCH_HEAD"); + fp = fopen(filename, "a"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); + result = append_fetch_head(fp, argv[2], argv[3], + argv[4], argv[5], + argv[6], !!argv[7][0], + verbose, force); + fclose(fp); + return result; + } + if (!strcmp("native-store", argv[1])) { + int result; + FILE *fp; + char *filename; + + if (argc != 5) + return error("fetch-native-store takes 3 args"); + filename = git_path("FETCH_HEAD"); + fp = fopen(filename, "a"); + if (!fp) + return error("cannot open %s: %s\n", filename, strerror(errno)); + result = fetch_native_store(fp, argv[2], argv[3], argv[4], + verbose, force); + fclose(fp); + return result; + } + if (!strcmp("parse-reflist", argv[1])) { + const char *reflist; + if (argc != 3) + return error("parse-reflist takes 1 arg"); + reflist = argv[2]; + if (!strcmp(reflist, "-")) + reflist = get_stdin(); + return parse_reflist(reflist); + } + if (!strcmp("pick-rref", argv[1])) { + const char *ls_remote_result; + if (argc != 4) + return error("pick-rref takes 2 args"); + ls_remote_result = argv[3]; + if (!strcmp(ls_remote_result, "-")) + ls_remote_result = get_stdin(); + return pick_rref(sopt, argv[2], ls_remote_result); + } + if (!strcmp("expand-refs-wildcard", argv[1])) { + const char *reflist; + if (argc < 4) + return error("expand-refs-wildcard takes at least 2 args"); + reflist = argv[2]; + if (!strcmp(reflist, "-")) + reflist = get_stdin(); + return expand_refs_wildcard(reflist, argc - 3, argv + 3); + } + + return error("Unknown subcommand: %s", argv[1]); +} -- cgit v1.2.3 From b4479f074760a788dd4e353b8c86a7d735afc53e Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Fri, 30 Oct 2009 20:42:34 -0500 Subject: add -i, send-email, svn, p4, etc: use "git var GIT_EDITOR" Use the new "git var GIT_EDITOR" feature to decide what editor to use, instead of duplicating its logic elsewhere. This should make the behavior of commands in edge cases (e.g., editor names with spaces) a little more consistent. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e710219ca5..48059d0aa7 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -729,13 +729,10 @@ class P4Submit(Command): tmpFile.write(submitTemplate + separatorLine + diff + newdiff) tmpFile.close() mtime = os.stat(fileName).st_mtime - defaultEditor = "vi" - if platform.system() == "Windows": - defaultEditor = "notepad" if os.environ.has_key("P4EDITOR"): editor = os.environ.get("P4EDITOR") else: - editor = os.environ.get("EDITOR", defaultEditor); + editor = read_pipe("git var GIT_EDITOR") system(editor + " " + fileName) response = "y" -- cgit v1.2.3 From 9858b87fbb990659ea98f1bc77c2e0283cdcaa4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 11 Nov 2009 05:49:07 +0100 Subject: bash: add the merge option --ff-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Gustavsson Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e3ddecc995..a0917d1809 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1224,7 +1224,7 @@ _git_log () __git_merge_options=" --no-commit --no-stat --log --no-log --squash --strategy - --commit --stat --no-squash --ff --no-ff + --commit --stat --no-squash --ff --no-ff --ff-only " _git_merge () -- cgit v1.2.3 From eaa4e6ee2a1df289e6900ee908751463b22a3a54 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Tue, 17 Nov 2009 18:49:10 -0600 Subject: Speed up bash completion loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since git is not used in each and every interactive xterm, it seems best to load completion support with cold caches and then load each needed thing lazily. This has most of the speed advantage of pre-generating everything at build time, without the complication of figuring out at build time what commands will be available at run time. On this slow laptop, this decreases the time to load git-completion.bash from about 500 ms to about 175 ms. Suggested-by: Kirill Smelkov Acked-by: Shawn O. Pearce Cc: Stephen Boyd Cc: SZEDER Gábor Cc: Sverre Rabbelier Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 76 ++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 35 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7cf8557468..f67254d3d1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -21,13 +21,7 @@ # 2) Added the following line to your .bashrc: # source ~/.git-completion.sh # -# 3) You may want to make sure the git executable is available -# in your PATH before this script is sourced, as some caching -# is performed while the script loads. If git isn't found -# at source time then all lookups will be done on demand, -# which may be slightly slower. -# -# 4) Consider changing your PS1 to also show the current branch: +# 3) Consider changing your PS1 to also show the current branch: # PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ ' # # The argument to __git_ps1 will be displayed only if you @@ -324,12 +318,8 @@ __git_remotes () done } -__git_merge_strategies () +__git_list_merge_strategies () { - if [ -n "${__git_merge_strategylist-}" ]; then - echo "$__git_merge_strategylist" - return - fi git merge -s help 2>&1 | sed -n -e '/[Aa]vailable strategies are: /,/^$/{ s/\.$// @@ -339,8 +329,17 @@ __git_merge_strategies () p }' } -__git_merge_strategylist= -__git_merge_strategylist=$(__git_merge_strategies 2>/dev/null) + +__git_merge_strategies= +# 'git merge -s help' (and thus detection of the merge strategy +# list) fails, unfortunately, if run outside of any git working +# tree. __git_merge_strategies is set to the empty string in +# that case, and the detection will be repeated the next time it +# is needed. +__git_compute_merge_strategies () +{ + : ${__git_merge_strategies:=$(__git_list_merge_strategies)} +} __git_complete_file () { @@ -474,27 +473,24 @@ __git_complete_remote_or_refspec () __git_complete_strategy () { + __git_compute_merge_strategies case "${COMP_WORDS[COMP_CWORD-1]}" in -s|--strategy) - __gitcomp "$(__git_merge_strategies)" + __gitcomp "$__git_merge_strategies" return 0 esac local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --strategy=*) - __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" + __gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}" return 0 ;; esac return 1 } -__git_all_commands () +__git_list_all_commands () { - if [ -n "${__git_all_commandlist-}" ]; then - echo "$__git_all_commandlist" - return - fi local i IFS=" "$'\n' for i in $(git help -a|egrep '^ [a-zA-Z0-9]') do @@ -504,17 +500,18 @@ __git_all_commands () esac done } -__git_all_commandlist= -__git_all_commandlist="$(__git_all_commands 2>/dev/null)" -__git_porcelain_commands () +__git_all_commands= +__git_compute_all_commands () +{ + : ${__git_all_commands:=$(__git_list_all_commands)} +} + +__git_list_porcelain_commands () { - if [ -n "${__git_porcelain_commandlist-}" ]; then - echo "$__git_porcelain_commandlist" - return - fi local i IFS=" "$'\n' - for i in "help" $(__git_all_commands) + __git_compute_all_commands + for i in "help" $__git_all_commands do case $i in *--*) : helper pattern;; @@ -595,8 +592,13 @@ __git_porcelain_commands () esac done } -__git_porcelain_commandlist= -__git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)" + +__git_porcelain_commands= +__git_compute_porcelain_commands () +{ + __git_compute_all_commands + : ${__git_porcelain_commands:=$(__git_list_porcelain_commands)} +} __git_aliases () { @@ -1081,7 +1083,8 @@ _git_help () return ;; esac - __gitcomp "$(__git_all_commands) + __git_compute_all_commands + __gitcomp "$__git_all_commands attributes cli core-tutorial cvs-migration diffcore gitk glossary hooks ignore modules repository-layout tutorial tutorial-2 @@ -1427,7 +1430,8 @@ _git_config () return ;; pull.twohead|pull.octopus) - __gitcomp "$(__git_merge_strategies)" + __git_compute_merge_strategies + __gitcomp "$__git_merge_strategies" return ;; color.branch|color.diff|color.interactive|\ @@ -1528,7 +1532,8 @@ _git_config () pager.*) local pfx="${cur%.*}." cur="${cur#*.}" - __gitcomp "$(__git_all_commands)" "$pfx" "$cur" + __git_compute_all_commands + __gitcomp "$__git_all_commands" "$pfx" "$cur" return ;; remote.*.*) @@ -2125,7 +2130,8 @@ _git () --help " ;; - *) __gitcomp "$(__git_porcelain_commands) $(__git_aliases)" ;; + *) __git_compute_porcelain_commands + __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;; esac return fi -- cgit v1.2.3 From 9a424b276c409a3510e7735b6ecc012f50dc2a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sat, 5 Dec 2009 01:51:41 +0100 Subject: bash: update 'git commit' completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I just wanted to add the recently learnt '--reset-author' option, but then noticed that there are many more options missing. This patch adds support for all of 'git commit's options, except '--allow-empty', because it is primarily there for foreign scm interfaces. Furthermore, this patch also adds support for completing the arguments of those options that take a non-filename argument: valid modes are offered for '--cleanup' and '--untracked-files', while refs for '--reuse-message' and '--reedit-message', because these two take a commit as argument. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 11bf17a86c..7c18b0c07e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -896,11 +896,31 @@ _git_commit () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in + --cleanup=*) + __gitcomp "default strip verbatim whitespace + " "" "${cur##--cleanup=}" + return + ;; + --reuse-message=*) + __gitcomp "$(__git_refs)" "" "${cur##--reuse-message=}" + return + ;; + --reedit-message=*) + __gitcomp "$(__git_refs)" "" "${cur##--reedit-message=}" + return + ;; + --untracked-files=*) + __gitcomp "all no normal" "" "${cur##--untracked-files=}" + return + ;; --*) __gitcomp " --all --author= --signoff --verify --no-verify --edit --amend --include --only --interactive - --dry-run + --dry-run --reuse-message= --reedit-message= + --reset-author --file= --message= --template= + --cleanup= --untracked-files --untracked-files= + --verbose --quiet " return esac -- cgit v1.2.3 From e25e2b4201716a912b29397515a858238497dbdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Sat, 12 Dec 2009 11:21:46 +0100 Subject: bash: Support new 'git fetch' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support the new options --all, --prune, and --dry-run for 'git fetch'. As the --multiple option was primarily introduced to enable 'git remote update' to be re-implemented in terms of 'git fetch' (16679e37) and is not likely to be used much from the command line, it does not seems worthwhile to complicate the code (to support completion of multiple remotes) to handle it. Signed-off-by: Björn Gustavsson Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7c18b0c07e..fbfa5f25c1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -417,7 +417,17 @@ __git_complete_remote_or_refspec () while [ $c -lt $COMP_CWORD ]; do i="${COMP_WORDS[c]}" case "$i" in - --all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;; + --mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;; + --all) + case "$cmd" in + push) no_complete_refspec=1 ;; + fetch) + COMPREPLY=() + return + ;; + *) ;; + esac + ;; -*) ;; *) remote="$i"; break ;; esac @@ -1002,7 +1012,7 @@ _git_difftool () __git_fetch_options=" --quiet --verbose --append --upload-pack --force --keep --depth= - --tags --no-tags + --tags --no-tags --all --prune --dry-run " _git_fetch () -- cgit v1.2.3 From c18d5d82b45cb6963b3229556cedd587d94afca9 Mon Sep 17 00:00:00 2001 From: Robert Zeh Date: Tue, 29 Dec 2009 18:58:48 -0600 Subject: Add completion for git-svn mkdirs,reset,and gc Signed-off-by: Robert Zeh Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index fbfa5f25c1..c65462c9ed 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -2022,7 +2022,7 @@ _git_svn () init fetch clone rebase dcommit log find-rev set-tree commit-diff info create-ignore propget proplist show-ignore show-externals branch tag blame - migrate + migrate mkdirs reset gc " local subcommand="$(__git_find_on_cmdline "$subcommands")" if [ -z "$subcommand" ]; then @@ -2069,7 +2069,7 @@ _git_svn () __gitcomp "--stdin $cmt_opts $fc_opts" ;; create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\ - show-externals,--*) + show-externals,--*|mkdirs,--*) __gitcomp "--revision=" ;; log,--*) @@ -2106,6 +2106,9 @@ _git_svn () --no-auth-cache --username= " ;; + reset,--*) + __gitcomp "--revision= --parent" + ;; *) COMPREPLY=() ;; -- cgit v1.2.3 From 4cc47382dfc2a71257934042f6c009287e101cb3 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 30 Dec 2009 19:04:38 -0800 Subject: bash completion: add space between branch name and status flags Improve the readability of the bash prompt by adding a space between the branch name and the status flags (dirty, stash, untracked). While we are cleaning up this section of code, the two cases for formatting the prompt are identical except for the format string, so make them the same. Suggested-by: Roman Fietze Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c65462c9ed..3a6498c04b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -163,11 +163,8 @@ __git_ps1 () fi fi - if [ -n "${1-}" ]; then - printf "$1" "$c${b##refs/heads/}$w$i$s$u$r" - else - printf " (%s)" "$c${b##refs/heads/}$w$i$s$u$r" - fi + local f="$w$i$s$u" + printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r" fi } -- cgit v1.2.3 From cf6e7ba1c4c29fda3f16bfd4a4f1995ed45082d5 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Thu, 31 Dec 2009 12:48:41 +0100 Subject: bash completion: factor submodules into dirty state In the implementation of GIT_PS1_SHOWDIRTYSTATE in 738a94a (bash: offer to show (un)staged changes, 2009-02-03), I cut&pasted the git-diff invocations from dirty-worktree checks elsewhere, carrying along the --ignore-submodules option. As pointed out by Kevin Ballard, this doesn't really make sense: to the _user_, a changed submodule counts towards uncommitted changes. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7cf8557468..4cb89a1256 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -148,11 +148,9 @@ __git_ps1 () elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then - git diff --no-ext-diff --ignore-submodules \ - --quiet --exit-code || w="*" + git diff --no-ext-diff --quiet --exit-code || w="*" if git rev-parse --quiet --verify HEAD >/dev/null; then - git diff-index --cached --quiet \ - --ignore-submodules HEAD -- || i="+" + git diff-index --cached --quiet HEAD -- || i="+" else i="#" fi -- cgit v1.2.3 From 0def5b6ed4ffbc2cced3206acd6359178f7b8c5d Mon Sep 17 00:00:00 2001 From: Bart Trojanowski Date: Fri, 8 Jan 2010 19:54:39 -0500 Subject: hg-to-git: fix COMMITTER type-o This script passes the author and committer to git-commit via environment variables, but it was missing the seccond T of COMMITTER in a few places. Signed-off-by: Bart Trojanowski Acked-by: Stelian Pop Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 2a6839d81e..854cd94ba5 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -59,14 +59,14 @@ def getgitenv(user, date): elems = re.compile('(.*?)\s+<(.*)>').match(user) if elems: env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1) - env += 'export GIT_COMMITER_NAME="%s" ;' % elems.group(1) + env += 'export GIT_COMMITTER_NAME="%s" ;' % elems.group(1) env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2) - env += 'export GIT_COMMITER_EMAIL="%s" ;' % elems.group(2) + env += 'export GIT_COMMITTER_EMAIL="%s" ;' % elems.group(2) else: env += 'export GIT_AUTHOR_NAME="%s" ;' % user - env += 'export GIT_COMMITER_NAME="%s" ;' % user + env += 'export GIT_COMMITTER_NAME="%s" ;' % user env += 'export GIT_AUTHOR_EMAIL= ;' - env += 'export GIT_COMMITER_EMAIL= ;' + env += 'export GIT_COMMITTER_EMAIL= ;' env += 'export GIT_AUTHOR_DATE="%s" ;' % date env += 'export GIT_COMMITTER_DATE="%s" ;' % date -- cgit v1.2.3 From 8cc5b29065e19267cbc08b39c34674b02c2e3d59 Mon Sep 17 00:00:00 2001 From: Avery Pennarun Date: Wed, 25 Nov 2009 21:23:55 -0500 Subject: git merge -X