Codebase list easygit / 336b012
Merge tag 'upstream/1.7.5.2+debian1' into debian/master Boyuan Yang 2 years ago
2 changed file(s) with 1345 addition(s) and 891 deletion(s). Raw diff Collapse all Expand all
00 #
1 # bash completion support for easy GIT.
1 # bash completion support for easy Git.
22 #
33 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
4 # Copyright (C) 2008 Elijah Newren <newren gmail com>
4 # Copyright (C) 2008-2010 Elijah Newren <newren@gmail.com>
55 # *Heavily* based on git-completion.sh
66 # Distributed under the GNU General Public License, version 2.0.
77 #
8888
8989 _eg_commit ()
9090 {
91 local cur="${COMP_WORDS[COMP_CWORD]}"
92 case "$cur" in
93 --*)
94 __gitcomp "
95 --all-tracked --bypass-untracked-check --staged --dirty
96 --author= --signoff --verify --no-verify
97 --edit --amend --include --only
98 "
99 return
100 esac
101 COMPREPLY=()
91 __git_has_doubledash && return
92
93 local cur="${COMP_WORDS[COMP_CWORD]}"
94 case "$cur" in
95 --cleanup=*)
96 __gitcomp "default strip verbatim whitespace
97 " "" "${cur##--cleanup=}"
98 return
99 ;;
100 --reuse-message=*)
101 __gitcomp "$(__git_refs)" "" "${cur##--reuse-message=}"
102 return
103 ;;
104 --reedit-message=*)
105 __gitcomp "$(__git_refs)" "" "${cur##--reedit-message=}"
106 return
107 ;;
108 --untracked-files=*)
109 __gitcomp "all no normal" "" "${cur##--untracked-files=}"
110 return
111 ;;
112 --*)
113 __gitcomp "
114 --all-tracked --bypass-untracked-check --staged --dirty
115 --author= --signoff --verify --no-verify
116 --edit --amend --include --only
117 "
118 return
119 esac
120 COMPREPLY=()
102121 }
103122
104123 _eg_diff ()
148167
149168 _eg_reset ()
150169 {
151 local cur="${COMP_WORDS[COMP_CWORD]}"
152 case "$cur" in
153 --*)
154 __gitcomp "--working-copy --no-unstaging --mixed --hard --soft"
155 return
156 ;;
157 esac
158 __gitcomp "$(__git_refs)"
170 __git_has_doubledash && return
171
172 local cur="${COMP_WORDS[COMP_CWORD]}"
173 case "$cur" in
174 --*)
175 __gitcomp "--working-copy --no-unstaging --merge --mixed --hard --soft --patch"
176 return
177 ;;
178 esac
179 __gitcomp "$(__git_refs)"
159180 }
160181
161182 _eg_revert ()
170191 __git_complete_file
171192 }
172193
194 _eg_track ()
195 {
196 local cur="${COMP_WORDS[COMP_CWORD]}"
197 case "$cur" in
198 --*)
199 __gitcomp "--show --show-all --unset"
200 return
201 ;;
202 esac
203 __git_complete_remote_or_refspec
204 }
205
173206 _eg ()
174207 {
175208 local i c=1 command __git_dir
209
210 local cur words cword prev
211 _get_comp_words_by_ref -n =: cur words cword prev
176212
177213 while [ $c -lt $COMP_CWORD ]; do
178214 i="${COMP_WORDS[c]}"
187223 c=$((++c))
188224 done
189225
190 if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
226 if [ -z "$command" ]; then
191227 case "${COMP_WORDS[COMP_CWORD]}" in
192 --*=*) COMPREPLY=() ;;
193228 --*) __gitcomp "
194229 --debug
195230 --translate
231 --paginate
196232 --no-pager
197233 --git-dir=
198234 --bare
199235 --version
200236 --exec-path
237 --work-tree=
238 --help
201239 "
202240 ;;
203241 *) __gitcomp "$(__eg_commands) $(__git_aliases)" ;;
257295 svn) _git_svn ;;
258296 switch) _git_checkout ;;
259297 tag) _git_tag ;;
298 track) _eg_track ;;
260299 # unstage) COMPREPLY=() ;; ## Already handled by default case
261300 whatchanged) _git_log ;;
262301 *) COMPREPLY=() ;;
263302 esac
264303 }
265304
266 complete -o default -o nospace -F _eg eg
305 complete -o bashdefault -o default -o nospace -F _eg eg 2> /dev/null \
306 || complete -o default -o nospace -F _eg eg
+1281
-867
eg less more
00 #!/usr/bin/perl -w
11
22 ## Easy GIT (eg), a frontend for git designed for former cvs and svn users
3 ## Version .99
4 ## Copyright 2008, 2009 by Elijah Newren, and others
3 ## Version 1.7.5.2
4 ## Copyright 2008-2013 by Elijah Newren and others
55 ## Licensed under GNU GPL, version 2.
66
77 ## To use eg, simply stick this file in your path. Then fire off an
1010 ## to get a comparison to svn in terms of capabilities and commands.
1111 ## Webpage for eg: http://www.gnome.org/~newren/eg
1212
13 use strict;
14 use warnings;
15
1316 package main;
1417
15 use warnings;
1618 use Getopt::Long;
1719 use Cwd qw(getcwd abs_path);
1820 use List::Util qw(max);
1921 use File::Temp qw/ tempfile /;
2022
2123 # configurables
22 my $debug=0;
24 my $DEBUG = $ENV{EASYGIT_DEBUG} || 0;
25 my $GIT_CMD = $ENV{EASYGIT_GIT_CMD} || "git"; # Includes any global args, thus "git --exec-path=..."
26 my $USE_PAGER = exists($ENV{EASYGIT_USE_PAGER}) ? $ENV{EASYGIT_USE_PAGER} : -1;
2327
2428 # globals :-(
25 my $outfh;
26 my $version = ".99";
27 my $eg_exec = abs_path($0);
28 my $git_cmd = "git"; # Includes any global args, thus "git --exec-path=..."
29 my $use_pager = -1;
30 my %command; # command=>{section, short_description} mapping
31 my $section = {
29 my $OUTFH;
30 my $VERSION = "1.7.5.2";
31 my $EG_EXEC = abs_path($0);
32 my %ALIAS;
33 my %COMMAND; # command=>{section, short_description} mapping
34 my $SECTION = {
3235 'creation' =>
3336 { order => 1,
3437 desc => 'Creating repositories',
6568 desc => 'Miscellaneous'
6669 },
6770 };
68 my ($curdir, $topdir, $gitdir);
71 my ($CURDIR, $TOPDIR, $GITDIR);
6972
7073 ## Commands to list in help even though we haven't overridden the git versions
7174 ## (yet, in most cases)
7275 INIT {
73 %command = (
76 %COMMAND = (
7477 blame => {
7578 unmodified_help => 1,
7679 unmodified_behavior => 1,
132135 # Most commands must be run inside a git working directory
133136 unless (!$self->{git_repo_needed} || (@ARGV > 0 && $ARGV[0] eq "--help")) {
134137 $self->{git_dir} = RepoUtil::git_dir();
135 die "Must be run inside a git repository!\n" if !defined $self->{git_dir};
138 if (!defined $self->{git_dir}) {
139 # Couldn't find git repository. That could be for any of three reasons:
140
141 # 1) Is 'git' even in the user's PATH?
142 my @paths_to_git = grep {-x "$_/git"} split(/:/, $ENV{PATH});
143 if (!@paths_to_git) {
144 die "Error: Cannot find git in your PATH!\n";
145 }
146
147 # 2) Does 'git' even work (possible bad installation)?
148 my ($ret, $output) =
149 ExecUtil::execute_captured("$GIT_CMD --version", ignore_ret => 1);
150 if ($ret != 0 || $output !~ /^git version .*$/) {
151 die "Error: Cannot execute git (check your git installation)!\n";
152 }
153
154 # 3) They just aren't in a project tracked by git
155 die "Error: Must be run inside a git-tracked working directory!\n";
156 }
136157 }
137158
138159 # Many commands do not work if no commit has yet been made
154175 $git_equiv =~ s/_/-/; # Packages use underscores, commands use dashes
155176
156177 if ($package_name eq "subcommand") {
157 exit ExecUtil::execute("$git_cmd $self->{command} --help")
178 exit ExecUtil::execute("$GIT_CMD $self->{command} --help")
158179 }
159180
160181 $ENV{"LESS"} = "FRSX" unless defined $ENV{"LESS"};
161 my $less = ($use_pager == 1) ? "less" :
162 ($use_pager == 0) ? "cat" :
163 `$git_cmd config core.pager` || "less";
182 my $less = ($USE_PAGER == 1) ? "less" :
183 ($USE_PAGER == 0) ? "cat" :
184 `$GIT_CMD config core.pager` || "less";
164185 chomp($less);
165186 open(OUTPUT, "| $less");
166 print OUTPUT "$package_name: $command{$package_name}{about}\n";
187 print OUTPUT "$package_name: $COMMAND{$package_name}{about}\n";
167188 print OUTPUT $self->{'help'};
168189 print OUTPUT "\nDifferences from git $package_name:";
169190 print OUTPUT "\n None.\n" if !defined $self->{'differences'};
171192 if ($git_equiv) {
172193 print OUTPUT "\nSee also\n";
173194 print OUTPUT <<EOF;
174 Run 'man git-$git_equiv' for a comprehensive list of options available.
195 Run 'git help $git_equiv' for a comprehensive list of options available.
175196 eg $package_name is designed to accept the same options as git $git_equiv, and
176197 with the same meanings unless specified otherwise in the above
177198 "Differences" section.
196217 $package_name eq "subcommand" ? $self->{'command'} : $package_name;
197218
198219 @ARGV = Util::quote_args(@ARGV);
199 return ExecUtil::execute("$git_cmd $subcommand @ARGV", ignore_ret => 1);
220 return ExecUtil::execute("$GIT_CMD $subcommand @ARGV", ignore_ret => 1);
200221 }
201222
202223 ###########################################################################
205226 package add;
206227 @add::ISA = qw(subcommand);
207228 INIT {
208 $command{add} = {
229 $COMMAND{add} = {
209230 unmodified_behavior => 1,
210231 section => 'compatibility',
211232 about => 'Mark content in files as being ready for commit'
230251 package apply;
231252 @apply::ISA = qw(subcommand);
232253 INIT {
233 $command{apply} = {
254 $COMMAND{apply} = {
234255 about => 'Apply a patch in a git repository'
235256 };
236257 }
279300
280301 sub preprocess {
281302 my $self = shift;
282 my $package_name = ref($self);
283303
284304 my $result = main::GetOptions("--help" => sub { $self->help() });
285305 foreach my $i (0..$#ARGV) {
293313 package branch;
294314 @branch::ISA = qw(subcommand);
295315 INIT {
296 $command{branch} = {
316 $COMMAND{branch} = {
297317 section => 'projects',
298318 about => 'List, create, or delete branches'
299319 };
320 $ALIAS{'br'} = "branch";
300321 }
301322
302323 sub new {
363384
364385 sub run {
365386 my $self = shift;
366 my $package_name = ref($self);
367387
368388 my $switch = 0;
369389 if (scalar(@ARGV) > 1 && $ARGV[0] eq "-s") {
372392 }
373393
374394 @ARGV = Util::quote_args(@ARGV);
375 my $ret = ExecUtil::execute("$git_cmd branch @ARGV", ignore_ret => 1);
376 $ret = ExecUtil::execute("$git_cmd checkout $ARGV[0]", ignore_ret => 1)
395 my $ret = ExecUtil::execute("$GIT_CMD branch @ARGV", ignore_ret => 1);
396 $ret = ExecUtil::execute("$GIT_CMD checkout $ARGV[0]", ignore_ret => 1)
377397 if ($switch && $ret == 0);
378398 return $ret;
379399 }
384404 package bundle;
385405 @bundle::ISA = qw(subcommand);
386406 INIT {
387 $command{bundle} = {
407 $COMMAND{bundle} = {
388408 extra => 1,
389409 section => 'collaboration',
390410 about => 'Pack repository updates (or whole repository) into a file'
489509
490510 sub preprocess {
491511 my $self = shift;
492 my $package_name = ref($self);
493512
494513 #
495514 # Parse options
520539 die "$oldname does not exist.\n" if ! -f $oldname;
521540
522541 my ($retval, $output) =
523 ExecUtil::execute_captured("$git_cmd bundle list-heads $oldname");
542 ExecUtil::execute_captured("$GIT_CMD bundle list-heads $oldname");
524543 my @lines = split '\n', $output;
525544
526545 my @refs = map { m#^([0-9a-f]+)# && "^$1" } @lines;
538557 package cat;
539558 @cat::ISA = qw(subcommand);
540559 INIT {
541 $command{cat} = {
560 $COMMAND{cat} = {
542561 new_command => 1,
543562 extra => 1,
544563 section => 'compatibility',
592611
593612 sub preprocess {
594613 my $self = shift;
595 my $package_name = ref($self);
596614
597615 my $result=main::GetOptions("--help" => sub { $self->help() });
598616
616634
617635 sub run {
618636 my $self = shift;
619 my $package_name = ref($self);
620637
621638 @ARGV = Util::quote_args(@ARGV);
622 return ExecUtil::execute("$git_cmd show @ARGV", ignore_ret => 1);
639 return ExecUtil::execute("$GIT_CMD show @ARGV", ignore_ret => 1);
623640 }
624641
625642 ###########################################################################
628645 package changes;
629646 @changes::ISA = qw(subcommand);
630647 INIT {
631 $command{changes} = {
648 $COMMAND{changes} = {
632649 new_command => 1,
633650 section => 'misc',
634651 about => 'Provide an overview of the changes from git to eg'
667684
668685 sub run {
669686 my $self = shift;
670 my $package_name = ref($self);
671
672 if ($debug == 2) {
687
688 if ($DEBUG == 2) {
673689 print " >>(No commands to run, just data to print)<<\n";
674690 return;
675691 }
678694 my $indent = " ";
679695 my $header_indent = "";
680696 $ENV{"LESS"} = "FRSX" unless defined $ENV{"LESS"};
681 my $less = ($use_pager == 1) ? "less" :
682 ($use_pager == 0) ? "cat" :
683 `$git_cmd config core.pager` || "less";
697 my $less = ($USE_PAGER == 1) ? "less" :
698 ($USE_PAGER == 0) ? "cat" :
699 `$GIT_CMD config core.pager` || "less";
684700 chomp($less);
685701 open(OUTPUT, "| $less");
686702
690706 $header_indent = " ";
691707 }
692708 print OUTPUT "${header_indent}Modified Behavior:\n";
693 foreach my $c (sort keys %command) {
694 next if $command{$c}{unmodified_behavior};
695 next if $command{$c}{new_command};
709 foreach my $c (sort keys %COMMAND) {
710 next if $COMMAND{$c}{unmodified_behavior};
711 next if $COMMAND{$c}{new_command};
696712 print OUTPUT "$indent$c\n";
697713 }
698714 print OUTPUT "${header_indent}New commands:\n";
699 foreach my $c (sort keys %command) {
700 next if !$command{$c}{new_command};
715 foreach my $c (sort keys %COMMAND) {
716 next if !$COMMAND{$c}{new_command};
701717 print OUTPUT "$indent$c\n";
702718 }
703719 print OUTPUT "${header_indent}Modified Help Only:\n";
704 foreach my $c (sort keys %command) {
705 next if $command{$c}{unmodified_help};
706 next if !$command{$c}{unmodified_behavior};
707 next if $command{$c}{new_command};
720 foreach my $c (sort keys %COMMAND) {
721 next if $COMMAND{$c}{unmodified_help};
722 next if !$COMMAND{$c}{unmodified_behavior};
723 next if $COMMAND{$c}{new_command};
708724 print OUTPUT "$indent$c\n";
709725 }
710726
711727 if ($self->{details}) {
712 foreach my $c (sort keys %command) {
713 next if $command{$c}{unmodified_help} || $command{$c}{unmodified_behavior};
714
728 foreach my $c (sort keys %COMMAND) {
729 next if $COMMAND{$c}{unmodified_help} || $COMMAND{$c}{unmodified_behavior};
730
731 my $real_c = $c;
715732 $c =~ s/-/_/; # Packages use underscores, commands use dashes
716733 next if !$c->can("new");
717734 my $obj = $c->new(initial_commit_error_msg => '');
718735
719 print OUTPUT "Changes to $c:\n";
736 print OUTPUT "Changes to $real_c:\n";
720737 if ($obj->{differences}) {
721738 $obj->{differences} =~ s/^\n//;
722739 print OUTPUT $obj->{differences};
734751 package checkout;
735752 @checkout::ISA = qw(subcommand);
736753 INIT {
737 $command{checkout} = {
754 $COMMAND{checkout} = {
738755 section => 'compatibility',
739756 about => 'Compatibility wrapper for clone/switch/revert'
740757 };
807824 return $self;
808825 }
809826
810 sub _looks_like_git_repo {
827 sub _looks_like_git_repo ($) {
811828 my $path = shift;
812829
813830 my $clone_protocol = qr#^(?:git|ssh|http|https|rsync)://#;
832849
833850 sub preprocess {
834851 my $self = shift;
835 my $package_name = ref($self);
836852
837853 if (scalar(@ARGV) > 0 && $ARGV[0] ne "--") {
838854 main::GetOptions("--help" => sub { $self->help() });
863879 $self->{git_dir} = RepoUtil::git_dir();
864880 die "Must be run inside a git repository!\n" if !defined $self->{git_dir};
865881
866 return ExecUtil::execute("$git_cmd checkout @ARGV", ignore_ret => 1);
882 return ExecUtil::execute("$GIT_CMD checkout @ARGV", ignore_ret => 1);
867883 } else {
868884 die "Did you mean to run\n eg clone @ARGV\n?\n";
869885 }
875891 package cherry_pick;
876892 @cherry_pick::ISA = qw(subcommand);
877893 INIT {
878 $command{"cherry-pick"} = {
894 $COMMAND{"cherry-pick"} = {
879895 extra => 1,
880896 section => 'modification',
881897 about => 'Apply (or reverse) a commit, usually from another branch'
962978
963979 sub preprocess {
964980 my $self = shift;
965 my $package_name = ref($self);
966981
967982 my ($reverse, $dash_x, $mainline) = (0, 0, -1);
968983 Getopt::Long::Configure("permute"); # Allow unrecognized options through
983998
984999 @ARGV = Util::quote_args(@ARGV);
9851000 if ($self->{reverse}) {
986 return ExecUtil::execute("$git_cmd revert @ARGV", ignore_ret => 1);
1001 return ExecUtil::execute("$GIT_CMD revert @ARGV", ignore_ret => 1);
9871002 } else {
988 return ExecUtil::execute("$git_cmd cherry-pick @ARGV", ignore_ret => 1);
1003 return ExecUtil::execute("$GIT_CMD cherry-pick @ARGV", ignore_ret => 1);
9891004 }
9901005 }
9911006
9951010 package clone;
9961011 @clone::ISA = qw(subcommand);
9971012 INIT {
998 $command{clone} = {
1013 $COMMAND{clone} = {
9991014 section => 'creation',
10001015 about => 'Clone a repository into a new directory'
10011016 };
10361051 --depth DEPTH
10371052 Only download the DEPTH most recent commits instead of all history
10381053 ";
1039 $self->{'differences'} = "
1040 eg clone and git clone are very similar, but eg clone by default sets up
1041 a branch for each remote branch automatically (instead of only creating
1042 one branch, typically master).
1043 ";
10441054 return $self;
1045 }
1046
1047 sub preprocess {
1048 my $self = shift;
1049 my $package_name = ref($self);
1050
1051 $self->{bare} = 0;
1052 my @old_argv = @ARGV;
1053 Getopt::Long::Configure("permute");
1054 my $result = main::GetOptions(
1055 "help" => sub { $self->help() },
1056 "bare" => sub { $self->{bare} = 1 },
1057 "depth=i",
1058 "origin|o=s",
1059 "reference=s",
1060 "upload-pack|u=s",
1061 );
1062 shift @ARGV while ($ARGV[0] =~ /^-/); # Skip past any other options
1063 $self->{repository} = shift @ARGV;
1064 die "No repository specified!\n" if !$self->{repository};
1065 my $basename = $self->{repository};
1066 $basename =~ s#/*$##; # Remove trailing slashes
1067 $basename =~ s#.*[/:]##; # Remove everything but final dirname
1068 $basename =~ s#\.git$##; # Remote .git suffix, if present
1069 $basename =~ s#\.bundle$##; # Remote .bundle suffix, if present
1070 $self->{directory} = shift @ARGV || $basename . ($self->{bare} ? ".git" : "");
1071 die "Too many parameters to clone!\n" if (scalar(@ARGV) > 0);
1072
1073 @ARGV = @old_argv; # Workaround: GetOptions may have stripped a leading '--'
1074 }
1075
1076 sub run {
1077 my $self = shift;
1078
1079 #
1080 # Perform the clone
1081 #
1082 @ARGV = Util::quote_args(@ARGV);
1083 my $ret = ExecUtil::execute("$git_cmd clone @ARGV", ignore_ret => 1);
1084 return $ret if $self->{bare};
1085 if ($debug == 2) {
1086 return if $self->{bare};
1087 print " >>Running: 'cd $self->{directory}'<<\n";
1088 print " >>Running: '$git_cmd branch -r'<<\n";
1089 print " --- Setting up extra branches by default ---\n";
1090 print " >>Running, for each remote branch besides master (referred to as BRANCH):\n";
1091 print " $git_cmd branch BRANCH origin/BRANCH\n";
1092 } elsif ($ret == 0) {
1093 # Switch to the appropriate directory, remembering the repository we
1094 # checked out
1095 die "$self->{directory} does not exist after checkout!"
1096 if ! -d $self->{directory};
1097 $self->{repository} = main::abs_path($self->{repository})
1098 if -d $self->{repository};
1099 chdir($self->{directory});
1100
1101 # Determine local and remote branches
1102 my @remote_branches =
1103 split('\n', `$git_cmd for-each-ref --format '%(refname)' refs/remotes`);
1104 @remote_branches = map { m#^refs/remotes/(.*)$# && $1 } @remote_branches;
1105 my @local_branches =
1106 split('\n', `$git_cmd for-each-ref --format '%(refname)' refs/heads`);
1107 @local_branches = map { m#^refs/heads/(.*)$# && $1 } @local_branches;
1108
1109 # Set branch.@local_branches.rebase to true if branch.autosetuprebse is true
1110 my $autosetuprebase = `$git_cmd config --global branch.autosetuprebase`;
1111 chomp($autosetuprebase);
1112 if ($autosetuprebase eq 'always' || $autosetuprebase eq 'remote') {
1113 foreach my $branch (@local_branches) {
1114 ExecUtil::execute("$git_cmd config branch.$branch.rebase true");
1115 }
1116 }
1117
1118 # Set up a branch for each remote branch, not just master
1119 foreach my $b (@remote_branches) {
1120 ($remote, $branch) = ($b =~ m#^(.*)/(.*?)$#);
1121 next if $branch eq "HEAD";
1122 next if grep {$branch eq $_} @local_branches;
1123 ExecUtil::execute("$git_cmd branch $branch $remote/$branch > /dev/null");
1124 }
1125 }
1126
1127 return $ret;
11281055 }
11291056
11301057 ###########################################################################
11331060 package commit;
11341061 @commit::ISA = qw(subcommand);
11351062 INIT {
1136 $command{commit} = {
1063 $COMMAND{commit} = {
11371064 section => 'modification',
11381065 about => 'Record changes locally'
11391066 };
1140 $alias{'checkin'} = "commit";
1141 $alias{'ci'} = "commit";
1067 $ALIAS{'checkin'} = "commit";
1068 $ALIAS{'ci'} = "commit";
11421069 }
11431070
11441071 sub new {
11481075 $self->{'help'} = "
11491076 Usage:
11501077 eg commit [-a|--all-known] [-b|--bypass-unknown-check]
1151 [--staged|-d|--dirty] [-F FILE | -m MSG | --amend] [--]
1078 [--staged|-d|--dirty] [-F FILE | -m MSG] [--amend] [--]
11521079 [FILE...]
11531080
11541081 Description:
12781205 my $record_arg = sub { push(@{$self->{args}}, "$_[0]$_[1]"); };
12791206 my $record_args = sub { push(@{$self->{args}}, "$_[0]$_[1]");
12801207 push(@{$self->{args}}, splice(@_, 2)); };
1281 my ($all_known, $bypass_unknown, $staged, $amend) = (0, 0, 0, 0);
1208 my ($all_known, $bypass_unknown, $staged, $amend,
1209 $dry_run, $allow_empty, $include) = (0, 0, 0, 0, 0, 0, 0);
12821210 Getopt::Long::Configure("permute");
12831211 my $result = main::GetOptions(
1284 "--help" => sub { $self->help() },
1212 "--help|h" => sub { $self->help() },
12851213 "all-known|a" => \$all_known,
12861214 "bypass-unknown-check|b" => \$bypass_unknown,
12871215 "staged|dirty|d" => \$staged,
1216 "dry-run" => sub { $dry_run = 1,
1217 &$record_arg("--", @_) },
12881218 "s" => sub { &$record_arg("-", @_) },
12891219 "v" => sub { &$record_arg("-", @_) },
12901220 "u" => sub { &$record_arg("-", @_) },
12941224 "file=s" => sub { &$record_args("--", @_) },
12951225 "m=s" => sub { &$record_args("-", @_) },
12961226 "amend" => sub { $amend = 1; &$record_arg("--", @_) },
1297 "allow-empty" => sub { &$record_arg("--", @_) },
1227 "allow-empty" => sub { $allow_empty = 1;
1228 &$record_arg("--", @_) },
1229 "interactive" => sub { $allow_empty = 1;
1230 &$record_arg("--", @_) },
12981231 "no-verify" => sub { &$record_arg("--", @_) },
12991232 "e" => sub { &$record_arg("-", @_) },
13001233 "author=s" => sub { &$record_args("--", @_) },
13011234 "cleanup=s" => sub { &$record_args("--", @_) },
1235 "include|i=s" => sub { $include = 1;
1236 &$record_args("--", @_) },
13021237 );
1303 my ($opts, $revs, $files) = RepoUtil::parse_args(@ARGV);
1238 my ($opts, $revs, $files) = RepoUtil::parse_args([], @ARGV);
13041239
13051240 #
13061241 # Set up flags based on options, do sanity checking of options
13071242 #
1308 my ($check_unknown, $check_mixed, $check_no_branch);
1243 my ($check_no_changes, $check_unknown, $check_mixed, $check_unmerged);
1244 my $skip_all = $include || $dry_run;
13091245 $self->{'commit_flags'} = [];
13101246 die "Cannot specify both --all-known (-a) and --staged (-d)!\n" if
13111247 $all_known && $staged;
13121248 die "Cannot specify --staged when specifying files!\n" if @$files && $staged;
1313 $check_unknown = !$bypass_unknown && !$staged && !@$files;
1314 $check_mixed = !$all_known && !$staged && !@$files;
1249 $check_no_changes = !$amend && !$allow_empty && !$skip_all;
1250 $check_unknown = !$bypass_unknown && !$staged && !@$files && !$skip_all;
1251 $check_mixed = !$all_known && !$staged && !@$files && !$skip_all;
1252 $check_unmerged = !$skip_all;
13151253 push(@{$self->{'commit_flags'}}, "-a") if $all_known;
13161254
13171255 #
13191257 #
13201258 my $status =
13211259 RepoUtil::commit_push_checks($package_name,
1322 {no_changes => 1,
1260 {no_changes => $check_no_changes,
13231261 unknown => $check_unknown,
1324 partially_staged => $check_mixed});
1325
1326 if ($amend && !$all_known && !$staged && !@$files &&
1262 partially_staged => $check_mixed,
1263 unmerged_changes => $check_unmerged});
1264
1265 if ($amend && !$all_known && !$staged && !$skip_all && !@$files &&
13271266 $status->{has_unstaged_changes} && !$status->{has_staged_changes}) {
13281267 print STDERR <<EOF;
13291268 Aborting: It is not clear whether you want to simply amend the commit
13361275 }
13371276
13381277 die "No staged changes, but --staged given.\n"
1339 if (!$status->{has_staged_changes} && $staged && !$amend);
1340
1341 if (!$all_known && !$staged &&
1278 if (!$status->{has_staged_changes} && $staged && !$amend && !$dry_run);
1279
1280 if (!$all_known && !$include && !$staged &&
13421281 $status->{has_unstaged_changes} && !$status->{has_staged_changes} &&
13431282 !@$files) {
13441283 push(@{$self->{'commit_flags'}}, "-a");
13621301 package config;
13631302 @config::ISA = qw(subcommand);
13641303 INIT {
1365 $command{config} = {
1304 $COMMAND{config} = {
13661305 unmodified_behavior => 1,
13671306 extra => 1,
13681307 section => 'misc',
13831322 Description:
13841323 Gets or sets configuration options.
13851324
1386 See the 'Configuration File' section of 'man git-config' for a fairly
1325 See the 'Configuration File' section of 'git help config' for a fairly
13871326 comprehensive list of special options used by eg (and git).
13881327
13891328 Examples:
14101349 package diff;
14111350 @diff::ISA = qw(subcommand);
14121351 INIT {
1413 $command{diff} = {
1352 $COMMAND{diff} = {
14141353 section => 'discovery',
14151354 about => 'Show changes to file contents'
14161355 };
15071446
15081447 The .. operator of git diff (e.g. git diff master..devel) means what
15091448 the ... operator of git log means, and vice-versa. This causes lots of
1510 confusion. We fix this by aliasing making the .. operator of eg diff
1449 confusion. We fix this by making the .. operator of eg diff
15111450 do exactly what the ... operator of git diff does. To see why:
15121451
15131452 Meanings of git commands, as a reminder (A and B are revisions):
15141453 git diff A..B <=> git diff A B # Endpoint difference
1515 git diff A...B <=> git diff $(git merge-base A B) B
1454 git diff A...B <=> git diff $(git merge-base A B) B # Changes from base
15161455
15171456 Why this is confusing (compare to above):
1518 git log A..B <=> git log $(git merge-base A B) B
1519 git log A...B <=> git log ^$(git merge-base A B) A B # Endpoint difference
1457 git log A..B <=> git log ^$(git merge-base A B) B # Changes from base
1458 git log A...B <=> git log A B ^$(git merge-base A B) # Endpoint difference
15201459
15211460 So, my translation:
15221461 eg diff A B <=> git diff A B <=> git diff A..B
15441483
15451484 sub preprocess {
15461485 my $self = shift;
1547 my $package_name = ref($self);
15481486
15491487 # Avoid Util::git_rev_parse because it fails on t2010-checkout-ambiguous by
15501488 # treating "--quiet" as a revision rather than an option; use our own
15511489 # parse_args implementation instead.
1552 my ($opts, $revs, $files) = RepoUtil::parse_args(@ARGV);
1490 my ($opts, $revs, $files) = RepoUtil::parse_args(["--extcmd", "-x"], @ARGV);
15531491
15541492 # Replace '..' with '...' in revision specifiers. Use backslash escaping to
15551493 # get actual dots and not just any character. Use negative lookbehind and
15621500 #
15631501 $self->{'opts'} = "";
15641502 @ARGV = @$opts;
1565 my ($staged, $unstaged, $no_index) = (0, 0, 0);
1503 my ($staged, $unstaged, $no_index, $ours, $theirs) = (0, 0, 0, 0, 0);
1504 my $extcmd;
15661505 Getopt::Long::Configure("permute");
15671506 my $result = main::GetOptions(
15681507 "--help" => sub { $self->help() },
15691508 "staged|cached" => \$staged,
15701509 "unstaged" => \$unstaged,
15711510 "no-index" => \$no_index,
1511 "extcmd=s" => \$extcmd,
1512 "ours" => \$ours,
1513 "theirs" => \$theirs,
15721514 );
15731515 die "Cannot specify both --staged and --unstaged!\n" if $staged && $unstaged;
15741516 my @args;
15751517 push(@args, "--cached") if $staged;
15761518 push(@args, "--no-index") if $no_index;
1519 push(@args, "--extcmd", $extcmd) if $extcmd;
1520 push(@args, "--theirs") if $theirs;
1521 push(@args, "--ours") if $ours;
15771522 push(@args, @ARGV);
15781523
15791524 #
15851530 if ($unstaged && scalar @$revs > 0);
15861531 # 'eg diff' (without arguments) should act like 'git diff HEAD', unless
15871532 # we are in an aborted merge state
1588 if (!@$revs && !$unstaged && !$staged && !$no_index) {
1533 if (!@$revs && !$unstaged && !$staged && !$no_index && !$ours && !$theirs) {
15891534 if (-f "$self->{git_dir}/MERGE_HEAD") {
15901535 my @merge_branches = RepoUtil::merge_branches();
15911536 my $list = join(", ", @merge_branches);
16021547 EOF
16031548 exit 1;
16041549 }
1550 if (RepoUtil::initial_commit()) {
1551 print STDERR <<EOF;
1552 Aborting: Cannot show the changes since the last commit, since you do not
1553 yet have any commits on the current branch. Try passing --unstaged to diff,
1554 or making a commit first.
1555 EOF
1556 exit 1;
1557 }
16051558 push(@$revs, "HEAD")
16061559 }
16071560
16121565 }
16131566
16141567 ###########################################################################
1568 # difftool #
1569 ###########################################################################
1570 package difftool;
1571 @difftool::ISA = qw(diff);
1572 INIT {
1573 $COMMAND{difftool} = {
1574 new_command => 1,
1575 extra => 1,
1576 section => 'discovery',
1577 about => 'Show changes to file contents using an external tool'
1578 };
1579 }
1580
1581 sub new {
1582 my $class = shift;
1583 my $self = $class->SUPER::new(git_repo_needed => 1, @_);
1584 bless($self, $class);
1585
1586 $self->{'help'} = "
1587 Usage:
1588 eg difftool [--tool=<tool>] [--unstaged | --staged] [REVISION] [REVISION] [FILE...]
1589
1590 Description:
1591 Shows differences between different versions of the project using an
1592 external tool. By default, it shows the differences between the last
1593 locally recorded version and the version in the working copy.
1594
1595 This command behaves just like 'eg diff'; see 'eg help diff' for more
1596 details.
1597
1598 You can configure the default external tool with 'eg config'. You can
1599 specify the default tool by setting 'diff.tool'; you can configure the
1600 tool by setting 'difftool.<tool>.cmd'. See the 'Configuration File'
1601 section of 'git help config' for more details.
1602
1603 Example:
1604 Show local unrecorded changes in vimdiff
1605 \$ eg difftool --tool=vimdiff
1606
1607 See 'eg help diff' for more diff examples.
1608
1609 ";
1610 $self->{'differences'} = "
1611 git difftool behaves just like git diff, but launches an external tool
1612 instead of using git's built-in machinery. eg diff is quite different from
1613 git diff (see 'eg help diff' for details); eg difftool behaves just like
1614 eg diff.
1615 ";
1616 return $self;
1617 }
1618
1619
1620
1621 ###########################################################################
16151622 # gc #
16161623 ###########################################################################
16171624 package gc;
16181625 @gc::ISA = qw(subcommand);
16191626 INIT {
1620 $command{gc} = {
1627 $COMMAND{gc} = {
16211628 unmodified_behavior => 1,
16221629 extra => 1,
16231630 section => 'timesavers',
16531660 package help;
16541661 @help::ISA = qw(subcommand);
16551662 INIT {
1656 $command{help} = {
1663 $COMMAND{help} = {
16571664 section => 'misc',
16581665 about => 'Get command syntax and examples'
16591666 };
17151722
17161723 sub preprocess {
17171724 my $self = shift;
1718 my $package_name = ref($self);
17191725
17201726 $self->{all} = 0;
17211727 my $result=main::GetOptions("--help" => sub { $self->help() },
17241730
17251731 sub run {
17261732 my $self = shift;
1727 my $package_name = ref($self);
1728
1729 if ($debug == 2) {
1733
1734 if ($DEBUG == 2) {
17301735 print " >>(No commands to run, just data to print)<<\n";
17311736 return;
17321737 }
17331738
17341739 # Check if we were asked to get help on a subtopic rather than toplevel help
17351740 if (@ARGV > 0) {
1736 my $subcommand = shift @ARGV;
1741 my $orig_subcommand = shift @ARGV;
1742 my $subcommand = $orig_subcommand;
17371743 $subcommand =~ s/-/_/; # Packages use underscores, commands use dashes
17381744 if (@ARGV != 0 && ($subcommand ne 'topic' || @ARGV != 1)) {
17391745 die "Too many arguments to help.\n";
17421748 $subcommand = "help::topic" if $subcommand eq 'topic';
17431749
17441750 if (!$subcommand->can("new")) {
1745 print "$subcommand is not modified by eg (eg $subcommand is equivalent" .
1746 " to git $subcommand).\nWill try running 'git help $subcommand'" .
1747 " in 2 seconds...\n";
1751 print "$orig_subcommand is not modified by eg (eg $orig_subcommand is" .
1752 " equivalent to git $orig_subcommand).\nWill try running 'git" .
1753 " help $orig_subcommand' in 2 seconds...\n";
17481754 sleep 2;
1749 exit ExecUtil::execute("$git_cmd help $subcommand");
1755 exit ExecUtil::execute("$GIT_CMD help $orig_subcommand");
17501756 }
17511757
17521758 my $subcommand_obj = $subcommand->new(initial_commit_error_msg => '',
17561762
17571763 # Set up a pager, if wanted
17581764 $ENV{"LESS"} = "FRSX" unless defined $ENV{"LESS"};
1759 my $less = ($use_pager == 1) ? "less" :
1760 ($use_pager == 0) ? "cat" :
1761 `$git_cmd config core.pager` || "less";
1765 my $less = ($USE_PAGER == 1) ? "less" :
1766 ($USE_PAGER == 0) ? "cat" :
1767 `$GIT_CMD config core.pager` || "less";
17621768 chomp($less);
17631769 open(OUTPUT, "| $less");
17641770
17691775
17701776 # Print valid subcommands sorted by section
17711777 foreach my $name (sort
1772 {$section->{$a}{'order'} <=> $section->{$b}{'order'}}
1773 keys %$section) {
1774 next if $section->{$name}{extra} && !$self->{all};
1775 print OUTPUT "$section->{$name}{desc}\n";
1776 foreach my $c (sort keys %command) {
1777 next if !defined $command{$c}{section};
1778 next if $command{$c}{section} ne $name;
1779 next if $command{$c}{extra} && !$self->{all};
1780 printf OUTPUT " eg %-11s %s\n", $c, $command{$c}{about};
1778 {$SECTION->{$a}{'order'} <=> $SECTION->{$b}{'order'}}
1779 keys %$SECTION) {
1780 next if $SECTION->{$name}{extra} && !$self->{all};
1781 print OUTPUT "$SECTION->{$name}{desc}\n";
1782 foreach my $c (sort keys %COMMAND) {
1783 next if !defined $COMMAND{$c}{section};
1784 next if $COMMAND{$c}{section} ne $name;
1785 next if $COMMAND{$c}{extra} && !$self->{all};
1786 printf OUTPUT " eg %-11s %s\n", $c, $COMMAND{$c}{about};
17811787 }
17821788 print OUTPUT "\n";
17831789 }
17841790
17851791 # Check to see if someone added a command with an invalid section
17861792 my $broken_commands = "";
1787 foreach my $c (keys %command) {
1788 next if !defined $command{$c}{section};
1789 next if defined $section->{$command{$c}{section}};
1790 my $tmp = sprintf(" eg %-10s %s\n", $c, $command{$c}{about});
1793 foreach my $c (keys %COMMAND) {
1794 next if !defined $COMMAND{$c}{section};
1795 next if defined $SECTION->{$COMMAND{$c}{section}};
1796 my $tmp = sprintf(" eg %-10s %s\n", $c, $COMMAND{$c}{about});
17911797 $broken_commands .= $tmp;
17921798 }
17931799 if ($broken_commands) {
18641870 without resolving all conflicts, the command will error out and tell you
18651871 that some conflicts remain to be resolved.";
18661872 my $abort_text = "
1867 If you had no uncommitted changes before the merge (or do not care about
1868 keeping those changes), you can run
1869 eg reset --working-copy ORIG_HEAD
1870
1871 If you had uncommitted changes before starting the merge, and have
1872 git-1.6.2 or later, you can try
1873 eg ls-files --unmerged | awk {'print \$4'} | uniq | xargs eg stage
1874 eg reset --merge ORIG_HEAD
1875 The first command will mark all unmerged files as ready for commit (who
1876 doesn't like having conflict markers in their files?), and the second
1877 command undoes all changes to staged files since ORIG_HEAD -- both the
1878 files that were successfully merged by git, and the files that you manually
1879 staged in the first command.";
1873 To abort your merge operation, simply run
1874 eg merge --abort";
18801875 return _conflict_resolution_message(op => "merge",
18811876 show_empty_case => 0,
18821877 continue_text => $completion_text,
19321927 );
19331928 }
19341929
1935 sub _conflict_resolution_message {
1930 sub _conflict_resolution_message (%) {
19361931 my $opts = {op => "!!!FIXME!!!",
19371932 show_empty_case => 0,
19381933 extra_stop_info => '',
20522047 100644 -- Normal, non-executable file
20532048 100755 -- File with the executable bit set
20542049 120000 -- symlink
2055 160000 -- A git submodule (run 'man git-submodule' for more info)
2050 160000 -- A git submodule (run 'git help submodule' for more info)
20562051
20572052 ******************** Tell git to continue the operation ********************
20582053 $opts->{continue_text}
20852080
20862081 eg bisect reset
20872082
2088 See 'man git-bisect' for more details."
2083 See 'git help bisect' for more details."
20892084 }
20902085
20912086 sub refspecs {
24492444 eg stage -p
24502445 (You will be asked whether to stage each change, listed in diff format;
24512446 the main options to know are \"y\" for yes, \"n\" for no, and \"s\" for
2452 splitting the selected change into smaller changes; see 'man git-add' for
2447 splitting the selected change into smaller changes; see 'git help add' for
24532448 more details).
24542449
24552450 Get all unstaged changes to bar.C and foo.pl
26962691 }
26972692
26982693 $ENV{"LESS"} = "FRSX" unless defined $ENV{"LESS"};
2699 my $less = ($use_pager == 1) ? "less" :
2700 ($use_pager == 0) ? "cat" :
2701 `$git_cmd config core.pager` || "less";
2694 my $less = ($USE_PAGER == 1) ? "less" :
2695 ($USE_PAGER == 0) ? "cat" :
2696 `$GIT_CMD config core.pager` || "less";
27022697 chomp($less);
27032698 open(OUTPUT, "| $less");
27042699 print OUTPUT "$topic\n";
27142709 package info;
27152710 @info::ISA = qw(subcommand);
27162711 INIT {
2717 $command{info} = {
2712 $COMMAND{info} = {
27182713 new_command => 1,
27192714 section => 'discovery',
27202715 extra => 1,
28102805 if ($path) {
28112806 die "$path does not look like a directory.\n" if ! -d $path;
28122807 my ($ret, $useless_output) =
2813 ExecUtil::execute_captured("$git_cmd ls-remote $path", ignore_ret => 1);
2808 ExecUtil::execute_captured("$GIT_CMD ls-remote $path", ignore_ret => 1);
28142809 if ($ret != 0) {
28152810 die "$path does not appear to be a git archive " .
28162811 "(maybe it has no commits yet?).\n";
28322827 # Special case the situation of no commits being present
28332828 #
28342829 if (RepoUtil::initial_commit()) {
2835 if ($debug < 2) {
2830 if ($DEBUG < 2) {
28362831 print STDERR <<EOF;
28372832 Total commits: 0
28382833 Local repository: $self->{git_dir}
28482843 #
28492844
28502845 # total commits
2851 my $total_commits = ExecUtil::output("$git_cmd rev-list --all | wc -l");
2852 print "Total commits: $total_commits\n" if $debug < 2;
2846 my $total_commits = ExecUtil::output("$GIT_CMD rev-list --all | wc -l");
2847 print "Total commits: $total_commits\n" if $DEBUG < 2;
28532848
28542849 # local repo
2855 print "Local repository: $self->{git_dir}\n" if $debug < 2;
2850 print "Local repository: $self->{git_dir}\n" if $DEBUG < 2;
28562851
28572852 # named remote repos
28582853 my %remotes;
28592854 my $longest = 0;
2860 my @abbrev_remotes = split('\n', ExecUtil::output("$git_cmd remote"));
2861 foreach $remote (@abbrev_remotes) {
2855 my @abbrev_remotes = split('\n', ExecUtil::output("$GIT_CMD remote"));
2856 foreach my $remote (@abbrev_remotes) {
28622857 chomp($remote);
28632858 my $url = RepoUtil::get_config("remote.$remote.url");
28642859 $remotes{$remote} = $url;
28652860 $longest = main::max($longest, length($remote));
28662861 }
2867 if (scalar keys %remotes > 0 && $debug < 2) {
2862 if (scalar keys %remotes > 0 && $DEBUG < 2) {
28682863 print "Named remote repositories: (name -> location)\n";
28692864 foreach my $remote (sort keys %remotes) {
28702865 printf " %${longest}s -> %s\n", $remote, $remotes{$remote};
28812876 chdir($top_dir);
28822877
28832878 # Name
2884 print "Current branch: $branch\n" if $debug < 2;
2879 print "Current branch: $branch\n" if $DEBUG < 2;
28852880
28862881 # Sha1sum
2887 my $current_commit = ExecUtil::output("$git_cmd show-ref -s -h | head -n 1");
2888 print " Cryptographic checksum (sha1sum): $current_commit\n" if $debug < 2;
2882 my $current_commit = ExecUtil::output("$GIT_CMD show-ref -s -h | head -n 1");
2883 print " Cryptographic checksum (sha1sum): $current_commit\n" if $DEBUG < 2;
28892884
28902885 # Default pull/push options
28912886 my $default = "-None-";
28922887 my $print_config_options = 0;
28932888 my ($ret, $options) =
2894 ExecUtil::execute_captured("$git_cmd config --get-regexp " .
2889 ExecUtil::execute_captured("$GIT_CMD config --get-regexp " .
28952890 "^branch\.$branch\.*", ignore_ret => 1);
28962891 chomp($options);
28972892 my @lines;
29042899 $default = $1;
29052900 $print_config_options = ($line_count > 1);
29062901 } else {
2907 my @output = `$git_cmd config --get-regexp remote.origin.*`;
2902 my @output = `$GIT_CMD config --get-regexp remote.origin.*`;
29082903 $default = "origin" if @output;
29092904 }
29102905 }
2911 print " Default pull/push repository: $default\n" if $debug < 2;
2912 if ($print_config_options && $debug < 2) {
2906 print " Default pull/push repository: $default\n" if $DEBUG < 2;
2907 if ($print_config_options && $DEBUG < 2) {
29132908 print " Default pull/push options:\n";
29142909 foreach my $line (@lines) {
29152910 $line =~ s/\s+/ = /;
29182913 }
29192914
29202915 # No. contributors
2921 my $contributors = ExecUtil::output("$git_cmd shortlog -s -n HEAD | wc -l");
2922 print " Number of contributors: $contributors\n" if $debug < 2;
2916 my $contributors = ExecUtil::output("$GIT_CMD shortlog -s -n HEAD | wc -l");
2917 print " Number of contributors: $contributors\n" if $DEBUG < 2;
29232918
29242919 # No. files
2925 my $num_files = ExecUtil::output("$git_cmd ls-tree -r HEAD | wc -l");
2926 print " Number of files: $num_files\n" if $debug < 2;
2920 my $num_files = ExecUtil::output("$GIT_CMD ls-tree -r HEAD | wc -l");
2921 print " Number of files: $num_files\n" if $DEBUG < 2;
29272922
29282923 # No. dirs
29292924 my $num_dirs = ExecUtil::output(
2930 "$git_cmd ls-tree -r -t HEAD " .
2925 "$GIT_CMD ls-tree -r -t HEAD " .
29312926 " | grep -E '[0-9]+ tree'" .
29322927 " | wc -l");
2933 print " Number of directories: $num_dirs\n" if $debug < 2;
2928 print " Number of directories: $num_dirs\n" if $DEBUG < 2;
29342929
29352930 # Some ugly, nasty code to get the biggest file. Seems to be the only
29362931 # method I could find that would work given the corner case filenames
29372932 # (spaces and unicode chars) in the git.git repo (Try eg info on repo
29382933 # from 'git clone git://git.kernel.org/pub/scm/git/git.git').
2939 my @files = `$git_cmd ls-tree -r -l --full-name HEAD`;
2934 my @files = `$GIT_CMD ls-tree -r -l --full-name HEAD`;
29402935 my %biggest = (name => '', size => 0);
29412936 foreach my $line (@files) {
29422937 if ($line =~ m#^[0-9]+ [a-z]+ [0-9a-f]+[ ]*(\d+)[ \t]*(.*)$#) {
29492944 }
29502945 }
29512946 my $biggest_file = "$biggest{size} ($biggest{name})";
2952 print " Biggest file size, in bytes: $biggest_file\n" if $debug < 2;
2947 print " Biggest file size, in bytes: $biggest_file\n" if $DEBUG < 2;
29532948
29542949 # No. commits
2955 my $branch_depth = ExecUtil::output("$git_cmd rev-list HEAD | wc -l");
2956 print " Commits: $branch_depth\n" if $debug < 2;
2950 my $branch_depth = ExecUtil::output("$GIT_CMD rev-list HEAD | wc -l");
2951 print " Commits: $branch_depth\n" if $DEBUG < 2;
29572952
29582953 # Other possibilities:
29592954 # Disk space used by respository (du -hs .git, or packfile size?)
29722967 package init;
29732968 @init::ISA = qw(subcommand);
29742969 INIT {
2975 $command{init} = {
2970 $COMMAND{init} = {
29762971 unmodified_behavior => 1,
29772972 section => 'creation',
29782973 about => 'Create a new repository'
29912986 Creates a new repository.
29922987
29932988 If you want to publish a copy of an existing repository so that others
2994 can access it, use eg publish instead.
2989 can access it, use 'eg publish' instead.
29952990
29962991 Note for cvs/svn users: With cvs or svn it is common to create an empty
29972992 repository on \"the server\", then check it out locally and start adding
30433038 package log;
30443039 @log::ISA = qw(subcommand);
30453040 INIT {
3046 $command{log} = {
3041 $COMMAND{log} = {
30473042 section => 'discovery',
30483043 about => 'Show history of recorded changes'
30493044 };
30773072 return $self;
30783073 }
30793074
3080 sub _get_values {
3075 sub _get_values ($$) {
30813076 my ($names, $sha1sum) = @_;
30823077 my ($name, $distance);
30833078 if (defined($names->{$sha1sum})) {
30863081 return ($name, $distance);
30873082 }
30883083
3089 sub _path_count {
3084 sub _path_count ($) {
30903085 my ($name) = @_;
30913086 my @matches = ($name =~ m/[~^]/g);
30923087 return scalar @matches;
30933088 }
30943089
3095 sub _get_revision_name {
3090 sub _get_revision_name ($$$) {
30963091 my ($sha1sum, $filehandle, $names) = @_;
30973092 my ($name, $distance);
30983093
31173112 # Determine any name we previously determined for $parent, the name we
31183113 # would give it relative to $child, and determine which should "win"
31193114 my ($orig_parent_name, $orig_parent_distance) = _get_values($names, $parent);
3115 my $parent_name;
31203116 if ($cur_name =~ /^(.*)~(\d+)$/) {
31213117 my $count = $2 + 1;
31223118 $parent_name = "$1~$count";
31233119 } else {
31243120 $parent_name = "$cur_name~1";
31253121 }
3126 $parent_distance = $distance + 1;
3122 my $parent_distance = $distance + 1;
31273123 if (!$orig_parent_name ||
31283124 _path_count($orig_parent_name) > _path_count($parent_name)) {
31293125 $names->{$parent} = [$parent_name, $parent_distance];
31663162 # all the arguments to determine if there is a valid revision listed on the
31673163 # command line (or, failing that, whether HEAD reference a valid revision);
31683164 # instead, we just check for the simple case of no branches existing yet.
3169 if (!`$git_cmd branch -a`) {
3165 if (!`$GIT_CMD branch -a`) {
31703166 die "Error: No recorded commits to show yet.\n";
31713167 }
31723168
31733169 # We can just run plain git log if there's not current branch
31743170 if (!$branch || !RepoUtil::valid_ref($branch)) {
3175 return ExecUtil::execute("$git_cmd log @ARGV", ignore_ret => 1);
3171 return ExecUtil::execute("$GIT_CMD log @ARGV", ignore_ret => 1);
31763172 }
31773173
31783174 my ($ret, $revision) =
3179 ExecUtil::execute_captured("$git_cmd rev-parse refs/heads/$branch");
3175 ExecUtil::execute_captured("$GIT_CMD rev-parse refs/heads/$branch");
31803176 exit $ret if $ret;
31813177 chomp($revision);
31823178
31833179 # Show the user the essential equivalent to what we manually do
3184 if ($debug) {
3185 print " >>Running: $git_cmd log @ARGV | \\\n" .
3186 " $git_cmd name-rev --stdin " .
3180 if ($DEBUG) {
3181 print " >>Running: $GIT_CMD log @ARGV | \\\n" .
3182 " $GIT_CMD name-rev --stdin " .
31873183 "--refs=refs/heads/$branch | \\\n" .
31883184 " less\n";
3189 return if $debug == 2;
3185 return 0 if $DEBUG == 2;
31903186 }
31913187
31923188 # Setup name determination via output from git rev-list
31933189 my %names;
3194 open(REV_LIST_INPUT, "$git_cmd rev-list --parents $branch | ");
3190 open(REV_LIST_INPUT, "$GIT_CMD rev-list --parents $branch -- | ");
31953191 $names{$revision} = [$branch, 0];
31963192
31973193 # Loop over the output of git log, printing/modifying as we go
3198 my $use_colors = -t STDOUT ? "GIT_PAGER_IN_USE=1" : "";
3199 open(INPUT, "$use_colors $git_cmd log @ARGV | ");
3194 my $old_git_pager_in_use = $ENV{"GIT_PAGER_IN_USE"};
3195 if (-t STDOUT) {
3196 # use colors
3197 $ENV{"GIT_PAGER_IN_USE"} = "1";
3198 }
3199 open(INPUT, "$GIT_CMD log @ARGV | ");
32003200 $ENV{"LESS"} = "FRSX" unless defined $ENV{"LESS"};
3201 my $less = ($use_pager == 1) ? "less" :
3202 ($use_pager == 0) ? "cat" :
3203 `$git_cmd config core.pager` || "less";
3201 my $less = ($USE_PAGER == 1) ? "less" :
3202 ($USE_PAGER == 0) ? "cat" :
3203 `$GIT_CMD config core.pager` || "less";
32043204 chomp($less);
32053205 my $pid = open(OUTPUT, "| $less");
32063206
32073207 # Make sure that we don't leave the terminal in a weird state if the user
32083208 # hits Ctrl-C during eg log
32093209 local $SIG{INT} =
3210 sub { kill SIGKILL, $pid; close(INPUT); close(OUTPUT); close(REV_LIST_INPUT);
3210 sub { kill 'SIGKILL', $pid; close(INPUT); close(OUTPUT); close(REV_LIST_INPUT);
32113211 exit(0); };
32123212
32133213 #open(OUTPUT, ">&STDOUT");
32153215 # If it's a commit line, determine the name of the commit and print it too
32163216 # ANSI color escape sequences make this regex kind of ugly...
32173217 if (/^((?:\e\[.*?m)?commit ([0-9a-f]{40}))((?:\e\[m)?)$/) {
3218 my $name = _get_revision_name($2, REV_LIST_INPUT, \%names);
3218 my $name = _get_revision_name($2, *REV_LIST_INPUT, \%names);
32193219 print OUTPUT "$1 ($name)$3\n" if $name;
32203220 print OUTPUT "$1$3\n" if !$name;
32213221 } else {
32233223 }
32243224 }
32253225 my ($ret1, $ret2, $ret3);
3226 close(INPUT); $ret1 = $?;
3227 close(OUTPUT); $ret2 = $?;
3226 close(INPUT); $ret1 = $? >> 8;
3227 close(OUTPUT); $ret2 = $? >> 8;
3228 $ENV{"GIT_PAGER_IN_USE"} = $old_git_pager_in_use;
32283229
32293230 # Make sure we close the pipe from rev-list too; We use "$? && $!"
32303231 # instead of "$?" because we don't care about the return value of the
32353236 # (This is my best guess at what to do given the random failures from
32363237 # t1411-reflog-show.sh, and reading 'man perlfunc' under 'close'; it seems
32373238 # to work.)
3238 close(REV_LIST_INPUT); $ret3 = $? && $!;
3239 close(REV_LIST_INPUT); $ret3 = ($? >> 8) && $!;
32393240
32403241 return $ret1 || $ret2 || $ret3;
32413242 }
32463247 package merge;
32473248 @merge::ISA = qw(subcommand);
32483249 INIT {
3249 $command{merge} = {
3250 $COMMAND{merge} = {
32503251 unmodified_behavior => 1,
32513252 section => 'projects',
32523253 about => 'Join two or more development histories (branches) together'
33103311 package publish;
33113312 @publish::ISA = qw(subcommand);
33123313 INIT {
3313 $command{publish} = {
3314 $COMMAND{publish} = {
33143315 extra => 1,
33153316 new_command => 1,
33163317 section => 'collaboration',
33283329 bless($self, $class);
33293330 $self->{'help'} = "
33303331 Usage:
3331 eg publish [--bypass-modification-check] REMOTE_DIRECTORY
3332 eg publish [-b|--bypass-modification-check] [-g|--group GROUP]
3333 [REMOTE_ALIAS] SSH_URL
33323334
33333335 Description:
33343336 Publishes a copy of the current repository on a remote machine. Note
33353337 that local changes will be ignored; only committed changes will be
33363338 published. You must have ssh access to the remote machine and must have
3337 both rsync and ssh installed on your local machine (every modern distro
3338 or OS installs both by default).
3339
3340 The remote directory should be specified using rsync syntax, even if the
3341 remote repository will be accessed by some other protocol. Typical rsync
3342 syntax for a (usually remote) directory is
3343 [[USER@]MACHINE:]PATH
3344 If PATH is not absolute and MACHINE is specified, it is taken as relative
3345 to the user's home directory on MACHINE. See the examples below for more
3346 detail, or the rsync(1) manpage. If any files or directories exist below
3347 the specified remote directory, they will be removed or replaced.
3348
3349 Note that if git is not installed on the remote machine, you will be
3350 unable to push updates to the remote repository (however, you can
3351 republish over the top of the previous copy).
3339 both git and ssh installed on both local and remote machines.
3340
3341 After publishing the repository, it is accessible via the remote
3342 REMOTE_ALIAS, thus allowing you to use REMOTE_ALIAS to push and pull
3343 commands. If REMOTE_ALIAS is not specified, it defaults to 'origin'.
3344
3345 If the --group (or -g) option is specified, the given GROUP must be a
3346 valid unix group on the remote machine, and the user must be a member of
3347 that group. When this option is passed, eg publish will ensure that all
3348 files are readable and writable by members of that group.
3349
3350 Note that the remote location is specified using a ssh url; see 'eg help
3351 topic remote_urls' for a full list of valid possibilities, but the
3352 general case is to use scp(1) syntax: [[USER@]MACHINE]:REMOTE_PATH. Note
3353 that if any files or directories exist below the specified remote
3354 directory, publish will abort.
33523355
33533356 Examples:
33543357 Publish a copy of the current repository on the machine myserver.com in
3355 the directory /var/scratch/git-stuff/my-repo.git. Then immediately
3356 make a clone of the remote repository
3357 \$ eg publish myserver.com:/var/scratch/git-stuff/my-repo.git
3358 \$ cd
3358 the directory /var/scratch/git-stuff/my-repo.git, and make it readable
3359 and writable by the unix group 'gitters'. Then immediately make a clone
3360 of the remote repository
3361 \$ eg publish -g gitters myserver.com:/var/scratch/git-stuff/my-repo.git
3362 \$ cd ..
33593363 \$ eg clone myserver.com:/var/scratch/git-stuff/my-repo.git
33603364
33613365 Publish a copy of the current repository on the machine www.gnome.org, in
33633367 user fake, then immediately clone it again into a separate directory
33643368 named another-myproj.
33653369 \$ eg publish fake\@www.gnome.org:public_html/myproj
3366 \$ cd
3370 \$ cd ..
33673371 \$ eg clone http://www.gnome.org/~fake/myproj another-myproj
33683372
33693373 Options
33743378 option.
33753379 ";
33763380 $self->{'differences'} = '
3377 eg publish is unique to eg; git makes publishing repositories annoyingly
3378 painful. The steps that eg publish performs are (assuming one is in the
3379 toplevel directory and that GIT_DIR=.git):
3380 touch .git/git-daemon-export-ok
3381 git gc
3382 cd .git
3383 git --bare update-server-info
3384 mv .git/hooks/post-update.sample .git/hooks/post-update
3385 chmod u+x hooks/post-update
3386 cd ..
3387 rsync -e ssh -az --delete .git REMOTE_DIRECTORY
3388 Since this does make some minor changes to the local repository that are
3389 unnecessary after the rsync command has completed, I might add some code
3390 to try to clean the .git directory back up. I doubt any of it will hurt
3391 if I do not get around to it, though.
3392
3393 eg publish also will setup the published repository as the new origin (if
3394 a remote named origin does not already exist), so that future pushes and
3395 pulls use the published repository.
3381 eg publish is unique to eg, designed to condense the multiple necessary
3382 steps with git into one (or a few) commands. The steps that eg publish
3383 performs are essentially:
3384 if ( git config --get remote.REMOTE_ALIAS.url > /dev/null); then
3385 echo "REMOTE_ALIAS already defined"; false;
3386 fi &&
3387 ssh [USER@]MACHINE "
3388 test -d REMOTE_PATH && echo "REMOTE_PATH already exists!" && exit 1;
3389 if (! groups | grep "\bGROUP\b" > /dev/null); then
3390 echo "Cannot change to group GROUP"; exit 1;
3391 fi;
3392 if (! type -p git>/dev/null);then echo "Cannot find git"; exit 1; fi &&
3393 newgrp GROUP;
3394 mkdir -p REMOTE_PATH &&
3395 cd REMOTE_PATH &&
3396 git init [--shared] --bare &&
3397 touch git-daemon-export-ok &&
3398 (mv hooks/post-update.sample hooks/post-update || true) &&
3399 chmod u+x hooks/post-update" &&
3400 git remote add REMOTE_ALIAS [USER@]MACHINE:REMOTE_PATH &&
3401 git push
3402 Note that the command involving git-daemon-export-ok is only needed if
3403 you will be cloning/pulling from the repository via the git:// protocol
3404 (in which case you are responsible for running git-daemon on the remote
3405 machine), and the post-update hook related stuff is only necessary if you
3406 are trying to clone/pull via the http:// protocol (in which case you are
3407 responsible for running a webserver such as httpd on the remote machine);
3408 neither of these steps are needed if you are cloning/pulling via ssh, but
3409 they do not cause problems either.
3410
3411 MULTI-USER NOTE: If you want multiple people to be able to push to the
3412 resulting repository, you will need to ensure that they all have ssh
3413 access to the machine, that they are all part of the same unix group, and
3414 that you use the --group option to ensure that the repository is set up
3415 to be shared by the relevant group.
33963416 ';
33973417 return $self;
33983418 }
34023422 my $package_name = ref($self);
34033423
34043424 my $bypass_modification_check = 0;
3425 my $group;
34053426 my $result = main::GetOptions(
34063427 "--help" => sub { $self->help() },
34073428 "bypass-modification-check|b" => \$bypass_modification_check,
3429 "group|g=s" => \$group,
34083430 );
34093431
3410 die "Invalid/insufficient args to eg publish: @ARGV\n" if @ARGV != 1;
3411 $self->{remote_dir} = shift @ARGV;
3432 die "Aborting: Need a URL to push to!\n" if @ARGV < 1;
3433 die "Aborting: Too many args to eg publish: @ARGV\n" if @ARGV > 2;
3434 my $extra_info = (@ARGV == 2) ? "" : ", please specify a REMOTE_ALIAS";
3435 $self->{remote} = (@ARGV == 2) ? shift @ARGV : "origin";
3436 $self->{repository} = shift @ARGV;
3437 $self->{group} = $group;
3438
3439 die "Aborting: remote '$self->{remote}' already exists$extra_info!\n"
3440 if RepoUtil::get_config("remote.$self->{remote}.url");
34123441
34133442 if (!$bypass_modification_check) {
34143443 my $status = RepoUtil::commit_push_checks($package_name,
3415 {unknown => 1, changes => 1});
3444 {unknown => 1,
3445 changes => 1,
3446 unmerged_changes => 1});
34163447 } else {
34173448 # Record the set of unknown files we ignored with -b, so the -b flag
34183449 # isn't needed next time.
34233454 sub run {
34243455 my $self = shift;
34253456
3426 my $orig_dir = main::getcwd();
3427 chdir($self->{git_dir});
3428 print " >>Running: 'cd $self->{git_dir}'<<\n" if $debug;
3429
3430 #
3431 # Warn the user if they have files that may have too restrictive permissions
3432 #
3433 my @non_readable_files = `find . ! -perm -004`;
3434 if (scalar @non_readable_files > 0) {
3435 print STDERR <<EOF;
3436 WARNING: Some files under $self->{git_dir} are not world readable (see the
3437 chmod(1) manpage). These permissions will be preserved in the published
3438 copy, so you will need to manually change the permissions of the published
3439 repository if you want them to be world readable (alternatively, you could
3440 modify the permissions of files under $self->{git_dir} and re-run eg publish.)
3441
3442 EOF
3443 }
3444
3445 #
3446 # Setup the repository for rsyncing
3447 #
3448 ExecUtil::execute("touch git-daemon-export-ok");
3449 print "Optimizing local repository and compressing it...\n" if $debug < 2;
3450 ExecUtil::execute("$git_cmd gc");
3451 ExecUtil::execute("$git_cmd --bare update-server-info");
3452 if (-f "hooks/post-update.sample") {
3453 ExecUtil::execute("mv hooks/post-update.sample hooks/post-update");
3454 }
3455 ExecUtil::execute("chmod u+x hooks/post-update");
3456 my $is_bare = ExecUtil::output("$git_cmd config --get core.bare");
3457 ExecUtil::execute("$git_cmd config core.bare true");
3458
3459 #
3460 # rsync .git to the publish location
3461 #
3462 print "Copy local repository to remote location...\n" if $debug < 2;
3463 my $ret = ExecUtil::execute(
3464 "rsync -e ssh -az --delete --exclude=refs/remotes " .
3465 "--exclude=COMMIT_EDITMSG --exclude=index --exclude=logs " .
3466 "--exclude=info/ignored-unknown " .
3467 "--exclude=ORIG_HEAD --exclude=FETCH_HEAD --exclude=MERGE_HEAD " .
3468 "./ \"$self->{remote_dir}\"");
3469 exit $ret if $ret;
3470
3471 #
3472 # Undo any temporary changes we did for publishing
3473 #
3474 ExecUtil::execute("$git_cmd config core.bare $is_bare");
3475 # FIXME: I should clean up git-daemon-export-ok, hooks/post-update, and
3476 # the files that 'man git-update-server-info' says it creates.
3477
3478 #
3479 # Set up the published repository as the default ("origin"), if origin
3480 # is not already setup. Otherwise, tell the user how to simplify
3481 # future pushes/pulls.
3482 #
3483 my @output = `$git_cmd config --get-regexp remote.origin.*`;
3484 if (@output) {
3485 print <<EOF;
3486 NOTE: Not setting up $self->{remote_dir} as your default push/pull location,
3487 since you already have one. To set up easy pushes and pulls to this location,
3488 run
3489 eg remote add NICKNAME $self->{remote_dir}
3490 and then do future pushes and pulls with
3491 eg push NICKNAME
3492 eg pull NICKNAME
3493 EOF
3457 my ($user, $machine, $port, $path) =
3458 Util::split_ssh_repository($self->{repository});
3459 if (!defined $path) {
3460 # It may be a local path rather than an ssh path...
3461 if ($self->{repository} =~ m#^/[^:]*#) {
3462 $path = $self->{repository};
3463 } else {
3464 die "Aborting: Could not parse remote repository URL " .
3465 "'$self->{repository}'.\n";
3466 }
3467 }
3468
3469 my ($sg, $sge, $check_group, $shared) = ("", "", "", "");
3470 if (defined $self->{group}) {
3471 $check_group = "
3472 if (! groups | grep '\\b$self->{group}\\b' > /dev/null); then
3473 echo 'Cannot change to group $self->{group}!'; exit 1;
3474 fi;";
3475 $sg = "sg $self->{group} -c '";
3476 $sge = "'";
3477 $shared = "--shared ";
3478 }
3479
3480 my $ret;
3481 if (defined $machine) {
3482 print "Setting up remote repository via ssh...\n";
3483 $ret = ExecUtil::execute("
3484 ssh $port -q $user$machine \"
3485 test -d $path && echo '$path already exists!' && exit 1; $check_group
3486 if (! which git>/dev/null);then echo 'Cannot find git'; exit 1; fi;
3487 ${sg}mkdir -p $path${sge} &&
3488 cd $path &&
3489 ${sg}git init ${shared}--bare${sge} &&
3490 touch git-daemon-export-ok &&
3491 (mv hooks/post-update.sample hooks/post-update || true) &&
3492 chmod u+x hooks/post-update\"",
3493 ignore_ret => 1);
34943494 } else {
3495 ExecUtil::execute("$git_cmd remote add origin $self->{remote_dir}");
3496
3497 #
3498 # Set up the configuration variables branch.BRANCH.(remote|merge)
3499 # for each BRANCH (used later as default push/pull locations)
3500 #
3501 my @branches = `$git_cmd branch`;
3502 print " >>Running: '$git_cmd branch'<<\n" if $debug;
3503 foreach my $branch (@branches) {
3504 chomp($branch);
3505 $branch =~ s#..##;
3506 next if $branch eq "HEAD";
3507 RepoUtil::set_config("branch.$branch.remote", "origin");
3508 RepoUtil::set_config("branch.$branch.merge", "refs/heads/$branch");
3509 }
3510 }
3511
3512 chdir($orig_dir);
3495 print "Setting up not-so-remote repository...\n";
3496 $ret = ExecUtil::execute("
3497 test -d $path && echo '$path already exists!' && exit 1; $check_group
3498 if (! which git>/dev/null);then echo 'Cannot find git'; exit 1; fi;
3499 ${sg}mkdir -p $path${sge} &&
3500 cd $path &&
3501 ${sg}$GIT_CMD init ${shared}--bare${sge} &&
3502 touch git-daemon-export-ok &&
3503 (mv hooks/post-update.sample hooks/post-update || true) &&
3504 chmod u+x hooks/post-update",
3505 ignore_ret => 1);
3506 }
3507 die "Remote repository setup failed!\n" if $ret != 0;
3508
3509 print "Creating new remote $self->{remote}...\n";
3510 #if ($self->{remote} ne "origin") {
3511 # print "If $self->{remote} should be a default push/pull location, run:\n" .
3512 # " eg track [LOCAL_BRANCH] $self->{remote}/REMOTE_BRANCH\n";
3513 #}
3514 ExecUtil::execute("$GIT_CMD remote add $self->{remote} $self->{repository}");
3515 print "Pushing to new remote $self->{remote}...\n";
3516 $ret = ExecUtil::execute("$GIT_CMD push --mirror $self->{remote}");
3517 if ($ret) {
3518 ExecUtil::execute("$GIT_CMD remote rm $self->{remote}");
3519 return $ret;
3520 }
3521
3522 print "Done.\n";
3523
35133524 return 0;
35143525 }
35153526
35193530 package pull;
35203531 @pull::ISA = qw(subcommand);
35213532 INIT {
3522 $command{pull} = {
3533 $COMMAND{pull} = {
35233534 section => 'collaboration',
35243535 about => 'Get updates from another repository and merge them'
35253536 };
35413552 See 'eg help topic remote-urls' for valid syntax for remote repositories.
35423553 If you frequently pull from the same repository, you may want to set up a
35433554 nickname for it (see 'eg help remote'), so that you can specify the
3544 nickname instead of the full repository URL every time.
3555 nickname instead of the full repository URL every time. If you want to
3556 set a (different) default repository and branch to pull from, see 'eg
3557 track'.
35453558
35463559 By default, tags in the remote repository associated with commits that
35473560 are pulled, will themselves be pulled. One can specify to pull
36233636 history so that your recent local commits become commits on top of the
36243637 changes downloaded from the remote repository.
36253638
3626 NOTE: This is a potentially _dangerous_ operation. Rewriting history
3627 that has been pushed or pulled into another repository can break
3628 subsequent pushes and pulls with those repositories. (Such breaks can
3629 be fixed, at the cost of having to modify the commit history of each
3630 affected repository.) Do not use this option without thoroughly
3631 understanding 'eg help rebase'.
3639 NOTE: This is a potentially dangerous operation if you have local
3640 commits not found in the repository you are pulling from but which are
3641 found in some other repository (e.g. your local commits have been
3642 directly pulled from your copy by another developer, or to your copy
3643 from another developer). In such a case, unless the other copy of
3644 these commits are also rebased (or discarded), you will probably get
3645 into trouble and need to thoroughly understand 'eg help rebase' before
3646 using this option.
36323647 ";
36333648 $self->{'differences'} = "
36343649 eg pull and git pull are nearly identical. eg provides a slightly more
36513666
36523667 sub preprocess {
36533668 my $self = shift;
3654 my $package_name = ref($self);
36553669
36563670 #
36573671 # Parse options
36913705 );
36923706 die "Cannot specify both --all-tags and --no-tags!\n"
36933707 if $all_tags && $no_tags;
3694 die "Cannot specify request tags along with --all-tags or --no-tags!\n"
3708 die "Cannot specify individual tags along with --all-tags or --no-tags!\n"
36953709 if @tags && ($all_tags || $no_tags);
36963710 my $repository = shift @ARGV;
36973711 my @git_refspecs = @ARGV;
37033717 #
37043718 # Get the repository to pull from
37053719 #
3720 my $repo_is_a_remote = 1;
3721 my $repo_specified = defined($repository);
37063722 if ($repository) {
37073723 push(@{$self->{args}}, $repository);
3708 } elsif (!$repository && @branches) {
3724 $repo_is_a_remote = 0 if !RepoUtil::get_config("remote.$repository.url");
3725 } else {
37093726 $repository = RepoUtil::get_default_push_pull_repository();
3710 push(@{$self->{args}}, $repository);
3711 } else {
3712 # Just drop through to the git pull defaults.
3727 if (!$repository) {
3728 # This line should never be reached
3729 die "Don't know what to pull!"
3730 }
3731 push(@{$self->{args}}, $repository) if (!$repo_specified && @branches);
37133732 }
37143733
37153734 #
37173736 #
37183737 push(@branches, @git_refspecs);
37193738
3720 if (!@branches && !@tags) {
3739 # If we were given explicit branches or tags to pull, then we know what
3740 # to pull. Also, for compatibility with git, if the repository specified
3741 # is a url rather than a remotename, then we should pull from HEAD if
3742 # no other refspecs are specified.
3743 #
3744 # In all other cases...
3745 if ($repo_is_a_remote && !@branches && !@tags) {
3746 my $dont_know_what_to_pull = 0;
3747
3748 # If we don't have a current branch, we can't tell what branch to merge
37213749 my $branch = RepoUtil::current_branch();
3722 if ($branch) {
3723 my ($merge_branch, $url);
3724 $url = RepoUtil::get_config("branch.$branch.remote");
3725 if ($url && (!$repository || $url eq $repository)) {
3726 $merge_branch = RepoUtil::get_config("branch.$branch.merge");
3727 }
3728 if (!$merge_branch && ($repository || $url)) {
3729 my $only_branch = RepoUtil::get_only_branch($repository || $url);
3730 push(@{$self->{args}}, $url) if !$repository;
3731 push(@branches, $only_branch);
3732 }
3750 if (!$branch) {
3751 $dont_know_what_to_pull = 1;
3752 goto PULL_CHECK;
3753 }
3754
3755 # If there's no default remote, we must ignore branch.$branch.merge so
3756 # we don't know what branch to merge
3757 my ($merge_branch, $default_remote);
3758 $default_remote = RepoUtil::get_config("branch.$branch.remote");
3759 if (!$default_remote) {
3760 $dont_know_what_to_pull = 1;
3761 goto PULL_CHECK;
3762 }
3763
3764 # If the default remote doesn't match the specified repository, we must
3765 # ignore branch.$branch.remote so we don't know what branch to merge
3766 if ($repo_specified and $repository ne $default_remote) {
3767 $dont_know_what_to_pull = 1;
3768 goto PULL_CHECK;
3769 }
3770
3771 # If branch.$branch.merge is not set we don't know what branch to merge
3772 $merge_branch = RepoUtil::get_config("branch.$branch.merge");
3773 if (!$merge_branch) {
3774 $dont_know_what_to_pull = 1;
3775 goto PULL_CHECK;
3776 }
3777
3778 PULL_CHECK:
3779 # Even if we don't know what to pull, if the remote repository has exactly
3780 # one branch, then we can just pull from that.
3781 if ($dont_know_what_to_pull) {
3782 my $location = $repository || $default_remote;
3783 my $only_branch = RepoUtil::get_only_branch($location, "pull");
3784 push(@{$self->{args}}, $location) if !$repo_specified;
3785 push(@branches, $only_branch);
37333786 }
37343787 }
37353788
37443797 sub run {
37453798 my $self = shift;
37463799
3747 @args = Util::quote_args(@{$self->{args}});
3748 return ExecUtil::execute("$git_cmd pull @args", ignore_ret => 1);
3800 my @args = Util::quote_args(@{$self->{args}});
3801 return ExecUtil::execute("$GIT_CMD pull @args", ignore_ret => 1);
37493802 }
37503803
37513804 ###########################################################################
37543807 package push;
37553808 @push::ISA = qw(subcommand);
37563809 INIT {
3757 $command{push} = {
3810 $COMMAND{push} = {
37583811 section => 'collaboration',
37593812 about => 'Push local commits to a published repository'
37603813 };
37743827
37753828 Description:
37763829 Push committed changes in the current repository to a published remote
3777 repository. The push can fail if the remote repository has commits not
3830 repository. Note that this command cannot be used to create a new remote
3831 repository; use 'eg publish' (which both creates a remote repository and
3832 pushes to it) if you need to do that.
3833
3834 The push can fail if the remote repository has commits not
37783835 in the current repository; this can be fixed by pulling and merging
37793836 changes from the remote repository (use eg pull for this) and then
37803837 repeating the push. Note that for getting changes directly to a fellow
37913848
37923849 If you frequently push to the same repository, you may want to set up a
37933850 nickname for it (see 'eg help remote'), so that you can specify the
3794 nickname instead of the full repository URL every time.
3851 nickname instead of the full repository URL every time. Also, if you want
3852 to change the default repository and branch to push to, see 'eg track'.
37953853
37963854 Examples:
37973855 Push commits in the current branch
38543912 the remote repository.
38553913 ";
38563914 $self->{'differences'} = "
3857 eg push tries to simplify git, but is essentially the same other than (1)
3858 defaulting to pushing only the current branch instead of matching
3859 branches, and (2) trying to avoid pushing into a bare repository. There
3860 are two other minor changes, namely providing extra --tag and --branch
3861 flags to make command lines more self-documenting and to avoid
3862 introducing refspecs (a very confusing topic for new users) too early.
3863 However, refspecs still work with eg push, and users can learn about them
3864 by running 'eg help topic refspecs'.
3865
3866 Pushing just the current branch to its tracking branch is a change mostly
3867 made with the idea that new git users coming from the cvs/svn world will
3868 probably be using central repositories that many developers push to.
3869 However, given the recent push.default config option introduced in
3870 git-1.6.3 (which can be set to 'tracking' to get the eg behavior or
3871 'matching' to get the old git default behavior), this change in eg may be
3872 obsolete and reverted in the future.
3873
3874 The method eg push uses to avoid pushing into a non-bare repository is a
3875 push-side check rather than a receive-side check. In cases it can detect
3876 that the remote repository is bare (pushing to a directory on a locally
3877 mounted filesystem or pushing over ssh), it will disable such pushes
3878 unless the user specifies both source and destination references
3879 explicitly (which can be done by running, for example, 'eg push origin
3880 master:master'). However, given the new receive-side check introduced in
3881 git-1.6.x to avoid pushing into bare repositories unless they have
3882 configured to allow it, this change in eg may be obsolete and reverted in
3883 the future.
3915 eg push is largely the same as git push, despite attempts to simplify in
3916 a number of areas:
3917
3918 (1) push.default=tracking is the default if push.default is unset (git
3919 uses push.default=matching if push.default is unset). This seems
3920 to match the intuition of most former cvs/svn users, though it is
3921 my one dangerous default change for existing git users. Tough
3922 call, since the 'safe' defaults for each group are unsafe and/or
3923 confusing for the other. A new --matching-branches flag is added
3924 to get the old behavior (the plain ':' refspec from git does the
3925 same, but --matching-branches is more self-documenting and also
3926 predates the ':' refspec).
3927
3928 (2) eg prevents pushing into a bare repository as a push-side check
3929 rather than a receive-side check (when it can determine that the
3930 remote repository is bare -- i.e. if the repository url is for a
3931 locally mounted filesystem or uses ssh). eg also allows the check
3932 to be overridden on the push-side (by specifying a refspec
3933 containing a ':' character). This means it can work for users of
3934 existing repositories (created with git < 1.7), and it provides a
3935 solution that both avoids working copy inconsistency for new users
3936 while allowing more advanced users to do what they need on the same
3937 repository, and without forcing users to twiddle with the
3938 configuration of the remote repository. However, this method
3939 doesn't work for repositories accessed via git://, and only works
3940 for ssh-accessed repositories if users have ssh setup to not need a
3941 password (kerberos, ssh-keys, etc.).
3942
3943 (3) eg performs checks for uncommitted changes and newly created
3944 unknown files and warns/aborts if such exist when the user pushes
3945 (most former cvs/svn users are not yet familiar with the fact that
3946 only committed stuff gets pushed/pulled). As with eg commit, such
3947 checks can be overridden with the -b flag.
3948
3949 (4) eg provides extra --tag and --branch flags to make command lines
3950 more self-documenting and to avoid excessively early introduction
3951 of refspecs (a very confusing topic for new users). However,
3952 refspecs still work with eg push, and users can learn about them by
3953 running 'eg help topic refspecs'.
38843954 ";
38853955 return $self;
38863956 }
38873957
3888 sub _get_push_repository {
3958 sub _get_push_repository ($) {
38893959 my ($repository) = @_;
38903960
38913961 if (defined $repository) {
3892 return RepoUtil::get_config("remote.$repository.url") || $repository;
3962 return RepoUtil::get_config("remote.$repository.pushurl") ||
3963 RepoUtil::get_config("remote.$repository.url") ||
3964 $repository;
38933965 } else {
38943966 return RepoUtil::get_config("remote.origin.url")
38953967 }
38993971 # undef the repository doesn't specify a valid repository or the repository
39003972 # is not of a type where we can determine bare-ness. Otherwise returns
39013973 # either the string "true" or "false".
3902 sub _check_if_bare {
3974 sub _check_if_bare ($) {
39033975 my $repository = shift;
39043976
39053977 # Don't know how to check rsync, http, https, or git repositories to see
39173989 chdir($repository);
39183990
39193991 my ($ret, $output) =
3920 ExecUtil::execute_captured("$git_cmd rev-parse --is-bare-repository",
3992 ExecUtil::execute_captured("$GIT_CMD rev-parse --is-bare-repository",
39213993 ignore_ret => 1);
39223994
39233995 chdir($orig_dir);
39294001 #
39304002 # Check ssh systems
39314003 #
3932 my ($machine, $path);
3933 if ($repository =~ m#^ssh://((?:.*?@)?)([^/:]*)(?::[0-9]+)?(.*)$#) {
3934 $user = $1;
3935 $machine = $2;
3936 $path = $3;
3937 $path =~ s#^/~#~#; # Change leading /~ into plain ~
3938 } elsif ($repository =~ m#^((?:.*?@)?)([^:]*):(.*)$#) {
3939 $user = $1;
3940 $machine = $2;
3941 $path = $3;
3942 }
4004 my ($user, $machine, $port, $path) = Util::split_ssh_repository($repository);
39434005 return undef if !defined $machine || !defined $path;
39444006
39454007 my ($ret, $output) =
39464008 ExecUtil::execute_captured(
3947 "ssh $user$machine 'cd $path && $git_cmd rev-parse --is-bare-repository'",
4009 "ssh $port -q -o BatchMode=yes $user$machine 'cd $path && $GIT_CMD rev-parse --is-bare-repository'",
39484010 ignore_ret => 1);
39494011 return undef if $ret != 0;
39504012 chomp($output);
3951 return $output;
4013 my @lines = split('\n', $output);
4014 my $result = $lines[-1];
4015 # If ssh or git itself failed, $ret could still be 0 but $result could be
4016 # something other than "true" or "false"
4017 return undef if (! grep {$_ eq $result} ["true", "false"]);
4018 return $result;
39524019 }
39534020
39544021 sub preprocess {
39734040 "--all-branches|all" => \$all_branches,
39744041 "--matching-branches" => \$matching_branches,
39754042 "--all-tags" => \$all_tags,
4043 "tags" => \$all_tags,
39764044 "--mirror" => \$mirror,
39774045 "--dry-run" => sub { &$record_arg(@_) },
4046 "--porcelain" => sub { &$record_arg(@_) },
4047 "--progress" => sub { &$record_arg(@_) },
39784048 "--receive-pack=s" => sub { &$record_args(@_) },
39794049 "force|f" => sub { &$record_arg(@_) },
39804050 "repo=s" => \$repo,
39814051 "thin" => sub { &$record_arg(@_) },
39824052 "no-thin" => sub { &$record_arg(@_) },
39834053 "verbose|v" => sub { &$record_arg(@_) },
4054 "set-upstream|u" => sub { &$record_arg(@_) },
39844055 "bypass-modification-check|b" => \$bypass_modification_check,
39854056 );
39864057 die "Cannot specify individual branches and request all branches too!\n"
39924063
39934064 if (!$bypass_modification_check) {
39944065 my $status = RepoUtil::commit_push_checks($package_name,
3995 {unknown => 1, changes => 1});
4066 {unknown => 1,
4067 changes => 1,
4068 unmerged_changes => 1});
39964069 } else {
39974070 # Record the set of unknown files we ignored with -b, so the -b flag
39984071 # isn't needed next time.
40054078
40064079 my $default_specified = 0;
40074080 $default_specified = 1 if $all_branches;
4008 $default_specified = 1 if $matching_branches;
40094081 $default_specified = 1 if $all_tags;
40104082 $default_specified = 1 if $mirror;
40114083
40234095 push(@{$self->{args}}, $repository);
40244096 $remote = $repository;
40254097 } else {
4026 # Just drop through to the git pull defaults.
4098 # Just drop through to the git push defaults.
40274099 }
40284100
40294101 #
40314103 # ssh; I don't know how to detect other cases)...unless user explicitly
40324104 # specifies both source and destination references explicitly
40334105 #
4106 my $remote_chk = $remote || $repository;
40344107 $repository = _get_push_repository($repository);
40354108 my $push_to_non_bare_repo;
40364109 if ($repository) {
40764149 #
40774150 # Get the default branch to push to, if needed
40784151 #
4152 push(@{$self->{args}}, ":") if $matching_branches;
4153 $default_specified = 1 if $matching_branches;
4154
40794155 if (!@branches && !@tags && !@git_refspecs && !$default_specified) {
40804156 # User hasn't specified what to push; default choices:
40814157 # 1 - remote.$remote.(push|mirror) options
40824158 my $default_known = 0;
4083 if (defined($remote)) {
4084 if (defined(RepoUtil::get_config("remote.$remote.push")) ||
4085 defined(RepoUtil::get_config("remote.$remote.mirror"))) {
4086 $default_known = 1;
4087 }
4088 }
4089 # 2 - branch.$branch.merge option
4159 if (defined(RepoUtil::get_config("remote.$remote_chk.push")) ||
4160 defined(RepoUtil::get_config("remote.$remote_chk.mirror"))) {
4161 $default_known = 1;
4162 }
4163 # 2 - push.default option
4164 if (defined(RepoUtil::get_config("push.default"))) {
4165 $default_known = 1;
4166 }
4167 # 3 - branch.$branch.merge option
40904168 if (!$default_known) {
40914169 my $branch = RepoUtil::current_branch();
4092 $push_branch = RepoUtil::get_config("branch.$branch.merge");
4170 my $push_branch = RepoUtil::get_config("branch.$branch.merge");
40934171 if (defined $push_branch) {
40944172 $push_branch =~ s#refs/heads/##;
40954173 push(@{$self->{args}}, "$branch:$push_branch");
40964174 $default_known = 1;
40974175 }
40984176 }
4099 # 3 - the only branch that exists at the remote end
4177 # 4 - the only branch that exists at the remote end
41004178 if (!$default_known && defined $repository) {
41014179 my $only_branch = RepoUtil::get_only_branch($repository, "push");
41024180 push(@{$self->{args}}, $only_branch);
41164194 sub run {
41174195 my $self = shift;
41184196
4119 @args = Util::quote_args(@{$self->{args}});
4120 return ExecUtil::execute("$git_cmd push @args", ignore_ret => 1);
4197 my @args = Util::quote_args(@{$self->{args}});
4198 return ExecUtil::execute("$GIT_CMD push @args", ignore_ret => 1);
41214199 }
41224200
41234201 ###########################################################################
41264204 package rebase;
41274205 @rebase::ISA = qw(subcommand);
41284206 INIT {
4129 $command{rebase} = {
4207 $COMMAND{rebase} = {
41304208 extra => 1,
41314209 section => 'timesavers',
41324210 about => "Port local commits, making them be based on a different\n" .
43404418
43414419 sub preprocess {
43424420 my $self = shift;
4343 my $package_name = ref($self);
43444421
43454422 #
43464423 # Parse options
43734450 sub run {
43744451 my $self = shift;
43754452
4376 @args = Util::quote_args(@{$self->{args}});
4377 return ExecUtil::execute("$git_cmd rebase @args", ignore_ret => 1);
4453 my @args = Util::quote_args(@{$self->{args}});
4454 return ExecUtil::execute("$GIT_CMD rebase @args", ignore_ret => 1);
43784455 }
43794456
43804457 ###########################################################################
43834460 package remote;
43844461 @remote::ISA = qw(subcommand);
43854462 INIT {
4386 $command{remote} = {
4463 $COMMAND{remote} = {
43874464 unmodified_behavior => 1,
43884465 extra => 1,
43894466 section => 'collaboration',
44894566 package reset;
44904567 @reset::ISA = qw(subcommand);
44914568 INIT {
4492 $command{reset} = {
4569 $COMMAND{reset} = {
44934570 extra => 1,
44944571 section => 'modification',
44954572 about => 'Forget local commits and (optionally) undo their changes'
45994676
46004677 sub preprocess {
46014678 my $self = shift;
4602 my $package_name = ref($self);
46034679
46044680 #
46054681 # Parse options
46244700 package resolved;
46254701 @resolved::ISA = qw(subcommand);
46264702 INIT {
4627 $command{resolved} = {
4703 $COMMAND{resolved} = {
46284704 new_command => 1,
46294705 extra => 1,
46304706 section => 'compatibility',
46514727 be done and the contents ready to commit.
46524728 \$ eg resolved foo.c
46534729 ";
4654 $self->{'differences'} = '
4655 eg resolved is a command new to eg that is not part of git; however, it
4656 simply calls git add.
4657 ';
4730 $self->{'differences'} = "
4731 eg resolved is a command new to eg that is not part of git. It is
4732 almost synonymous with git add; however, there are two differences:
4733 (a) eg resolved will work on a locally deleted file in the unmerged
4734 state (git add will complain that there's 'No such file or
4735 directory', and some users have had difficulty trying to find out
4736 that they needed to run git rm on such files), (b) eg resolved only
4737 works on files in the unmerged state (reporting an error if files
4738 not in such a state are specified).
4739 ";
46584740 return $self;
46594741 }
46604742
46614743 sub run {
46624744 my $self = shift;
4663 my $package_name = ref($self);
4664
4745
4746 die "Error: Must specify paths to resolve.\n" if !@ARGV;
46654747 @ARGV = Util::quote_args(@ARGV);
4666 return ExecUtil::execute("$git_cmd add @ARGV", ignore_ret => 1);
4748
4749 # Determine which files are actually unmerged
4750 my ($ret, $output) =
4751 ExecUtil::execute_captured("$GIT_CMD ls-files -u --error-unmatch @ARGV",
4752 ignore_ret => 1);
4753 chomp($output);
4754 my @lines = split('\n', $output);
4755
4756 # If there are some files that do not have conflicts, scream at the user
4757 if ($ret != 0) {
4758 my @not_unmerged_paths;
4759
4760 foreach my $line (@lines) {
4761 if ($line =~ m/^error: pathspec '(.*?)' did not match any file/) {
4762 push(@not_unmerged_paths, $1);
4763 }
4764 }
4765 if (@not_unmerged_paths) {
4766 die "Error: The following are not unmerged files and thus don't " .
4767 "need resolving:\n " . join("\n ", @not_unmerged_paths) . "\n";
4768 } else {
4769 die "$output\n";
4770 }
4771 }
4772
4773 # Determine the unmerged files (users may have passed a directory which
4774 # has both unmerged files and modified but unstaged ones; we only want
4775 # to stage the unmerged files from such a directory).
4776 my %files;
4777 foreach my $line (@lines) {
4778 $line =~ m/^\d+ [0-9a-f]+ \d\t(.*)$/;
4779 $files{$1} = 1;
4780 }
4781 my @unmerged_files = keys %files;
4782
4783 # Run add -u instead of just add, since we want locally deleted files to
4784 # be picked up as well.
4785 return ExecUtil::execute("$GIT_CMD add -u @unmerged_files", ignore_ret => 1);
46674786 }
46684787
46694788 ###########################################################################
46724791 package revert;
46734792 @revert::ISA = qw(subcommand);
46744793 INIT {
4675 $command{revert} = {
4676 extra => 1,
4794 $COMMAND{revert} = {
46774795 section => 'modification',
46784796 about => 'Revert local changes and/or changes from previous commits'
46794797 };
48664984
48674985 sub preprocess {
48684986 my $self = shift;
4869 my $package_name = ref($self);
48704987
48714988 my ($cur_dir, $top_dir, $git_dir) = RepoUtil::get_dirs();
48724989 my $initial_commit = RepoUtil::initial_commit();
48855002 );
48865003
48875004 # Parsing revs and files
4888 my ($opts, $revs, $files) = RepoUtil::parse_args(@ARGV);
5005 my ($opts, $revs, $files) = RepoUtil::parse_args([], @ARGV);
48895006 unshift(@$revs, $rev) if defined($rev);
48905007
48915008 #
49175034 Alternatively, if you want to modify commits instead of just the working copy
49185035 then use reset instead of revert:
49195036
4920 If you want to undo a rebase or a merge (including a pull or update), try:
4921 eg reset --working-copy ORIG_HEAD
5037 If you want to undo a rebase (or a 'pull --rebase'), try:
5038 eg rebase --abort
5039 If you want to undo a merge (or a plain 'pull'), try:
5040 eg merge --abort
49225041 If you want to undo the last commit (but keep its changes in the working copy),
49235042 try:
49245043 eg reset $active_branch~1
49775096 to your flags to eg revert, where BRANCH is one of
49785097 $list
49795098 If you simply want to abort your merge and undo its conflicts, run
4980 eg revert --since HEAD
5099 eg merge --abort
49815100 EOF
49825101 exit 1;
49835102 }
49905109 }
49915110
49925111 my @quoted_files = Util::quote_args(@$files);
4993 my @unmerged_files = `$git_cmd ls-files --full-name -u -- @quoted_files`;
5112 my @unmerged_files = `$GIT_CMD ls-files --full-name -u -- @quoted_files`;
49945113 if (@unmerged_files && $in) {
49955114 die "Aborting: please clear conflicts from @unmerged_files before " .
49965115 "proceeding.\n";
50085127 # Get the revision whose changes we want to revert, and its parents
50095128 Util::push_debug(new_value => 0);
50105129 my $links = ExecUtil::output(
5011 "$git_cmd rev-list --parents --max-count=1 $self->{revs}");
5130 "$GIT_CMD rev-list --parents --max-count=1 $self->{revs}");
50125131 Util::pop_debug();
50135132 my @list = split(' ', $links); # commit id + parent ids
50145133
50365155 # Determine some other stuff needed
50375156 $self->{files} = \@quoted_files;
50385157 my ($new_files, $newish_files, $revert_files);
5158 my ($newly_added_files, $new_since_rev_files, $other_files);
50395159 if (!$in && !$initial_commit) {
50405160 my $revision = (@$revs) ? $revs->[0] : "HEAD";
50415161 ($newly_added_files, $new_since_rev_files, $other_files) =
50685188 # Case: Initial commit
50695189 #
50705190 if ($self->{initial_commit}) {
5071 $ret = ExecUtil::execute("$git_cmd rm --cached --quiet -- " .
5191 $ret = ExecUtil::execute("$GIT_CMD rm --cached --quiet -- " .
50725192 "@newly_added_files", ignore_ret => 1);
50735193 exit $ret if $ret;
50745194 }
50865206 # state; if three files have conflicts and I try to reset some
50875207 # file other than those three, the command is successful but it
50885208 # spews warnings and gives a bad exit status
5089 ExecUtil::execute("$git_cmd reset -q $revision --" .
5209 ExecUtil::execute("$GIT_CMD reset -q $revision --" .
50905210 " @newly_added_files >/dev/null",
50915211 ignore_ret => 1);
50925212 }
50955215 if ($revision ne "HEAD") {
50965216 Util::push_debug(new_value => 0);
50975217 ($temp_ret, $revision_sha1) =
5098 ExecUtil::execute_captured("$git_cmd rev-parse --verify $revision",
5218 ExecUtil::execute_captured("$GIT_CMD rev-parse --verify $revision",
50995219 ignore_ret => 1);
51005220 ($temp_ret, $head_sha1) =
5101 ExecUtil::execute_captured("$git_cmd rev-parse --verify HEAD",
5221 ExecUtil::execute_captured("$GIT_CMD rev-parse --verify HEAD",
51025222 ignore_ret => 1);
51035223 Util::pop_debug();
51045224 }
51055225
5106 $ret = ExecUtil::execute("$git_cmd reset --hard $revision",
5226 $ret = ExecUtil::execute("$GIT_CMD reset --hard $revision",
51075227 ignore_ret => 1);
51085228 exit $ret if $ret;
51095229
51105230 if ($revision ne "HEAD" && $revision_sha1 ne $head_sha1) {
51115231 # Note, cannot git reset --soft HEAD, since HEAD has changed in
51125232 # the above reset...
5113 $ret = ExecUtil::execute("$git_cmd reset --soft HEAD\@{1}",
5233 $ret = ExecUtil::execute("$GIT_CMD reset --soft HEAD\@{1}",
51145234 ignore_ret => 1);
51155235 exit $ret if $ret;
51165236 }
51225242 if ($paths_specified) {
51235243 if (@newly_added_files) {
51245244 # See rant above about 'git reset is not quiet even when requested'
5125 ExecUtil::execute("$git_cmd reset -q $revision --" .
5245 ExecUtil::execute("$GIT_CMD reset -q $revision --" .
51265246 " @newly_added_files >/dev/null",
51275247 ignore_ret => 1);
51285248 }
51295249 if (@new_since_rev_files) {
51305250 # Ugh, when --quiet doesn't actually mean "quiet".
51315251 # (Reproduce with git-1.6.0.6 on incomplete merge handling testcase)
5132 $ret = ExecUtil::execute("$git_cmd rm --quiet --force " .
5252 $ret = ExecUtil::execute("$GIT_CMD rm --quiet --force " .
51335253 "--ignore-unmatch -- @new_since_rev_files" .
51345254 " > /dev/null",
51355255 ignore_ret => 1);
51365256 exit $ret if $ret;
51375257 }
51385258 if (@other_files) {
5139 $ret = ExecUtil::execute("$git_cmd checkout $revision -- " .
5259 $ret = ExecUtil::execute("$GIT_CMD checkout $revision -- " .
51405260 "@other_files", ignore_ret => 1);
51415261 exit $ret if $ret;
51425262 }
51485268 #
51495269 elsif ($self->{staged}) {
51505270 if ($paths_specified) {
5151 $ret = ExecUtil::execute("$git_cmd reset -q $revision -- @all_files",
5271 $ret = ExecUtil::execute("$GIT_CMD reset -q $revision -- @all_files",
51525272 ignore_ret => 1);
51535273 exit $ret if $ret;
51545274 } else {
5155 $ret = ExecUtil::execute("$git_cmd read-tree $revision",
5275 $ret = ExecUtil::execute("$GIT_CMD read-tree $revision",
51565276 ignore_ret => 1);
51575277 exit $ret if $ret;
51585278 }
51645284 elsif ($self->{unstaged}) {
51655285 if ($self->{just_recent_unstaged}) {
51665286 die "Assertion failed: Paths not specified.\n" if (!$paths_specified);
5167 $ret = ExecUtil::execute("$git_cmd checkout -- @all_files",
5287 $ret = ExecUtil::execute("$GIT_CMD checkout -- @all_files",
51685288 ignore_ret => 1);
51695289 exit $ret if $ret;
51705290 }
51825302 }
51835303
51845304 if (@other_files || !$paths_specified) {
5185 my ($tmp_index) = Util::quote_args("$git_dir/tmp_index");
5186 my $git_index = "GIT_INDEX_FILE=$tmp_index ";
5187
51885305 my $cf = "@other_files";
51895306 if (!$paths_specified) {
51905307 my ($cur_dir, $top_dir, $git_dir) = RepoUtil::get_dirs();
51915308 ($cf) = Util::reroot_paths__from_to_files($top_dir, $cur_dir, '.');
51925309 }
5193 $ret = ExecUtil::execute("$git_index $git_cmd checkout $revision " .
5310 my $old_git_index_file = $ENV{"GIT_INDEX_FILE"};
5311 $ENV{"GIT_INDEX_FILE"} = "$git_dir/tmp_index";
5312 $ret = ExecUtil::execute("$GIT_CMD checkout $revision " .
51945313 "-- $cf", ignore_ret => 1);
5314 $ENV{"GIT_INDEX_FILE"} = $old_git_index_file;
51955315 exit $ret if $ret;
51965316
5317 my ($tmp_index) = Util::quote_args("$git_dir/tmp_index");
51975318 $ret = ExecUtil::execute("rm $tmp_index");
51985319 exit $ret if $ret;
51995320 }
52205341
52215342 # Print out the (nearly) equivalent commands if the user asked for
52225343 # debugging information
5223 if ($debug) {
5344 if ($DEBUG) {
52245345 print " >>Running: " .
5225 "$git_cmd diff @diff_flags $self->{revs} ${marker}@files | ";
5346 "$GIT_CMD diff @diff_flags $self->{revs} ${marker}@files | ";
52265347 print "(cd $top_dir && " if ($top_dir ne $cur_dir);
5227 print "$git_cmd apply @apply_flags -R";
5348 print "$GIT_CMD apply @apply_flags -R";
52285349 print ")" if ($top_dir ne $cur_dir);
52295350 print "\n";
52305351 }
52345355 # we have to run diff, slurp in its output, check if its nonempty,
52355356 # and then only pipe that output back out to git apply if we have
52365357 # an actual diff to revert.
5237 if ($debug < 2) {
5238 open(DIFF, "$git_cmd diff @diff_flags $self->{revs} ${marker}@files |");
5358 if ($DEBUG < 2) {
5359 open(DIFF, "$GIT_CMD diff @diff_flags $self->{revs} ${marker}@files |");
52395360 my @output = <DIFF>;
52405361 my $diff = join("", @output);
52415362 # Listing unmerged paths doesn't count as nonempty
52465367
52475368 if ($diff) {
52485369 chdir($top_dir) if $top_dir ne $cur_dir;
5249 open(APPLY, "| $git_cmd apply @apply_flags -R");
5370 open(APPLY, "| $GIT_CMD apply @apply_flags -R");
52505371 print APPLY $diff;
52515372 close(APPLY);
52525373 chdir($cur_dir) if $top_dir ne $cur_dir;
52635384 package rm;
52645385 @rm::ISA = qw(subcommand);
52655386 INIT {
5266 $command{rm} = {
5387 $COMMAND{rm} = {
52675388 extra => 1,
52685389 section => 'modification',
52695390 about => 'Remove files from subsequent commits and the working copy'
53305451
53315452 sub preprocess {
53325453 my $self = shift;
5333 my $package_name = ref($self);
53345454
53355455 return if (scalar(@ARGV) > 0 && $ARGV[0] eq "--");
53365456 my $result = main::GetOptions("--help" => sub { $self->help() });
53465466 package squash;
53475467 @squash::ISA = qw(subcommand);
53485468 INIT {
5349 $command{squash} = {
5469 $COMMAND{squash} = {
53505470 new_command => 1,
53515471 extra => 1,
53525472 section => 'modification',
53915511
53925512 sub preprocess {
53935513 my $self = shift;
5394 my $package_name = ref($self);
53955514
53965515 my $since;
53975516 my $result = main::GetOptions(
54035522 if (!defined($since)) {
54045523 my $branch = RepoUtil::current_branch();
54055524 die "Aborting: No revision specified.\n" if !defined($branch);
5406 $merge_remote = RepoUtil::get_config("branch.$branch.remote");
5407 $merge_branch = RepoUtil::get_config("branch.$branch.merge");
5525 my $merge_remote = RepoUtil::get_config("branch.$branch.remote");
5526 my $merge_branch = RepoUtil::get_config("branch.$branch.merge");
54085527 die "Aborting: No revision specified.\n" if !defined($merge_branch);
54095528 $merge_branch =~ s#^refs/heads/##;
54105529 $since = "$merge_remote/$merge_branch";
54175536
54185537 # Get the sha1sum where HEAD points now, make sure HEAD is valid
54195538 ($retval, $orig_head) =
5420 ExecUtil::execute_captured("$git_cmd rev-parse HEAD", ignore_ret => 1);
5539 ExecUtil::execute_captured("$GIT_CMD rev-parse HEAD", ignore_ret => 1);
54215540 die "Aborting: You have no commits on HEAD.\n" if $retval != 0;
54225541 chomp($orig_head);
54235542 $self->{orig_head} = $orig_head;
54245543
54255544 # Get the sha1sum where $since points now, make sure it is valid
54265545 ($retval, $since_sha1sum) =
5427 ExecUtil::execute_captured("$git_cmd rev-parse $self->{since}",
5546 ExecUtil::execute_captured("$GIT_CMD rev-parse $self->{since}",
54285547 ignore_ret => 1);
54295548 die "Invalid revision reference: $self->{since}\n" if $retval != 0;
54305549 chomp($since_sha1sum);
54315550
54325551 # Make sure user has no staged changes
5433 my $output = `$git_cmd diff --cached --quiet`;
5552 my $output = `$GIT_CMD diff --cached --quiet`;
54345553 die "Aborting: You have staged changes; please commit them first.\n" if $?;
54355554
54365555 # Ensure $self->{since} is an ancestor of HEAD
5437 my ($ret, $unmerged) =
5438 ExecUtil::execute_captured("$git_cmd rev-list HEAD..$self->{since} | " .
5439 " wc -l");
5440 chomp($unmerged);
5441 die "Aborting: $self->{since} is not an ancestor of HEAD.\n" if $unmerged;
5556 my $command = "$GIT_CMD rev-list HEAD..$self->{since} | wc -l";
5557 my ($ret, $unique_to_since) = ExecUtil::execute_captured($command);
5558 die "Couldn't parse '$command' output '$unique_to_since'"
5559 unless ($unique_to_since =~ /^\s*([0-9]+)$/);
5560 my $need_commits = $1;
5561 die "Aborting: $self->{since} is not an ancestor of HEAD.\n" if $need_commits;
54425562 die "Aborting: There are no commits since $self->{since}.\n"
54435563 if $orig_head eq $since_sha1sum;
54445564
54475567
54485568 sub run {
54495569 my $self = shift;
5450 my $package_name = ref($self);
54515570
54525571 # Fill out a basic log message
54535572 my ($fh, $filename) = main::tempfile();
54575576
54585577 EOF
54595578 close($fh);
5460 $ret = ExecUtil::execute("$git_cmd log --no-merges --pretty=format:" .
5579 my $ret = ExecUtil::execute("$GIT_CMD log --reverse --no-merges --pretty=format:" .
54615580 "'#commit %H%n#Author: %an <%ae>%n#Date: %ad%n%n%s%n%n%b' " .
54625581 " $self->{since}..$self->{orig_head} >> $filename");
54635582 exit $ret if $ret;
54645583
54655584 # Now, reset and commit
5466 $ret = ExecUtil::execute("$git_cmd reset --soft $self->{since}");
5585 $ret = ExecUtil::execute("$GIT_CMD reset --soft $self->{since}");
54675586 exit $ret if $ret;
5468 $ret = ExecUtil::execute("$git_cmd commit -F $filename --edit",
5587 $ret = ExecUtil::execute("$GIT_CMD commit -F $filename --edit",
54695588 ignore_ret => 1);
54705589
54715590 # Restore the branch pointer if the commit failed (e.g. empty log message)
5472 ExecUtil::execute("$git_cmd reset --soft $self->{orig_head}") if $ret != 0;
5591 ExecUtil::execute("$GIT_CMD reset --soft $self->{orig_head}") if $ret != 0;
54735592
54745593 unlink($filename);
54755594 return 0;
54815600 package stage;
54825601 @stage::ISA = qw(subcommand);
54835602 INIT {
5484 $command{stage} = {
5603 $COMMAND{stage} = {
54855604 new_command => 1,
54865605 section => 'modification',
54875606 about => 'Mark content in files as being ready for commit'
55015620 Description:
55025621 Marks the contents of the specified files as being ready to commit,
55035622 scheduling them for addition to the repository. (This is also known as
5504 staging.) When a directory is passed, all files in that directory or any
5505 subdirectory are recursively added.
5623 staging.) This step is often not neccessary, since 'eg commit' will fall
5624 back to unstaged changes if you have not staged anything. When a
5625 directory is passed, all files in that directory or any subdirectory are
5626 recursively added.
55065627
55075628 You can use 'eg unstage PATH...' to unstage files.
55085629
55475668
55485669 sub run {
55495670 my $self = shift;
5550 my $package_name = ref($self);
55515671
55525672 @ARGV = Util::quote_args(@ARGV);
5553 return ExecUtil::execute("$git_cmd add @ARGV", ignore_ret => 1);
5673 return ExecUtil::execute("$GIT_CMD add @ARGV", ignore_ret => 1);
55545674 }
55555675
55565676 ###########################################################################
55595679 package stash;
55605680 @stash::ISA = qw(subcommand);
55615681 INIT {
5562 $command{stash} = {
5682 $COMMAND{stash} = {
55635683 section => 'timesavers',
55645684 about => 'Save and revert local changes, or apply stashed changes',
55655685 };
55935713 When no arguments are specified to eg stash, the current changes are
55945714 saved away with a default description.
55955715
5716 WARNING: Using the default description can be a bad idea if you will not
5717 be reapplying the stash very soon. The default description provided for
5718 you is based on the commit message of the most recent commit, which has
5719 confused some users into believing that they have already incorporated
5720 changes from a stash and throwing the stash away (something that can be
5721 recovered from, but which involves some sleuthing and low-level commands
5722 like git-fsck and git-cat-file).
5723
55965724 Examples:
55975725 You have lots of changes that you're working on, then get an important
55985726 but simple bug report. You can stash away your current changes, fix the
56795807 # Parse options
56805808 #
56815809 my @args;
5682 my $result=main::GetOptions("--help" => sub { $self->help() });
5810 if (scalar(@ARGV) > 0 && $ARGV[0] ne "--") {
5811 main::GetOptions("--help" => sub { $self->help() });
5812 }
56835813
56845814 # Get the (sub)subcommand
56855815 if (scalar @ARGV == 0) {
56865816 $self->{subcommand} = 'save';
5817 } elsif ($ARGV[0] =~ /^--/) {
5818 $self->{subcommand} = "save";
56875819 } else {
56885820 $self->{subcommand} = shift @ARGV;
5821 if ($self->{subcommand} eq '-k') {
5822 $self->{subcommand} = "save";
5823 unshift @ARGV, '-k';
5824 }
56895825 push(@args, $self->{subcommand});
56905826
5691 if ($self->{subcommand} eq 'apply' && @ARGV > 0 && $ARGV[0] eq '--index') {
5692 push(@args, shift @ARGV);
5693 }
5694 if ($self->{subcommand} eq 'save' &&
5695 @ARGV > 0 && $ARGV[0] eq '--keep-index') {
5696 push(@args, shift @ARGV);
5697 }
5698 if ($self->{subcommand} eq 'show') {
5699 while(@ARGV > 0 && $ARGV[0] =~ /^-/) {
5827 # Pass all flags on to git
5828 while(@ARGV > 0 && $ARGV[0] =~ /^-/ && $ARGV[0] !~ /^--$/) {
5829 if ($self->{subcommand} eq 'list' && $ARGV[0] eq '--refs') {
5830 $self->{show_refs} = 1;
5831 shift @ARGV;
5832 } elsif ($self->{subcommand} eq 'list' && $ARGV[0] eq '--details') {
5833 $self->{show_details} = 1;
5834 shift @ARGV;
5835 } else {
57005836 push(@args, shift @ARGV);
57015837 }
57025838 }
57195855 if ((grep {$_ eq $self->{subcommand}} @commands_accepting_existing_stash) &&
57205856 scalar @ARGV > 0) {
57215857 my $stash_description = "@ARGV";
5858 my $single_word_desc = (scalar(@ARGV) == 1);
57225859 @ARGV = ();
5723 if ($stash_description =~ m#^stash\@{[^{]+}$#) {
5860 if ($stash_description =~ m#^stash\@{[^{]+}$# ||
5861 ($single_word_desc && RepoUtil::valid_ref($stash_description))) {
57245862 push(@args, $stash_description)
57255863 } else {
57265864 # Will need to compare arguments to existing stash descriptions...
57275865 print " >>Getting stash descriptions to compare to arguments:\n"
5728 if $debug;
5866 if $DEBUG;
57295867 my ($retval, $output) =
5730 ExecUtil::execute_captured("$eg_exec stash list --refs");
5868 ExecUtil::execute_captured("$EG_EXEC stash list --refs");
57315869 my @lines = split('\n', $output);
57325870 my %refs;
57335871 my %bad_refs;
57605898
57615899 push(@args, $refs{$stash_description});
57625900 }
5763 } elsif ($self->{subcommand} eq 'list' && @ARGV) {
5764 my $arg = shift @ARGV;
5765 if ($arg eq '--refs') {
5766 $self->{show_refs} = 1;
5767 } elsif ($arg eq '--details') {
5768 $self->{show_details} = 1;
5769 } else {
5770 unshift(@ARGV, $arg);
5771 }
57725901 }
57735902
57745903 # Add any unprocessed args to the arguments to use
57785907 @ARGV = @args;
57795908 }
57805909
5781 sub postprocess {
5910 sub run {
57825911 my $self = shift;
5783 my $output = shift;
5784
5785 if ($debug == 2) {
5786 print " >>(No commands to run, just data to print)<<\n";
5787 return;
5788 }
5789
5790 my @lines = split('\n', $output);
5912 my $package_name = ref($self);
5913 my $ret;
5914
5915 @ARGV = Util::quote_args(@ARGV);
57915916 if ($self->{subcommand} eq 'list') {
5917 my $output = "";
5918 open($OUTFH, '>', \$output) ||
5919 die "eg $package_name: cannot open \$OUTFH: $!";
5920
5921 $ret = ExecUtil::execute("$GIT_CMD $package_name @ARGV", ignore_ret => 1);
5922
5923 my @lines = split('\n', $output);
57925924 my $regex =
5793 qr#(stash\@{[^}]+}): (?:WIP )?[Oo]n [^ ]*: (?:[0-9a-f]+\.\.\. )?#;
5925 qr#(stash\@{[^}]+}): (?:WIP )?[Oo]n [^:]*: (?:[0-9a-f]+\.\.\. )?#;
57945926 foreach my $line (@lines) {
57955927 if ($self->{show_details}) {
57965928 print "$line\n";
58015933 }
58025934 }
58035935 } else {
5804 foreach my $line (@lines) {
5805 print "$line\n";
5806 }
5807 }
5936 $ret = ExecUtil::execute("$GIT_CMD $package_name @ARGV", ignore_ret => 1);
5937 }
5938 return $ret;
58085939 }
58095940
58105941 ###########################################################################
58135944 package status;
58145945 @status::ISA = qw(subcommand);
58155946 INIT {
5816 $command{status} = {
5947 $COMMAND{status} = {
58175948 section => 'discovery',
58185949 about => 'Summarize current changes'
58195950 };
5951 $ALIAS{'st'} = "status";
58205952 }
58215953
58225954 sub new {
58295961
58305962 Description:
58315963 Show the current state of the project. In addition to showing the
5832 currently active branch, this command will list files with content in any
5833 of the following states:
5964 currently active branch, whether you have unpushed local commits,
5965 whether you have stashed any sets of changes away (see 'eg help
5966 stash'), this command will list files with content in any of the
5967 following states:
58345968
58355969 Unknown files
58365970 Files that are not explicitly ignored (i.e. do not appear in an
58525986 subdirectories that are tracked under their own git repository, and
58535987 that are being tracked via use of the 'git submodule' command.
58545988
5855 Changed but not updated (\"unstaged\")
5989 Changes not staged for commit (\"unstaged\")
58565990 Files whose contents have been modified in the working copy.
58575991
58585992 (Advanced usage note) If you explicitly mark all the changes in a
58605994 list and will instead appear in the \"staged\" list (see below).
58615995 However, a file can appear in both the unstaged and staged lists if
58625996 only part of the changes in the file are marked as ready for commit.
5997
5998 Unmerged paths (files with conflicts)
5999 Files which could not be automatically merged. If such files are
6000 text files, they will have the typical conflict markers. These
6001 files need to be manually edited to give them the correct contents,
6002 and then the user should inform git that the conflicts are resolved
6003 by running 'eg resolved FILENAME'.
58636004
58646005 Changes ready to be committed (\"staged\")
58656006 Files with content changes that have explicitly been marked as ready
59116052 sub preprocess {
59126053 my $self = shift;
59136054
6055 my @old_argv = @ARGV;
6056 my $no_filter = 0;
59146057 Getopt::Long::Configure("permute");
59156058 my $result = main::GetOptions(
5916 "--help" => sub { $self->help() },
5917 "v" => sub { print STDERR "-v option is not supported.\n"; exit 1 },
6059 "help|h" => sub { $self->help() },
6060 "short|s" => sub { $no_filter = 1 },
6061 "suno|sunormal|suall" => sub { $no_filter = 1 }, # git status parser allows combining -s and -u options, somewhat weirdly
6062 "porcelain" => sub { $no_filter = 1 },
6063 "z" => sub { $no_filter = 1 },
59186064 );
6065 @ARGV = @old_argv;
6066 $self->{no_filter} = $no_filter;
59196067 }
59206068
59216069 sub run {
59226070 my $self = shift;
5923 my $package_name = ref($self);
6071
6072 -t STDOUT and $ENV{"GIT_PAGER_IN_USE"}=1;
6073 return $self->SUPER::run() if $self->{no_filter};
59246074
59256075 $self->{special_state} = RepoUtil::get_special_state($self->{git_dir});
59266076
59276077 @ARGV = Util::quote_args(@ARGV);
5928 -t STDOUT and $ENV{"GIT_PAGER_IN_USE"}=1;
5929 return ExecUtil::execute("$git_cmd status @ARGV", ignore_ret => 1);
6078 return ExecUtil::execute("$GIT_CMD status @ARGV", ignore_ret => 1);
59306079 }
59316080
59326081 sub postprocess {
59336082 my $self = shift;
59346083 my $output = shift;
59356084
5936 if ($debug == 2) {
6085 # If we can't parse the git status output, what we tell the user...
6086 my $workaround_msg =
6087 "You'll need to use an older git or a newer eg or 'git status'.";
6088
6089 if ($DEBUG == 2) {
59376090 print " >>(No commands to run, just data to print)<<\n";
59386091 return;
59396092 }
59406093
6094 if ($self->{no_filter}) {
6095 print $output;
6096 return;
6097 }
6098
59416099 my $branch;
59426100 my $initial_commit = 0;
5943 my %files = ( unknown => undef, unstaged => undef, staged => undef );
6101 my @sections;
6102 my %files;
6103 my %section_mapping = (
6104 'Untracked files:' => 'Unknown files:',
6105 'Changes to be committed:' => 'Changes ready to be committed ("staged"):',
6106 'Changed but not updated:' => 'Changes not staged for commit ("unstaged"):',
6107 'Changes not staged for commit:' => 'Changes not staged for commit ("unstaged"):',
6108 'Unmerged paths:' => 'Unmerged paths (files with conflicts):'
6109 );
6110
59446111 my @basic_info;
6112 my @diff_info;
59456113
59466114 # Exit early if git status had an error
59476115 if ($output =~ m/^fatal:/) {
59486116 print STDERR $output;
59496117 exit 128;
59506118 }
6119
6120 my $status_hints = RepoUtil::get_config("advice.statusHints");
6121 $status_hints = (!defined($status_hints) || $status_hints eq "true");
59516122
59526123 # Parse the output
59536124 my @lines = split('\n', $output);
59556126 while (@lines) {
59566127 my $line = shift @lines;
59576128 my $section = undef;
6129 my $title;
59586130
59596131 if ($line =~ m/^# On branch (.*)$/) {
59606132 $branch = $1;
59616133 } elsif ($line =~ m/^# Initial commit$/) {
59626134 $initial_commit = 1;
5963 } elsif ($line =~ m/^# Untracked files:$/) {
6135 } elsif ($line =~ m/^# ([A-Z].*:)$/) {
59646136 $cur_state = 1;
5965 $section = 'unknown';
5966 $title = "Unknown files:";
5967 } elsif ($line =~ m/^# Modified submodules:$/) {
5968 $cur_state = 3;
5969 $section = 'submodules';
5970 $title = "Modified submodules:";
5971 } elsif ($line =~ m/^# Changes to be committed:$/) {
5972 $cur_state = 2;
5973 $section = 'staged';
5974 $title = 'Changes ready to be committed ("staged"):';
5975 } elsif ($line =~ m/^# Changed but not updated:$/) {
5976 $cur_state = 2;
5977 $section = 'unstaged';
5978 $title = 'Changed but not updated ("unstaged"):';
6137 $title = $section_mapping{$1} || $1;
6138 $section = $title;
59796139 } elsif ($cur_state < 0) {
59806140 next if $line !~ m/^# (.+)/;
59816141 push(@basic_info, $1);
6142 } elsif ($line =~ m/^no changes added to commit/ ||
6143 $line =~ m/^# Untracked files not listed/) {
6144 next; # Skip this line
6145 } elsif ($line =~ m#^(?:\e\[.*?m)?diff --git a/#) {
6146 push(@diff_info, $line);
6147 push(@diff_info, @lines);
6148 last;
6149 } else {
6150 die "ERROR: Cannot parse git status output.\n" .
6151 "$workaround_msg\n" .
6152 "Remaining unparsed lines:\n$line\n" . join("\n", @lines) . "\n";
59826153 }
59836154
59846155 # If we're inside a section type, parse it
59856156 if ($cur_state > 0) {
6157 push (@sections, $section);
59866158 my @section_files;
59876159 my $hints;
59886160
59896161 # Parse the hints first
59906162 $line = shift @lines;
5991 while ($line =~ m/^#\s+\(use ".*/) {
5992 $hints .= $line;
5993 $line = shift @lines;
6163 if ($status_hints) {
6164 while ($line =~ m/^#\s+\((?:use "|commit).*/) {
6165 $hints .= $line;
6166 $line = shift @lines;
6167 }
6168 die("Bug parsing git status output.\n$workaround_msg\n") if $line ne '#';
6169 $line = shift @lines; # Get rid of blank line
59946170 }
5995 die("Bug parsing git status output") if $line ne '#';
5996 $line = shift @lines; # Get rid of blank line
5997
5998 while (defined $line && $line =~ m/^#.+$/) {
5999 if ($cur_state == 1) {
6000 if ($line =~ m/^#(\s+)(.*)/) {
6001 my $file = $2;
6002 push @section_files, "$1$file";
6171
6172 while (defined $line && $line =~ m/^(?:\e\[.*?m)?#.+$/) {
6173 if ($line =~ m/^(?:\e\[.*?m)?#(\s+)(.*)/) {
6174 my $space = $1;
6175 my $file = $2;
6176
6177 # Remove leading space character for submodule changes
6178 # (There's no real reason to do this other than laziness in
6179 # updating test file results; output looks fine either way.)
6180 $space =~ s/^[ ]//;
6181 # Workaround the file not have proper terminating color escape sequence
6182 if ($file =~ /^\s*\e\[.*?m/ && $file !~ /\e\[m$/) {
6183 $file .= "\e[m";
60036184 }
6004 } elsif ($cur_state == 2) {
6005 if ($line =~ m/^#(\s+.*:\s+)(.*)/) {
6006 my $file = $2;
6007 push(@section_files, "$1$file");
6008 }
6009 } elsif ($cur_state == 3) {
6010 if ($line =~ m/^#\s(.*)/) {
6011 my $sub_info = $1;
6012 push(@section_files, "$sub_info");
6013 }
6185 push @section_files, "$space$file";
60146186 }
60156187 $line = shift @lines;
6188 unshift(@lines, $line) if $line && $line =~ m#^(?:\e\[.*?m)?diff --git a/#;
60166189 }
60176190
60186191 if (defined($files{$section})) {
60196192 push(@{$files{$section}{'file_list'}}, @section_files);
60206193 } else {
6021 $files{$section} = { title => $title,
6194 $files{$section} = { title => $title, # may be undef
60226195 hint => $hints,
60236196 file_list => \@section_files };
60246197 }
60256198
60266199 # Record that we finished parsing this section
60276200 $cur_state = 0;
6201 }
6202 }
6203
6204 # Split the unknown files into those that are newly created and those that
6205 # have been around
6206 if (defined($files{'Unknown files:'})) {
6207 # Get the list of unknown files that have been around for a while
6208 my ($cur_dir, $top_dir, $git_dir) = RepoUtil::get_dirs();
6209 my %old_unknown;
6210 if (-f "$git_dir/info/ignored-unknown") {
6211 my @old_unknown_files = `cat "$git_dir/info/ignored-unknown"`;
6212 chomp(@old_unknown_files);
6213 @old_unknown_files =
6214 Util::reroot_paths__from_to_files($top_dir, $cur_dir, @old_unknown_files);
6215 map { $old_unknown{$_} = 1 } @old_unknown_files;
6216 }
6217
6218 my @new_unknowns;
6219 my @old_unknowns;
6220 foreach my $fileline (@{$files{'Unknown files:'}{'file_list'}}) {
6221 $fileline =~ m#(\s+(?:\e\[.*?m)?)(.*?)((?:\e\[m)?)$# ||
6222 die "Failed parsing git status output: '$fileline'\n$workaround_msg\n";
6223 if ($old_unknown{$2}) {
6224 push(@old_unknowns, $fileline);
6225 } else {
6226 push(@new_unknowns, $fileline);
6227 }
6228 }
6229
6230 my ($index) = grep $sections[$_] eq "Unknown files:", 0 .. $#sections;
6231 splice(@sections, $index, 1);
6232 if (@new_unknowns) {
6233 $files{'new_unknowns'} = { title => 'Newly created unknown files:',
6234 file_list => \@new_unknowns };
6235 splice(@sections, $index++, 0, 'new_unknowns');
6236 }
6237 if (@old_unknowns) {
6238 $files{'old_unknowns'} = { title => 'Unknown files:',
6239 file_list => \@old_unknowns };
6240 splice(@sections, $index, 0, 'old_unknowns');
60286241 }
60296242 }
60306243
60376250 foreach my $line (@basic_info) {
60386251 print "($line)\n";
60396252 }
6253 my ($retval, $num_stashes) =
6254 ExecUtil::execute_captured("$GIT_CMD stash list | wc -l");
6255 chomp($num_stashes);
6256 if ($num_stashes > 0) {
6257 print "(You have $num_stashes stash(es). Use 'eg stash list' to see them.)\n";
6258 }
60406259
60416260 # Print out info about any special state we're in
60426261 my $notice = "";
60436262 if (defined $self->{special_state}) {
60446263 my ($highlight, $reset) = ("", "");
60456264 if (-t STDOUT) {
6046 $highlight=`$git_cmd config --get-color color.status.header "red reverse"`;
6047 $reset=`$git_cmd config --get-color "" "reset"`;
6265 $highlight=`$GIT_CMD config --get-color color.status.header "red reverse"`;
6266 $reset=`$GIT_CMD config --get-color "" "reset"`;
60486267 }
60496268
60506269 $notice .= "($highlight";
60686287 print $notice;
60696288 }
60706289
6071 # Split the unknown files into those that are newly created and those that
6072 # have been around
6073 if (defined($files{'unknown'})) {
6074 # Get the list of unknown files that have been around for a while
6075 my ($cur_dir, $top_dir, $git_dir) = RepoUtil::get_dirs();
6076 my %old_unknown;
6077 if (-f "$git_dir/info/ignored-unknown") {
6078 my @old_unknown_files = `cat "$git_dir/info/ignored-unknown"`;
6079 chomp(@old_unknown_files);
6080 @old_unknown_files =
6081 Util::reroot_paths__from_to_files($top_dir, $cur_dir, @old_unknown_files);
6082 map { $old_unknown{$_} = 1 } @old_unknown_files;
6083 }
6084
6085 my @new_unknowns;
6086 my @old_unknowns;
6087 foreach my $fileline (@{$files{'unknown'}{'file_list'}}) {
6088 $fileline =~ m#(\s+(?:\e\[.*?m)?)(.*?)((?:\e\[m)?)$# ||
6089 die "Failed parsing git status output: '$fileline'\n";
6090 if ($old_unknown{$2}) {
6091 push(@old_unknowns, $fileline);
6092 } else {
6093 push(@new_unknowns, $fileline);
6094 }
6095 }
6096 if (@new_unknowns) {
6097 $files{'new_unknowns'} = { title => 'Newly created unknown files:',
6098 file_list => \@new_unknowns };
6099 }
6100 if (@old_unknowns) {
6101 $files{'old_unknowns'} = { title => 'Unknown files:',
6102 file_list => \@old_unknowns };
6103 }
6104 }
6105
61066290 # Print out all the various changes
61076291 my $linecount = 0;
6108 foreach my $section ('staged', 'unstaged', 'submodules',
6109 'new_unknowns', 'old_unknowns') {
6292 foreach my $section (@sections) {
61106293 if (defined($files{$section})) {
61116294 print "$files{$section}{'title'}\n";
61126295 $linecount += 1;
61226305 print $notice;
61236306 }
61246307
6308 # Print the diff if we're running with the -v option
6309 print join("\n", @diff_info)."\n" if (@diff_info);
61256310 }
61266311
61276312 ###########################################################################
61306315 package switch;
61316316 @switch::ISA = qw(subcommand);
61326317 INIT {
6133 $command{switch} = {
6318 $COMMAND{switch} = {
61346319 new_command => 1,
61356320 section => 'projects',
61366321 about => 'Switch the working copy to another branch'
61376322 };
6323 $ALIAS{'sw'} = "switch";
61386324 }
61396325
61406326 sub new {
62106396
62116397 sub run {
62126398 my $self = shift;
6213 my $package_name = ref($self);
62146399
62156400 @ARGV = Util::quote_args(@ARGV);
6216 return ExecUtil::execute("$git_cmd checkout @ARGV", ignore_ret => 1);
6401 return ExecUtil::execute("$GIT_CMD checkout @ARGV", ignore_ret => 1);
62176402 }
62186403
62196404 ###########################################################################
62226407 package tag;
62236408 @tag::ISA = qw(subcommand);
62246409 INIT {
6225 $command{tag} = {
6410 $COMMAND{tag} = {
62266411 unmodified_behavior => 1,
62276412 extra => 1,
62286413 section => 'modification',
62746459 }
62756460
62766461 ###########################################################################
6462 # track #
6463 ###########################################################################
6464 package track;
6465 @track::ISA = qw(subcommand);
6466 INIT {
6467 $COMMAND{track} = {
6468 new_command => 1,
6469 extra => 1,
6470 section => 'projects',
6471 about => 'Set which remote branch a local branch tracks'
6472 };
6473 }
6474
6475 sub new {
6476 my $class = shift;
6477 my $self = $class->SUPER::new(git_repo_needed => 1, git_equivalent => '', @_);
6478 bless($self, $class);
6479 $self->{'help'} = "
6480 Usage:
6481 eg track (--show [LOCAL_BRANCH] | --show-all)
6482 eg track [LOCAL_BRANCH] REMOTE_TRACKING_BRANCH
6483 eg track --unset [LOCAL_BRANCH]
6484
6485 Description:
6486 eg track helps manage which remote branches your local branches
6487 track. Having a local branch track a remote branch means that when
6488 the local branch is the active branch, that the corresponding remote
6489 branch is the default push or pull location for eg push or eg pull.
6490
6491 There are three different things eg track can do, each corresponding to
6492 one of the usage forms listed above: list which remote branch a local
6493 branch tracks, make a local branch track some remote branch, or make a
6494 local branch no longer track any remote branch.
6495
6496 If LOCAL_BRANCH is not specified, the currently active branch is
6497 assumed.
6498
6499 Examples:
6500 Show which remote branches all local branches are tracking
6501 \$ eg track --show-all
6502
6503 Show which remote branch the local branch 'stable' tracks
6504 \$ eg track --show stable
6505
6506 Make your currently active branch track the 'magic' branch of the 'jim'
6507 repository (see 'eg help remote' for setting up nicknames like 'jim' for
6508 remote repositories)
6509 \$ eg track jim/magic
6510
6511 Make your branch 'bugfix' track the 'master' branch of the 'origin'
6512 repository (note that 'origin' is the repository you cloned from, unless
6513 you've explicitly changed that using the eg remote command or some other
6514 low-level means):
6515 \$ eg track bugfix origin/master
6516
6517 Have your 'random-changes' branch stop tracking any remote branch:
6518 \$ eg track --unset random-changes
6519 ";
6520 $self->{'differences'} = '
6521 eg track is unique to eg; git does not have a similar command.
6522 ';
6523 return $self;
6524 }
6525
6526 sub preprocess {
6527 my $self = shift;
6528
6529 # Check for the --help arg
6530 my $mode = "set";
6531 my ($local_branch, $remote_branch, $remote);
6532 Getopt::Long::Configure("permute");
6533 my $result = main::GetOptions(
6534 "--help" => sub { $self->help() },
6535 "show" => sub { $mode = "show"; },
6536 "show-all" => sub { $mode = "show-all"; },
6537 "unset" => sub { $mode = "unset"; });
6538
6539 # Get the remote branch to track, if we're setting up tracking info
6540 if ($mode eq "set") {
6541 my ($ret, $remote_tracking_branch);
6542
6543 # Sanity checks
6544 die "Error: Too many arguments. Run 'eg help track'.\n" if (@ARGV > 2);
6545 die "Error: Insufficient arguments. Run 'eg help track'.\n" if (@ARGV < 1);
6546
6547 # Get the remote tracking branch, and sanity check it
6548 $remote_tracking_branch = pop @ARGV;
6549 die "Error: Invalid remote tracking branch '$remote_tracking_branch'\n" .
6550 "Correct format for remote tracking branches is:\n" .
6551 " REMOTENAME/REMOTEBRANCHNAME\n" if $remote_tracking_branch !~ '/';
6552
6553 # Split remote tracking branch into remote name and remote branch name
6554 ($remote, $remote_branch) = split('/', $remote_tracking_branch, 2);
6555
6556 # Make sure the remote is a valid name
6557 Util::push_debug(new_value => 0);
6558 $ret = ExecUtil::execute("$GIT_CMD remote | grep '^$remote\$' > /dev/null",
6559 ignore_ret => 1);
6560 die "Error: '$remote' is not a valid remote name.\n" .
6561 "(Use 'eg remote' to find valid remote names).\n" if $ret;
6562 Util::pop_debug();
6563 }
6564
6565 # Get the local branch to operate on
6566 $local_branch = shift @ARGV;
6567 if (!$local_branch && $mode ne "show-all") {
6568 $local_branch = RepoUtil::current_branch();
6569 }
6570 # Make sure $local_branch is defined and has a valid value
6571 Util::push_debug(new_value => 0);
6572 if (!$local_branch) {
6573 die "Error: There is no active branch, so it cannot track anything.\n";
6574 } elsif (!RepoUtil::valid_ref("refs/heads/$local_branch")) {
6575 die "Error: The branch '$local_branch' is not (yet) a valid local branch.\n";
6576 }
6577 Util::pop_debug();
6578
6579 $self->{mode} = $mode;
6580 $self->{branch} = $local_branch;
6581 if ($mode eq "set") {
6582 $self->{remote_branch} = "refs/heads/$remote_branch";
6583 $self->{remote} = $remote
6584 }
6585 }
6586
6587 sub run {
6588 my $self = shift;
6589
6590 my ($ret, $output);
6591 my ($mode, $branch, $remote_branch, $remote) =
6592 ($self->{mode}, $self->{branch}, $self->{remote_branch}, $self->{remote});
6593
6594 if ($mode eq "show-all") {
6595 $branch = ".*";
6596 $mode = "show";
6597 }
6598
6599 if ($mode eq "show") {
6600 my %tracking;
6601
6602 # Get the remote tracking flags
6603 ($ret, $output) = ExecUtil::execute_captured(
6604 "$GIT_CMD config --get-regexp '^branch\.$branch\.(remote|merge)'",
6605 ignore_ret => 1);
6606 chomp($output);
6607
6608 # Exit early if we're in --translate mode
6609 if ($DEBUG == 2) {
6610 print " >>(No more commands to run, " .
6611 "just output to parse and print)<<\n";
6612 }
6613
6614 # Check if there are no matches
6615 if ($ret) {
6616 my $message = "Branch $branch is not";
6617 $message = "No branches are" if $branch eq ".*";
6618 print "$message set to track anything.\n";
6619 return 0;
6620 }
6621
6622 # Fill the %tracking hash
6623 my @lines = split('\n', $output);
6624 foreach my $line (@lines) {
6625 $line =~ /^branch\.(.*)\.(remote|merge) (.*)$/
6626 or die "Bad output '$line'!\n";
6627
6628 $tracking{$1}{$2} = $3;
6629 }
6630
6631 # Show all the tracking information
6632 foreach my $bname (sort keys %tracking) {
6633 my $remote = $tracking{$bname}{'remote'} || "''";
6634 my $remote_branch = $tracking{$bname}{'merge'} || "''";
6635 print "Branch $bname tracks $remote_branch of remote $remote.\n";
6636 }
6637 return 0;
6638
6639 } elsif ($mode eq "set") {
6640 $ret = ExecUtil::execute(
6641 "$GIT_CMD config branch.$branch.remote $remote", ignore_ret => 1);
6642 $ret |= ExecUtil::execute(
6643 "$GIT_CMD config branch.$branch.merge $remote_branch", ignore_ret => 1);
6644
6645 if (!$ret && $DEBUG < 2) {
6646 print "$branch now set to track " .
6647 "branch $remote_branch of remote $remote.\n";
6648 }
6649 return $ret;
6650 } elsif ($mode eq "unset") {
6651 ExecUtil::execute(
6652 "$GIT_CMD config --unset branch.$branch.remote", ignore_ret => 1);
6653 ExecUtil::execute(
6654 "$GIT_CMD config --unset branch.$branch.merge", ignore_ret => 1);
6655
6656 if ($DEBUG < 2) {
6657 print "$branch no longer tracks any remote branch.\n";
6658 }
6659 return 0;
6660 }
6661 }
6662
6663 ###########################################################################
62776664 # unstage #
62786665 ###########################################################################
62796666 package unstage;
62806667 @unstage::ISA = qw(revert);
62816668 INIT {
6282 $command{unstage} = {
6669 $COMMAND{unstage} = {
62836670 new_command => 1,
62846671 extra => 1,
62856672 section => 'modification',
63396726 package update;
63406727 @update::ISA = qw(subcommand);
63416728 INIT {
6342 $command{update} = {
6729 $COMMAND{update} = {
63436730 new_command => 1,
63446731 extra => 1,
63456732 section => 'compatibility',
64366823 exit 1;
64376824 }
64386825
6439 if ($debug) {
6826 if ($DEBUG) {
64406827 print " >>Commands to determine where to update from:\n";
64416828 }
64426829
64546841 # otherwise throw an error
64556842 my ($quoted_repo) = Util::quote_args("$self->{repository}");
64566843 my ($ret, $output) =
6457 ExecUtil::execute_captured("$git_cmd ls-remote -h $quoted_repo");
6844 ExecUtil::execute_captured("$GIT_CMD ls-remote -h $quoted_repo");
64586845 if ($ret == 0) {
64596846 my @remote_refs = split('\n', $output);
64606847 if (@remote_refs == 1) {
64866873 # Get value to set ORIG_HEAD to (unless we are on the initial commit)
64876874 Util::push_debug(new_value => 0);
64886875 my ($retval, $orig_sha1sum) =
6489 ExecUtil::execute_captured("$git_cmd rev-parse HEAD", ignore_ret => 1);
6876 ExecUtil::execute_captured("$GIT_CMD rev-parse HEAD", ignore_ret => 1);
64906877 my $has_orig_head = ($retval == 0);
64916878 Util::pop_debug();
64926879
64936880 # Do the fetch && reset, making sure to set ORIG_HEAD
64946881 my ($ret, $output) =
6495 ExecUtil::execute_captured("$git_cmd fetch $self->{repository} " .
6882 ExecUtil::execute_captured("$GIT_CMD fetch $self->{repository} " .
64966883 "$self->{merge_branch}:$self->{local_branch}",
64976884 ignore_ret => 1);
64986885 if ($output =~ /\[rejected\].*\(non fast forward\)/) {
65026889 die "Error updating (output = $output); please report the bug, and\n" .
65036890 "try using 'eg pull' instead.\n";
65046891 } else {
6505 $ret = ExecUtil::execute_captured("$git_cmd reset --hard " .
6892 $ret = ExecUtil::execute_captured("$GIT_CMD reset --hard " .
65066893 "$self->{local_branch}");
6507 if ($has_orig_head && $debug < 2) {
6894 if ($has_orig_head && $DEBUG < 2) {
65086895 open(ORIG_HEAD, "> $self->{git_dir}/ORIG_HEAD");
65096896 print ORIG_HEAD $output;
65106897 close(ORIG_HEAD);
65116898 }
6512 print "Updated the current branch.\n" if ($debug < 2);
6899 print "Updated the current branch.\n" if ($DEBUG < 2);
65136900 }
65146901 return $ret;
65156902 }
65306917 bless($self, $class);
65316918 }
65326919
6533 # Override help because we don't want to both definining $command{help}
6920 # Override help because we don't want to both definining $COMMAND{help}
65346921 sub help {
65356922 my $self = shift;
65366923
65516938 sub run {
65526939 my $self = shift;
65536940
6554 print "eg version $version\n" if $debug < 2;
6555 print " >>(We can print the eg version directly)<<\n" if $debug == 2;
6941 print "eg version $VERSION\n" if $DEBUG < 2;
6942 print " >>(We can print the eg version directly)<<\n" if $DEBUG == 2;
65566943 return $self->SUPER::run();
65576944 }
65586945
65726959 package ExecUtil;
65736960
65746961 # _execute_impl is the guts for execute() and execute_captured()
6575 sub _execute_impl {
6962 sub _execute_impl ($@) {
65766963 my ($command, @opts) = @_;
65776964 my ($ret, $output);
65786965 my %options = ( ignore_ret => 0, capture_output => 0, @opts );
65796966
6580 if ($debug) {
6967 if ($DEBUG) {
65816968 print " >>Running: '$command'<<\n";
6582 return $options{capture_output} ? (0, "") : 0 if $debug == 2;
6969 return $options{capture_output} ? (0, "") : 0 if $DEBUG == 2;
65836970 }
65846971
65856972 #
65936980 $output = `$command 2>&1`;
65946981 }
65956982 $ret = $?;
6596 } elsif (defined $outfh) {
6983 } elsif (defined $OUTFH) {
65976984 open(OUTPUT, "$command 2>&1 |");
65986985 while (<OUTPUT>) {
6599 print $outfh $_;
6986 print $OUTFH $_;
66006987 }
66016988 close(OUTPUT);
66026989 $ret = $?;
66187005 else {
66197006 $ret = ($ret >> 8);
66207007 if (! $options{ignore_ret}) {
6621 print STDERR "eg: failed ($ret)\n" if $debug;
7008 print STDERR "eg: failed ($ret)\n" if $DEBUG;
66227009 if ($ret >> 8 != 0) {
66237010 print STDERR "eg: command ($command) failed\n";
66247011 }
66347021
66357022 # executes a command, capturing its output (both STDOUT and STDERR),
66367023 # returning both the return value and the output
6637 sub execute_captured {
7024 sub execute_captured ($@) {
66387025 my ($command, @options) = @_;
66397026 return _execute_impl($command, capture_output => 1, @options);
66407027 }
66417028
66427029 # executes a command, returning its chomped output
6643 sub output {
7030 sub output ($@) {
66447031 my ($command, @options) = @_;
66457032 my ($ret, $output) = execute_captured($command, @options);
66467033 die "Failed executing '$command'!\n" if $ret != 0;
66497036 }
66507037
66517038 # executes a command (output not captured), returning its return value
6652 sub execute {
7039 sub execute ($@) {
66537040 my ($command, @options) = @_;
66547041 return _execute_impl($command, @options);
66557042 }
66607047 package RepoUtil;
66617048
66627049 # current_branch: Get the currently active branch
6663 sub current_branch {
6664 Util::push_debug(new_value => $debug ? 1 : 0);
6665 my ($ret, $output) = ExecUtil::execute_captured("$git_cmd symbolic-ref HEAD",
7050 sub current_branch () {
7051 Util::push_debug(new_value => $DEBUG ? 1 : 0);
7052 my ($ret, $output) = ExecUtil::execute_captured("$GIT_CMD symbolic-ref HEAD",
66667053 ignore_ret => 1);
66677054 Util::pop_debug();
66687055
66727059 return $output;
66737060 }
66747061
6675 sub git_dir {
7062 sub git_dir (%) {
66767063 my $options = {force => 0, @_}; # Hashref initialized as we're told
66777064 if (!$options->{force}) {
6678 return $gitdir if ($gitdir);
7065 return $GITDIR if ($GITDIR);
66797066 }
66807067
66817068 Util::push_debug(new_value => 0);
66827069 my ($ret, $output) =
6683 ExecUtil::execute_captured("$git_cmd rev-parse --git-dir", ignore_ret => 1);
7070 ExecUtil::execute_captured("$GIT_CMD rev-parse --git-dir", ignore_ret => 1);
66847071 Util::pop_debug();
66857072
66867073 return undef if $ret != 0;
66887075 return $output;
66897076 }
66907077
6691 sub get_dirs {
7078 sub get_dirs () {
66927079 my $options = {force => 0, @_}; # Hashref initialized as we're told
66937080
6694 if ($curdir && !$options->{force}) {
6695 return ($curdir, $topdir, $gitdir);
7081 if ($CURDIR && !$options->{force}) {
7082 return ($CURDIR, $TOPDIR, $GITDIR);
66967083 }
66977084
66987085 Util::push_debug(new_value => 0);
66997086
6700 $curdir = main::getcwd();
7087 $CURDIR = main::getcwd();
67017088
67027089 # Get the toplevel repository directory
6703 $topdir = $curdir;
7090 $TOPDIR = $CURDIR;
67047091 my ($ret, $rel_dir) =
6705 ExecUtil::execute_captured("$git_cmd rev-parse --show-prefix",
7092 ExecUtil::execute_captured("$GIT_CMD rev-parse --show-prefix",
67067093 ignore_ret => 1);
67077094 chomp($rel_dir);
67087095 if ($ret != 0) {
6709 $topdir = undef;
7096 $TOPDIR = undef;
67107097 } elsif ($rel_dir) {
67117098 $rel_dir =~ s#/$##; # Remove trailing slash
6712 $topdir =~ s#\Q$rel_dir\E$##;
6713 $topdir =~ s#/$##; # Remove trailing slash
6714 }
6715
6716 $gitdir = git_dir(force => $options->{force});
7099 $TOPDIR =~ s#\Q$rel_dir\E$##;
7100 $TOPDIR =~ s#/$##; # Remove trailing slash
7101 }
7102
7103 $GITDIR = git_dir(force => $options->{force});
67177104
67187105 Util::pop_debug();
67197106
6720 return ($curdir, $topdir, $gitdir);
6721 }
6722
6723 sub initial_commit {
6724 my @output = `$git_cmd rev-parse --verify -q HEAD`;
7107 return ($CURDIR, $TOPDIR, $GITDIR);
7108 }
7109
7110 sub initial_commit () {
7111 my @output = `$GIT_CMD rev-parse --verify -q HEAD`;
67257112 return $?;
67267113 }
67277114
6728 sub valid_ref {
7115 sub valid_ref ($) {
67297116 my ($ref) = @_;
67307117 my ($ret, $sha1sum) =
6731 ExecUtil::execute_captured("$git_cmd rev-parse --verify -q $ref",
7118 ExecUtil::execute_captured("$GIT_CMD rev-parse --verify -q $ref",
67327119 ignore_ret => 1);
67337120 return $ret == 0;
67347121 }
67357122
6736 sub files_modified() {
6737 my @output = `$git_cmd status -a`;
7123 sub files_modified () {
7124 my @output = `$GIT_CMD commit --dry-run -a`;
67387125 return $? == 0;
67397126 }
67407127
6741 sub merge_branches {
7128 sub merge_branches () {
67427129 my $git_dir = RepoUtil::git_dir();
67437130 my $active_branch = RepoUtil::current_branch() || 'HEAD';
67447131 my @merge_branches =
6745 `cat "$git_dir/MERGE_HEAD" | $git_cmd name-rev --stdin`;
7132 `cat "$git_dir/MERGE_HEAD" | $GIT_CMD name-rev --stdin`;
67467133 @merge_branches = map { /^[0-9a-f]* \((.*)\)$/ && $1 } @merge_branches;
67477134 my @all_merge_branches = ($active_branch, @merge_branches);
67487135 return @all_merge_branches;
67497136 }
67507137
6751 sub get_special_state {
7138 sub get_special_state ($) {
67527139 my $git_dir = shift;
67537140
67547141 my $special_state;
67727159 return $special_state;
67737160 }
67747161
6775 sub get_config {
7162 sub get_config ($) {
67767163 my $key = shift;
6777 my ($ret, $output) = ExecUtil::execute_captured("$git_cmd config --get $key",
7164 my ($ret, $output) = ExecUtil::execute_captured("$GIT_CMD config --get $key",
67787165 ignore_ret => 1);
67797166 return undef if $ret != 0;
67807167 chomp($output);
67817168 return $output;
67827169 }
67837170
6784 sub set_config {
7171 sub set_config ($$) {
67857172 my $key = shift;
67867173 my $value = shift;
6787 ExecUtil::execute("$git_cmd config $key \"$value\"");
6788 }
6789
6790 sub unset_config {
7174 ExecUtil::execute("$GIT_CMD config $key \"$value\"");
7175 }
7176
7177 # XXX unused?
7178 sub unset_config ($) {
67917179 my $key = shift;
6792 ExecUtil::execute("$git_cmd config --unset $key", ignore_ret => 1);
6793 }
6794
6795 sub get_only_branch {
7180 ExecUtil::execute("$GIT_CMD config --unset $key", ignore_ret => 1);
7181 }
7182
7183 sub get_only_branch ($$) {
67967184 my $repository = shift;
67977185 my $check_type = shift;
67987186
6799 if ($debug == 2) {
6800 print " >>Running: '$git_cmd ls-remote -h $repository'<<\n";
7187 if ($DEBUG == 2) {
7188 print " >>Running: '$GIT_CMD ls-remote -h $repository'<<\n";
68017189 return;
68027190 }
68037191
68057193 # otherwise throw an error
68067194 my ($quoted_repo) = Util::quote_args("$repository");
68077195 my ($ret, $output) =
6808 ExecUtil::execute_captured("$git_cmd ls-remote -h $quoted_repo",
7196 ExecUtil::execute_captured("$GIT_CMD ls-remote -h $quoted_repo",
68097197 capture_stdout_only => 1, ignore_ret => 1);
6810 die "Could not determine remote branches from repository '$repository'\n"
6811 if $ret != 0;
7198 die "Aborting: Could not determine remote branches " .
7199 "from repository '$repository'\n" if $ret != 0;
68127200 my @remote_refs = split('\n', $output);
68137201
68147202 die "'$repository' has no branches to $check_type!\n" if @remote_refs == 0;
68387226 return $remote_branches[0];
68397227 }
68407228
6841 sub get_default_push_pull_repository {
7229 sub get_default_push_pull_repository () {
68427230 my $branch = current_branch();
6843 return undef if !$branch;
6844
6845 my $default_remote = `$git_cmd config --get branch.$branch.remote`;
6846 if ($default_remote) {
6847 chomp($default_remote);
6848 return $default_remote;
6849 }
6850
6851 my @output = `$git_cmd config --get-regexp remote\.origin\.*`;
7231
7232 if ($branch) {
7233 my $default_remote = `$GIT_CMD config --get branch.$branch.remote`;
7234 if ($default_remote) {
7235 chomp($default_remote);
7236 return $default_remote;
7237 }
7238 }
7239
7240 my @output = `$GIT_CMD config --get-regexp remote\.origin\.*`;
68527241 if (@output) {
68537242 return "origin";
68547243 } else {
68577246 repository. Please specify a repository or setup "origin" by running
68587247 eg remote add origin URL
68597248 EOF
6860 }
6861 }
6862
6863 sub print_new_unknowns {
7249 exit 1;
7250 }
7251 }
7252
7253 sub print_new_unknowns ($) {
68647254 my ($new_unknowns) = @_;
68657255 my $num = scalar(@$new_unknowns);
68667256 print STDERR "New unknown files";
68787268 }
68797269
68807270 # Error messages spewed by commit with non-clean working copies
6881 sub commit_error_message_checks {
7271 sub commit_error_message_checks ($$$$) {
68827272 my ($commit_type, $check_for, $status, $new_unknown) = @_;
68837273
6884 if ($status->{has_unmerged_changes}) {
7274 if ($check_for->{unmerged_changes} && $status->{has_unmerged_changes}) {
68857275 print STDERR <<EOF;
68867276 Aborting: You have unresolved conflicts from your merge (run 'eg status' to get
68877277 the list of files with conflicts). You must first resolve any conflicts and
68927282 }
68937283
68947284 if ($check_for->{no_changes} && $status->{has_no_changes}) {
6895 print STDERR <<EOF;
6896 Committing changes should (usually) only be done on a branch. Specify
6897 --bypass-no-branch-check to commit anyway.
6898 EOF
6899 exit 1;
6900 }
6901
6902 if ($check_for->{no_changes} && $status->{has_no_changes}) {
6903 print STDERR "Aborting: Nothing to commit (run 'eg status' for details).\n";
6904 exit 1;
7285 # There doesn't need to be any changes for a commit if we're trying to
7286 # make a merge commit.
7287 my $gitdir = git_dir();
7288 if ( ! -f "$gitdir/MERGE_HEAD" ) {
7289 die "Aborting: Nothing to commit (run 'eg status' for details).\n";
7290 }
69057291 }
69067292 elsif ($check_for->{unknown} && $check_for->{partially_staged} &&
69077293 $status->{has_new_unknown_files} &&
69357321 }
69367322
69377323 # Error messages spewed by push, publish for non-clean working copies
6938 sub push_error_message_checks {
7324 sub push_error_message_checks ($$$$) {
69397325 my ($clean_check_type, $check_for, $status, $new_unknown) = @_;
69407326
6941 if ($status->{has_unmerged_changes}) {
7327 if ($check_for->{unmerged_changes} && $status->{has_unmerged_changes}) {
69427328 print STDERR <<EOF;
69437329 Aborting: You have unresolved conflicts from your merge (run 'eg status' to get
69447330 the list of files with conflicts). You should first resolve any conflicts
69767362 }
69777363 }
69787364
6979 sub commit_push_checks {
6980 my ($clean_check_type, $check_for) = @_;
7365 # XXX called with 0 args or 2 args
7366 sub commit_push_checks (;$$) {
7367 my $clean_check_type = shift;
7368 my $check_for = shift || {};
69817369 my %status;
69827370
69837371 # Determine some useful directories
69847372 my ($cur_dir, $top_dir, $git_dir) = RepoUtil::get_dirs();
69857373
69867374 # Save debug mode, print out commands used up front
6987 if ($debug) {
7375 if ($DEBUG) {
69887376 Util::push_debug(new_value => 0);
69897377 if ($clean_check_type) {
69907378 print " >>Commands to gather data for pre-$clean_check_type sanity checks:\n";
69917379 } else {
69927380 print " >>Commands to gather data for sanity checks:\n";
69937381 }
6994 print " $git_cmd status\n";
6995 print " $git_cmd ls-files --unmerged\n";
6996 print " $git_cmd symbolic-ref HEAD\n" if $check_for->{no_branch};
6997 print " cd $top_dir && $git_cmd ls-files --exclude-standard --others --directory --no-empty-directory\n";
7382 print " $GIT_CMD status\n";
7383 print " $GIT_CMD ls-files --unmerged\n";
7384 print " $GIT_CMD symbolic-ref HEAD\n" if $check_for->{no_branch};
7385 print " cd $top_dir && $GIT_CMD ls-files --exclude-standard --others --directory --no-empty-directory\n";
69987386 } else {
69997387 Util::push_debug(new_value => 0);
70007388 }
70027390 #
70037391 # Determine which types of changes are present
70047392 #
7005 my ($ret, $output) = ExecUtil::execute_captured("$eg_exec status",
7393 my ($ret, $output) = ExecUtil::execute_captured("$EG_EXEC status",
70067394 ignore_ret => 1);
7007 my @unmerged_files = `$git_cmd ls-files --unmerged`;
7395 my @unmerged_files = `$GIT_CMD ls-files --unmerged`;
70087396 $status{has_new_unknown_files} = ($output =~ /^Newly created unknown files:$/m);
7009 $status{has_unstaged_changes} = ($output =~ /^Changed but not updated/m);
7397 $status{has_unstaged_changes} = ($output =~ /^Changes not staged for commit/m);
70107398 $status{has_staged_changes} = ($output =~ /^Changes ready to be commit/m);
7011 $status{has_no_changes} =
7012 ($output =~ /^nothing to commit \(working directory clean\)\n\z/m);
70137399 $status{has_unmerged_changes} = (scalar @unmerged_files > 0);
7400 $status{has_no_changes} = !$status{has_unstaged_changes} &&
7401 !$status{has_staged_changes} &&
7402 !$status{has_unmerged_changes};
70147403 $status{output} = $output;
70157404
70167405 #
70177406 # Determine which unknown files are "newly created"
70187407 #
7019 my @new_unknown = `(cd "$top_dir" && $git_cmd ls-files --exclude-standard --others --directory --no-empty-directory)`;
7408 my @new_unknown = `(cd "$top_dir" && $GIT_CMD ls-files --exclude-standard --others --directory --no-empty-directory)`;
70207409 chomp(@new_unknown);
70217410 if ($check_for->{unknown} && $status{has_new_unknown_files} &&
70227411 -f "$git_dir/info/ignored-unknown") {
70317420 Util::pop_debug();
70327421
70337422 if ($check_for->{no_branch}) {
7034 my $rc = system('$git_cmd symbolic-ref -q HEAD >/dev/null');
7423 my $rc = system('$GIT_CMD symbolic-ref -q HEAD >/dev/null');
70357424 $status{has_no_branch} = $rc >> 8;
70367425 }
70377426
70547443 return \%status;
70557444 }
70567445
7057 sub record_ignored_unknowns {
7446 sub record_ignored_unknowns () {
70587447 # Determine some useful directories
70597448 my ($cur_dir, $top_dir, $git_dir) = RepoUtil::get_dirs();
70607449
70617450 mkdir "$git_dir/info" unless -d "$git_dir/info";
70627451 open(OUTPUT, "> $git_dir/info/ignored-unknown");
7063 my @unknown_files = `cd "$top_dir" && $git_cmd ls-files --exclude-standard --others --directory --no-empty-directory`;
7452 my @unknown_files = `cd "$top_dir" && $GIT_CMD ls-files --exclude-standard --others --directory --no-empty-directory`;
70647453 foreach my $file (@unknown_files) {
70657454 print OUTPUT $file;
70667455 }
70677456 close(OUTPUT);
70687457 }
70697458
7070 sub parse_args {
7459 sub parse_args ($@) {
7460 my $multi_args = shift;
70717461 my (@args) = @_;
70727462
70737463 Util::push_debug(new_value => 0);
70857475
70867476 if ($arg =~ /^-/) {
70877477 push(@opts, $arg);
7478 push(@opts, shift @args) if (grep {$arg eq $_} @$multi_args);
70887479 } else {
70897480 unshift(@args, $arg);
70907481 last;
71447535 return (\@opts, \@revs, \@files);
71457536 }
71467537
7147 sub get_revert_info {
7538 sub get_revert_info ($@) {
71487539 my ($revision, @quoted_files) = @_;
71497540
71507541 my $marker = "";
71697560 my $ref2 = shift;
71707561 my (@files, @lines);
71717562 if (defined $ref2) {
7172 @lines = `$git_cmd diff-tree -r $ref1 $ref2 $marker @quoted_files`;
7563 @lines = `$GIT_CMD diff-tree -r $ref1 $ref2 $marker @quoted_files`;
71737564 } else {
7174 @lines = `$git_cmd diff-index --cached $ref1 $marker @quoted_files`;
7565 @lines = `$GIT_CMD diff-index --cached $ref1 $marker @quoted_files`;
71757566 }
71767567 foreach my $line (@lines) {
71777568 # Check for newly added files (not previously tracked but now staged)
72257616 package Util;
72267617
72277618 # Return items in @$lista but not in @$listb
7228 sub difference {
7619 sub difference ($$) {
72297620 my ($lista, $listb) = @_;
72307621 my %count;
72317622
72367627 }
72377628
72387629 # Return items in both @$lista and in @$listb
7239 sub intersect {
7630 sub intersect ($$) {
72407631 my ($lista, $listb) = @_;
72417632 my %original;
72427633 my @both = ();
72487639 }
72497640
72507641 # Return items in either @$lista or @$listb
7251 sub union {
7642 sub union ($$) {
72527643 my ($lista, $listb) = @_;
72537644 my %either;
72547645
72597650 }
72607651
72617652 # Returns whether @$list contains $item
7262 sub contains {
7653 sub contains ($$) {
72637654 my ($list, $item) = @_;
72647655 my $found = 0;
72657656 foreach my $elem (@$list) {
72727663 return $found;
72737664 }
72747665
7275 sub uniquify_list {
7666 sub uniquify_list (@) {
72767667 my @list = @_;
72777668 my %unique;
72787669 @unique{@list} = @list;
72797670 return keys %unique;
72807671 }
72817672
7282 sub quote_args {
7673 sub split_ssh_repository ($) {
7674 my ($repository) = @_;
7675 my ($user, $machine, $port, $path);
7676 if ($repository =~ m#^ssh://((?:.*?@)?)([^/:]*)(?::(\d+))?(.*)$#) {
7677 $user = $1;
7678 $machine = $2;
7679 $port = defined $3 ? "-p $3" : "";
7680 $path = $4;
7681 $path =~ s#^/~#~#; # Change leading /~ into plain ~
7682 } elsif ($repository =~ m#^((?:.*?@)?)([^:]*):(.*)$#) {
7683 $user = $1;
7684 $machine = $2;
7685 $port = "";
7686 $path = $3;
7687 }
7688 return ($user, $machine, $port, $path);
7689 }
7690
7691 sub quote_args (@) {
72837692 my @args = @_;
72847693
72857694 # Quote arguments with special characters so that when we
72897698 my @newargs;
72907699 foreach my $arg (@args) {
72917700 my $quotes_needed = 0;
7292 if (!$arg || $arg =~ /[;'"<>()\[\]|`* \n\$\\]/) {
7701 if (!$arg || $arg =~ /[;'"<>()\[\]|`* \n\$\\~]/) {
72937702 $quotes_needed = 1;
72947703 }
72957704
73087717 # Have git's rev-parse command parse @args and decide which part is files,
73097718 # which is options, and which are revisions. Further, have git translate
73107719 # revisions into full 40-character hexadecimal commit ids.
7311 sub git_rev_parse {
7720 sub git_rev_parse (@) {
73127721 my @args = @_;
73137722
73147723 Util::push_debug(new_value => 0);
73157724
73167725 my @quoted_args = Util::quote_args(@args);
73177726 my ($ret, $output) =
7318 ExecUtil::execute_captured("$git_cmd rev-parse @quoted_args",
7727 ExecUtil::execute_captured("$GIT_CMD rev-parse @quoted_args",
73197728 ignore_ret => 1);
73207729 if ($ret != 0) {
73217730 $output =~ /^(fatal:.*)$/m && print STDERR "$1\n";
73237732 exit 1;
73247733 }
73257734 my @opts =
7326 split('\n', `$git_cmd rev-parse --no-revs --flags @quoted_args`);
7735 split('\n', `$GIT_CMD rev-parse --no-revs --flags @quoted_args`);
73277736 my @revs =
7328 split('\n', `$git_cmd rev-parse --revs-only @quoted_args`);
7737 split('\n', `$GIT_CMD rev-parse --revs-only @quoted_args`);
73297738 my @files =
7330 split('\n', `$git_cmd rev-parse --no-revs --no-flags @quoted_args`);
7739 split('\n', `$GIT_CMD rev-parse --no-revs --no-flags @quoted_args`);
73317740
73327741 # Translate sha1sums back to human specified version of revisions. Note that
73337742 # something like "REV1...REV2" is translated into "SHA1 SHA2 ^SHA3", so one
73547763 # reroot_paths__from_to_files("/tmp/junk", "/tmp", ('bar', '../foo'))
73557764 # would return
73567765 # ('junk/bar', 'foo')
7357 sub reroot_paths__from_to_files {
7766 sub reroot_paths__from_to_files ($$@) {
73587767 my ($from, $to, @files) = @_;
73597768 $from =~ s#/*$#/#; # Make sure $from ends with exactly 1 slash
73607769 $to =~ s#/*$#/#; # Make sure $to ends with exactly 1 slash
73697778 # Find what $oldpath and $to have in common
73707779 my $common_leading_path = "";
73717780 my $combined = "$oldpath\n$to";
7372 if ($combined =~ /^(.*).*\n\1.*$/) {
7781 if ($combined =~ m#^(.*/).*\n\1.*$#) {
73737782 $common_leading_path = $1;
73747783 }
73757784
73897798
73907799 {
73917800 my @debug_values;
7392 sub push_debug {
7801 sub push_debug (@) {
73937802 my @opts = @_;
73947803 my %options = ( @opts );
73957804 die "Called without new_value!" if !defined($options{new_value});
73967805
7397 my $old_value = $debug;
7398 push(@debug_values, $debug);
7399 $debug = $options{new_value};
7806 my $old_value = $DEBUG;
7807 push(@debug_values, $DEBUG);
7808 $DEBUG = $options{new_value};
74007809 return $old_value;
74017810 }
74027811
7403 sub pop_debug {
7404 $debug = pop @debug_values;
7812 sub pop_debug () {
7813 $DEBUG = pop @debug_values;
74057814 }
74067815 }
74077816
74167825
74177826 package main;
74187827
7419 sub launch {
7828 sub launch ($) {
74207829 my $job=shift;
7830 $job = $ALIAS{$job} || $job;
74217831 my $orig_job = $job;
74227832 $job =~ s/-/_/; # Packages must have underscores, commands often have dashes
74237833
74317841 if ($action->can("preprocess")) {
74327842 # Do not skip commands normally executed during the preprocess stage,
74337843 # since they just gather data.
7434 Util::push_debug(new_value => $debug ? 1 : 0);
7435
7436 print ">>Stage: Preprocess<<\n" if $debug;
7844 Util::push_debug(new_value => $DEBUG ? 1 : 0);
7845
7846 print ">>Stage: Preprocess<<\n" if $DEBUG;
74377847 $action->preprocess();
74387848
74397849 Util::pop_debug();
74417851
74427852 # run & postprocess
74437853 if (!$action->can("postprocess")) {
7444 print ">>Stage: Run<<\n" if $debug;
7854 print ">>Stage: Run<<\n" if $DEBUG;
74457855 $ret = $action->run();
74467856 } else {
74477857 my $output = "";
7448 open($outfh, '>', \$output) || die "eg $job: cannot open \$outfh: $!";
7449 print ">>Stage: Run<<\n" if $debug;
7858 open($OUTFH, '>', \$output) || die "eg $job: cannot open \$OUTFH: $!";
7859 print ">>Stage: Run<<\n" if $DEBUG;
74507860 $ret = $action->run();
7451 print ">>Stage: Postprocess<<\n" if $debug;
7861 print ">>Stage: Postprocess<<\n" if $DEBUG;
74527862 $action->postprocess($output);
74537863 }
74547864
74557865 # wrapup
74567866 if ($action->can("wrapup")) {
7457 print ">>Stage: Wrapup<<\n" if $debug;
7867 print ">>Stage: Wrapup<<\n" if $DEBUG;
74587868 $action->wrapup();
74597869 }
74607870
7871 $ret = 0 unless ($ret);
74617872 exit $ret;
74627873 }
74637874
7464 sub version {
7875 sub version () {
74657876 my $version_obj = "version"->new();
74667877 $version_obj->run();
74677878 exit 0;
74687879 }
74697880
74707881 # User gave invalid input; print an error_message, then show command usage
7471 sub help {
7882 sub help (;$) {
74727883 my $error_message = shift;
74737884 my %extra_args;
74747885
74877898 $help_obj->run();
74887899 }
74897900
7490 sub main {
7901 sub main () {
74917902 #
74927903 # Get any global options
74937904 #
74947905 Getopt::Long::Configure("no_bundling", "no_permute",
74957906 "pass_through", "no_auto_abbrev", "no_ignore_case");
7496 my $record_arg = sub { $git_cmd .= " --$_[0]"; };
7497 my $record_args = sub { $git_cmd .= " --$_[0]=$_[1]"; };
7907 my @global_args = ();
7908 my $record_arg = sub { push(@global_args, "--$_[0]"); };
7909 my $record_args = sub { push(@global_args, "--$_[0]=$_[1]"); };
74987910 my $result=GetOptions(
7499 "--debug" => sub { $debug = 1 },
7911 "--debug" => sub { $DEBUG = 1 },
75007912 "--help" => sub { help() },
7501 "--translate" => sub { $debug = 2 },
7913 "--translate" => sub { $DEBUG = 2 },
75027914 "--version" => sub { version() },
7503 "exec-path=s" => sub { &$record_args(@_) },
7504 "paginate|p" => sub { $use_pager = 1; &$record_arg(@_) },
7505 "no-pager" => sub { $use_pager = 0; &$record_arg(@_) },
7915 "exec-path:s" => sub { $_[1] ? &$record_args(@_)
7916 : &$record_arg(@_); },
7917 "c:s" => sub { push(@global_args, "-c", $_[1]); },
7918 "paginate|p" => sub { $USE_PAGER = 1; &$record_arg(@_) },
7919 "no-pager" => sub { $USE_PAGER = 0; &$record_arg(@_) },
75067920 "bare" => sub { &$record_arg(@_) },
75077921 "git-dir=s" => sub { &$record_args(@_) },
75087922 "work-tree=s" => sub { &$record_args(@_) },
7923 "no-replace-objects" => sub { &$record_arg(@_) },
75097924 );
75107925 # Make sure all global args are passed to eg subprocesses as well...
7511 $eg_exec .= substr($git_cmd, index($git_cmd, ' ')) if $git_cmd ne "git";
7926 @global_args = Util::quote_args(@global_args);
7927 $GIT_CMD .= " @global_args";
7928 $EG_EXEC .= " @global_args" if @global_args;
7929
7930 #
7931 # Fix the environment, if needed (eg testsuite invokes eg via 'git', but
7932 # eg needs to be able to call the real git).
7933 # WARNING: This does not handle mutual recursion (eg in PATH twice as 'git')
7934 #
7935 if ($0 =~ m#(.*)/git$#) {
7936 my $baddir = $1;
7937 my @newpaths = grep {$_ ne $baddir} split(/:/, $ENV{PATH});
7938 $ENV{PATH} = join(':', @newpaths);
7939 }
75127940
75137941 # Sanity check the arguments
7942 exit ExecUtil::execute($GIT_CMD) if $GIT_CMD =~ m#--exec-path$#;
75147943 die "eg: Error parsing arguments. (Try 'eg help')\n" if !$result;
75157944 die "eg: No subcommand specified. (Try 'eg help')\n" if @ARGV < 1;
75167945 die "eg: Invalid argument '$ARGV[0]'. (Try 'eg help')\n"
75177946 if ($ARGV[0] !~ m#^[a-z]#);
7518
7519 #
7520 # Fix the environment, if needed
7521 #
7522 if (defined $ENV{"EG_GIT_TEST_PATH"}) {
7523 # Trim the first dir out of path, since it has a symlink git -> eg
7524 my @oldpaths = split(/:/, $ENV{PATH});
7525 my @newpaths = ();
7526 foreach my $path (@oldpaths) {
7527 if (! -l "$path/git" || readlink("$path/git") !~ m#/eg$# ) {
7528 push(@newpaths, $path);
7529 }
7530 }
7531 $ENV{PATH} = join(':', @newpaths);
7532 }
75337947
75347948 #
75357949 # Now execute the action