0 | 0 |
#!/usr/bin/perl -w
|
1 | 1 |
|
2 | 2 |
## 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
|
5 | 5 |
## Licensed under GNU GPL, version 2.
|
6 | 6 |
|
7 | 7 |
## To use eg, simply stick this file in your path. Then fire off an
|
|
10 | 10 |
## to get a comparison to svn in terms of capabilities and commands.
|
11 | 11 |
## Webpage for eg: http://www.gnome.org/~newren/eg
|
12 | 12 |
|
|
13 |
use strict;
|
|
14 |
use warnings;
|
|
15 |
|
13 | 16 |
package main;
|
14 | 17 |
|
15 | |
use warnings;
|
16 | 18 |
use Getopt::Long;
|
17 | 19 |
use Cwd qw(getcwd abs_path);
|
18 | 20 |
use List::Util qw(max);
|
19 | 21 |
use File::Temp qw/ tempfile /;
|
20 | 22 |
|
21 | 23 |
# 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;
|
23 | 27 |
|
24 | 28 |
# 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 = {
|
32 | 35 |
'creation' =>
|
33 | 36 |
{ order => 1,
|
34 | 37 |
desc => 'Creating repositories',
|
|
65 | 68 |
desc => 'Miscellaneous'
|
66 | 69 |
},
|
67 | 70 |
};
|
68 | |
my ($curdir, $topdir, $gitdir);
|
|
71 |
my ($CURDIR, $TOPDIR, $GITDIR);
|
69 | 72 |
|
70 | 73 |
## Commands to list in help even though we haven't overridden the git versions
|
71 | 74 |
## (yet, in most cases)
|
72 | 75 |
INIT {
|
73 | |
%command = (
|
|
76 |
%COMMAND = (
|
74 | 77 |
blame => {
|
75 | 78 |
unmodified_help => 1,
|
76 | 79 |
unmodified_behavior => 1,
|
|
132 | 135 |
# Most commands must be run inside a git working directory
|
133 | 136 |
unless (!$self->{git_repo_needed} || (@ARGV > 0 && $ARGV[0] eq "--help")) {
|
134 | 137 |
$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 |
}
|
136 | 157 |
}
|
137 | 158 |
|
138 | 159 |
# Many commands do not work if no commit has yet been made
|
|
154 | 175 |
$git_equiv =~ s/_/-/; # Packages use underscores, commands use dashes
|
155 | 176 |
|
156 | 177 |
if ($package_name eq "subcommand") {
|
157 | |
exit ExecUtil::execute("$git_cmd $self->{command} --help")
|
|
178 |
exit ExecUtil::execute("$GIT_CMD $self->{command} --help")
|
158 | 179 |
}
|
159 | 180 |
|
160 | 181 |
$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";
|
164 | 185 |
chomp($less);
|
165 | 186 |
open(OUTPUT, "| $less");
|
166 | |
print OUTPUT "$package_name: $command{$package_name}{about}\n";
|
|
187 |
print OUTPUT "$package_name: $COMMAND{$package_name}{about}\n";
|
167 | 188 |
print OUTPUT $self->{'help'};
|
168 | 189 |
print OUTPUT "\nDifferences from git $package_name:";
|
169 | 190 |
print OUTPUT "\n None.\n" if !defined $self->{'differences'};
|
|
171 | 192 |
if ($git_equiv) {
|
172 | 193 |
print OUTPUT "\nSee also\n";
|
173 | 194 |
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.
|
175 | 196 |
eg $package_name is designed to accept the same options as git $git_equiv, and
|
176 | 197 |
with the same meanings unless specified otherwise in the above
|
177 | 198 |
"Differences" section.
|
|
196 | 217 |
$package_name eq "subcommand" ? $self->{'command'} : $package_name;
|
197 | 218 |
|
198 | 219 |
@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);
|
200 | 221 |
}
|
201 | 222 |
|
202 | 223 |
###########################################################################
|
|
205 | 226 |
package add;
|
206 | 227 |
@add::ISA = qw(subcommand);
|
207 | 228 |
INIT {
|
208 | |
$command{add} = {
|
|
229 |
$COMMAND{add} = {
|
209 | 230 |
unmodified_behavior => 1,
|
210 | 231 |
section => 'compatibility',
|
211 | 232 |
about => 'Mark content in files as being ready for commit'
|
|
230 | 251 |
package apply;
|
231 | 252 |
@apply::ISA = qw(subcommand);
|
232 | 253 |
INIT {
|
233 | |
$command{apply} = {
|
|
254 |
$COMMAND{apply} = {
|
234 | 255 |
about => 'Apply a patch in a git repository'
|
235 | 256 |
};
|
236 | 257 |
}
|
|
279 | 300 |
|
280 | 301 |
sub preprocess {
|
281 | 302 |
my $self = shift;
|
282 | |
my $package_name = ref($self);
|
283 | 303 |
|
284 | 304 |
my $result = main::GetOptions("--help" => sub { $self->help() });
|
285 | 305 |
foreach my $i (0..$#ARGV) {
|
|
293 | 313 |
package branch;
|
294 | 314 |
@branch::ISA = qw(subcommand);
|
295 | 315 |
INIT {
|
296 | |
$command{branch} = {
|
|
316 |
$COMMAND{branch} = {
|
297 | 317 |
section => 'projects',
|
298 | 318 |
about => 'List, create, or delete branches'
|
299 | 319 |
};
|
|
320 |
$ALIAS{'br'} = "branch";
|
300 | 321 |
}
|
301 | 322 |
|
302 | 323 |
sub new {
|
|
363 | 384 |
|
364 | 385 |
sub run {
|
365 | 386 |
my $self = shift;
|
366 | |
my $package_name = ref($self);
|
367 | 387 |
|
368 | 388 |
my $switch = 0;
|
369 | 389 |
if (scalar(@ARGV) > 1 && $ARGV[0] eq "-s") {
|
|
372 | 392 |
}
|
373 | 393 |
|
374 | 394 |
@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)
|
377 | 397 |
if ($switch && $ret == 0);
|
378 | 398 |
return $ret;
|
379 | 399 |
}
|
|
384 | 404 |
package bundle;
|
385 | 405 |
@bundle::ISA = qw(subcommand);
|
386 | 406 |
INIT {
|
387 | |
$command{bundle} = {
|
|
407 |
$COMMAND{bundle} = {
|
388 | 408 |
extra => 1,
|
389 | 409 |
section => 'collaboration',
|
390 | 410 |
about => 'Pack repository updates (or whole repository) into a file'
|
|
489 | 509 |
|
490 | 510 |
sub preprocess {
|
491 | 511 |
my $self = shift;
|
492 | |
my $package_name = ref($self);
|
493 | 512 |
|
494 | 513 |
#
|
495 | 514 |
# Parse options
|
|
520 | 539 |
die "$oldname does not exist.\n" if ! -f $oldname;
|
521 | 540 |
|
522 | 541 |
my ($retval, $output) =
|
523 | |
ExecUtil::execute_captured("$git_cmd bundle list-heads $oldname");
|
|
542 |
ExecUtil::execute_captured("$GIT_CMD bundle list-heads $oldname");
|
524 | 543 |
my @lines = split '\n', $output;
|
525 | 544 |
|
526 | 545 |
my @refs = map { m#^([0-9a-f]+)# && "^$1" } @lines;
|
|
538 | 557 |
package cat;
|
539 | 558 |
@cat::ISA = qw(subcommand);
|
540 | 559 |
INIT {
|
541 | |
$command{cat} = {
|
|
560 |
$COMMAND{cat} = {
|
542 | 561 |
new_command => 1,
|
543 | 562 |
extra => 1,
|
544 | 563 |
section => 'compatibility',
|
|
592 | 611 |
|
593 | 612 |
sub preprocess {
|
594 | 613 |
my $self = shift;
|
595 | |
my $package_name = ref($self);
|
596 | 614 |
|
597 | 615 |
my $result=main::GetOptions("--help" => sub { $self->help() });
|
598 | 616 |
|
|
616 | 634 |
|
617 | 635 |
sub run {
|
618 | 636 |
my $self = shift;
|
619 | |
my $package_name = ref($self);
|
620 | 637 |
|
621 | 638 |
@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);
|
623 | 640 |
}
|
624 | 641 |
|
625 | 642 |
###########################################################################
|
|
628 | 645 |
package changes;
|
629 | 646 |
@changes::ISA = qw(subcommand);
|
630 | 647 |
INIT {
|
631 | |
$command{changes} = {
|
|
648 |
$COMMAND{changes} = {
|
632 | 649 |
new_command => 1,
|
633 | 650 |
section => 'misc',
|
634 | 651 |
about => 'Provide an overview of the changes from git to eg'
|
|
667 | 684 |
|
668 | 685 |
sub run {
|
669 | 686 |
my $self = shift;
|
670 | |
my $package_name = ref($self);
|
671 | |
|
672 | |
if ($debug == 2) {
|
|
687 |
|
|
688 |
if ($DEBUG == 2) {
|
673 | 689 |
print " >>(No commands to run, just data to print)<<\n";
|
674 | 690 |
return;
|
675 | 691 |
}
|
|
678 | 694 |
my $indent = " ";
|
679 | 695 |
my $header_indent = "";
|
680 | 696 |
$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";
|
684 | 700 |
chomp($less);
|
685 | 701 |
open(OUTPUT, "| $less");
|
686 | 702 |
|
|
690 | 706 |
$header_indent = " ";
|
691 | 707 |
}
|
692 | 708 |
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};
|
696 | 712 |
print OUTPUT "$indent$c\n";
|
697 | 713 |
}
|
698 | 714 |
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};
|
701 | 717 |
print OUTPUT "$indent$c\n";
|
702 | 718 |
}
|
703 | 719 |
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};
|
708 | 724 |
print OUTPUT "$indent$c\n";
|
709 | 725 |
}
|
710 | 726 |
|
711 | 727 |
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;
|
715 | 732 |
$c =~ s/-/_/; # Packages use underscores, commands use dashes
|
716 | 733 |
next if !$c->can("new");
|
717 | 734 |
my $obj = $c->new(initial_commit_error_msg => '');
|
718 | 735 |
|
719 | |
print OUTPUT "Changes to $c:\n";
|
|
736 |
print OUTPUT "Changes to $real_c:\n";
|
720 | 737 |
if ($obj->{differences}) {
|
721 | 738 |
$obj->{differences} =~ s/^\n//;
|
722 | 739 |
print OUTPUT $obj->{differences};
|
|
734 | 751 |
package checkout;
|
735 | 752 |
@checkout::ISA = qw(subcommand);
|
736 | 753 |
INIT {
|
737 | |
$command{checkout} = {
|
|
754 |
$COMMAND{checkout} = {
|
738 | 755 |
section => 'compatibility',
|
739 | 756 |
about => 'Compatibility wrapper for clone/switch/revert'
|
740 | 757 |
};
|
|
807 | 824 |
return $self;
|
808 | 825 |
}
|
809 | 826 |
|
810 | |
sub _looks_like_git_repo {
|
|
827 |
sub _looks_like_git_repo ($) {
|
811 | 828 |
my $path = shift;
|
812 | 829 |
|
813 | 830 |
my $clone_protocol = qr#^(?:git|ssh|http|https|rsync)://#;
|
|
832 | 849 |
|
833 | 850 |
sub preprocess {
|
834 | 851 |
my $self = shift;
|
835 | |
my $package_name = ref($self);
|
836 | 852 |
|
837 | 853 |
if (scalar(@ARGV) > 0 && $ARGV[0] ne "--") {
|
838 | 854 |
main::GetOptions("--help" => sub { $self->help() });
|
|
863 | 879 |
$self->{git_dir} = RepoUtil::git_dir();
|
864 | 880 |
die "Must be run inside a git repository!\n" if !defined $self->{git_dir};
|
865 | 881 |
|
866 | |
return ExecUtil::execute("$git_cmd checkout @ARGV", ignore_ret => 1);
|
|
882 |
return ExecUtil::execute("$GIT_CMD checkout @ARGV", ignore_ret => 1);
|
867 | 883 |
} else {
|
868 | 884 |
die "Did you mean to run\n eg clone @ARGV\n?\n";
|
869 | 885 |
}
|
|
875 | 891 |
package cherry_pick;
|
876 | 892 |
@cherry_pick::ISA = qw(subcommand);
|
877 | 893 |
INIT {
|
878 | |
$command{"cherry-pick"} = {
|
|
894 |
$COMMAND{"cherry-pick"} = {
|
879 | 895 |
extra => 1,
|
880 | 896 |
section => 'modification',
|
881 | 897 |
about => 'Apply (or reverse) a commit, usually from another branch'
|
|
962 | 978 |
|
963 | 979 |
sub preprocess {
|
964 | 980 |
my $self = shift;
|
965 | |
my $package_name = ref($self);
|
966 | 981 |
|
967 | 982 |
my ($reverse, $dash_x, $mainline) = (0, 0, -1);
|
968 | 983 |
Getopt::Long::Configure("permute"); # Allow unrecognized options through
|
|
983 | 998 |
|
984 | 999 |
@ARGV = Util::quote_args(@ARGV);
|
985 | 1000 |
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);
|
987 | 1002 |
} 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);
|
989 | 1004 |
}
|
990 | 1005 |
}
|
991 | 1006 |
|
|
995 | 1010 |
package clone;
|
996 | 1011 |
@clone::ISA = qw(subcommand);
|
997 | 1012 |
INIT {
|
998 | |
$command{clone} = {
|
|
1013 |
$COMMAND{clone} = {
|
999 | 1014 |
section => 'creation',
|
1000 | 1015 |
about => 'Clone a repository into a new directory'
|
1001 | 1016 |
};
|
|
1036 | 1051 |
--depth DEPTH
|
1037 | 1052 |
Only download the DEPTH most recent commits instead of all history
|
1038 | 1053 |
";
|
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 | |
";
|
1044 | 1054 |
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;
|
1128 | 1055 |
}
|
1129 | 1056 |
|
1130 | 1057 |
###########################################################################
|
|
1133 | 1060 |
package commit;
|
1134 | 1061 |
@commit::ISA = qw(subcommand);
|
1135 | 1062 |
INIT {
|
1136 | |
$command{commit} = {
|
|
1063 |
$COMMAND{commit} = {
|
1137 | 1064 |
section => 'modification',
|
1138 | 1065 |
about => 'Record changes locally'
|
1139 | 1066 |
};
|
1140 | |
$alias{'checkin'} = "commit";
|
1141 | |
$alias{'ci'} = "commit";
|
|
1067 |
$ALIAS{'checkin'} = "commit";
|
|
1068 |
$ALIAS{'ci'} = "commit";
|
1142 | 1069 |
}
|
1143 | 1070 |
|
1144 | 1071 |
sub new {
|
|
1148 | 1075 |
$self->{'help'} = "
|
1149 | 1076 |
Usage:
|
1150 | 1077 |
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] [--]
|
1152 | 1079 |
[FILE...]
|
1153 | 1080 |
|
1154 | 1081 |
Description:
|
|
1278 | 1205 |
my $record_arg = sub { push(@{$self->{args}}, "$_[0]$_[1]"); };
|
1279 | 1206 |
my $record_args = sub { push(@{$self->{args}}, "$_[0]$_[1]");
|
1280 | 1207 |
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);
|
1282 | 1210 |
Getopt::Long::Configure("permute");
|
1283 | 1211 |
my $result = main::GetOptions(
|
1284 | |
"--help" => sub { $self->help() },
|
|
1212 |
"--help|h" => sub { $self->help() },
|
1285 | 1213 |
"all-known|a" => \$all_known,
|
1286 | 1214 |
"bypass-unknown-check|b" => \$bypass_unknown,
|
1287 | 1215 |
"staged|dirty|d" => \$staged,
|
|
1216 |
"dry-run" => sub { $dry_run = 1,
|
|
1217 |
&$record_arg("--", @_) },
|
1288 | 1218 |
"s" => sub { &$record_arg("-", @_) },
|
1289 | 1219 |
"v" => sub { &$record_arg("-", @_) },
|
1290 | 1220 |
"u" => sub { &$record_arg("-", @_) },
|
|
1294 | 1224 |
"file=s" => sub { &$record_args("--", @_) },
|
1295 | 1225 |
"m=s" => sub { &$record_args("-", @_) },
|
1296 | 1226 |
"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("--", @_) },
|
1298 | 1231 |
"no-verify" => sub { &$record_arg("--", @_) },
|
1299 | 1232 |
"e" => sub { &$record_arg("-", @_) },
|
1300 | 1233 |
"author=s" => sub { &$record_args("--", @_) },
|
1301 | 1234 |
"cleanup=s" => sub { &$record_args("--", @_) },
|
|
1235 |
"include|i=s" => sub { $include = 1;
|
|
1236 |
&$record_args("--", @_) },
|
1302 | 1237 |
);
|
1303 | |
my ($opts, $revs, $files) = RepoUtil::parse_args(@ARGV);
|
|
1238 |
my ($opts, $revs, $files) = RepoUtil::parse_args([], @ARGV);
|
1304 | 1239 |
|
1305 | 1240 |
#
|
1306 | 1241 |
# Set up flags based on options, do sanity checking of options
|
1307 | 1242 |
#
|
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;
|
1309 | 1245 |
$self->{'commit_flags'} = [];
|
1310 | 1246 |
die "Cannot specify both --all-known (-a) and --staged (-d)!\n" if
|
1311 | 1247 |
$all_known && $staged;
|
1312 | 1248 |
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;
|
1315 | 1253 |
push(@{$self->{'commit_flags'}}, "-a") if $all_known;
|
1316 | 1254 |
|
1317 | 1255 |
#
|
|
1319 | 1257 |
#
|
1320 | 1258 |
my $status =
|
1321 | 1259 |
RepoUtil::commit_push_checks($package_name,
|
1322 | |
{no_changes => 1,
|
|
1260 |
{no_changes => $check_no_changes,
|
1323 | 1261 |
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 &&
|
1327 | 1266 |
$status->{has_unstaged_changes} && !$status->{has_staged_changes}) {
|
1328 | 1267 |
print STDERR <<EOF;
|
1329 | 1268 |
Aborting: It is not clear whether you want to simply amend the commit
|
|
1336 | 1275 |
}
|
1337 | 1276 |
|
1338 | 1277 |
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 &&
|
1342 | 1281 |
$status->{has_unstaged_changes} && !$status->{has_staged_changes} &&
|
1343 | 1282 |
!@$files) {
|
1344 | 1283 |
push(@{$self->{'commit_flags'}}, "-a");
|
|
1362 | 1301 |
package config;
|
1363 | 1302 |
@config::ISA = qw(subcommand);
|
1364 | 1303 |
INIT {
|
1365 | |
$command{config} = {
|
|
1304 |
$COMMAND{config} = {
|
1366 | 1305 |
unmodified_behavior => 1,
|
1367 | 1306 |
extra => 1,
|
1368 | 1307 |
section => 'misc',
|
|
1383 | 1322 |
Description:
|
1384 | 1323 |
Gets or sets configuration options.
|
1385 | 1324 |
|
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
|
1387 | 1326 |
comprehensive list of special options used by eg (and git).
|
1388 | 1327 |
|
1389 | 1328 |
Examples:
|
|
1410 | 1349 |
package diff;
|
1411 | 1350 |
@diff::ISA = qw(subcommand);
|
1412 | 1351 |
INIT {
|
1413 | |
$command{diff} = {
|
|
1352 |
$COMMAND{diff} = {
|
1414 | 1353 |
section => 'discovery',
|
1415 | 1354 |
about => 'Show changes to file contents'
|
1416 | 1355 |
};
|
|
1507 | 1446 |
|
1508 | 1447 |
The .. operator of git diff (e.g. git diff master..devel) means what
|
1509 | 1448 |
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
|
1511 | 1450 |
do exactly what the ... operator of git diff does. To see why:
|
1512 | 1451 |
|
1513 | 1452 |
Meanings of git commands, as a reminder (A and B are revisions):
|
1514 | 1453 |
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
|
1516 | 1455 |
|
1517 | 1456 |
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
|
1520 | 1459 |
|
1521 | 1460 |
So, my translation:
|
1522 | 1461 |
eg diff A B <=> git diff A B <=> git diff A..B
|
|
1544 | 1483 |
|
1545 | 1484 |
sub preprocess {
|
1546 | 1485 |
my $self = shift;
|
1547 | |
my $package_name = ref($self);
|
1548 | 1486 |
|
1549 | 1487 |
# Avoid Util::git_rev_parse because it fails on t2010-checkout-ambiguous by
|
1550 | 1488 |
# treating "--quiet" as a revision rather than an option; use our own
|
1551 | 1489 |
# parse_args implementation instead.
|
1552 | |
my ($opts, $revs, $files) = RepoUtil::parse_args(@ARGV);
|
|
1490 |
my ($opts, $revs, $files) = RepoUtil::parse_args(["--extcmd", "-x"], @ARGV);
|
1553 | 1491 |
|
1554 | 1492 |
# Replace '..' with '...' in revision specifiers. Use backslash escaping to
|
1555 | 1493 |
# get actual dots and not just any character. Use negative lookbehind and
|
|
1562 | 1500 |
#
|
1563 | 1501 |
$self->{'opts'} = "";
|
1564 | 1502 |
@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;
|
1566 | 1505 |
Getopt::Long::Configure("permute");
|
1567 | 1506 |
my $result = main::GetOptions(
|
1568 | 1507 |
"--help" => sub { $self->help() },
|
1569 | 1508 |
"staged|cached" => \$staged,
|
1570 | 1509 |
"unstaged" => \$unstaged,
|
1571 | 1510 |
"no-index" => \$no_index,
|
|
1511 |
"extcmd=s" => \$extcmd,
|
|
1512 |
"ours" => \$ours,
|
|
1513 |
"theirs" => \$theirs,
|
1572 | 1514 |
);
|
1573 | 1515 |
die "Cannot specify both --staged and --unstaged!\n" if $staged && $unstaged;
|
1574 | 1516 |
my @args;
|
1575 | 1517 |
push(@args, "--cached") if $staged;
|
1576 | 1518 |
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;
|
1577 | 1522 |
push(@args, @ARGV);
|
1578 | 1523 |
|
1579 | 1524 |
#
|
|
1585 | 1530 |
if ($unstaged && scalar @$revs > 0);
|
1586 | 1531 |
# 'eg diff' (without arguments) should act like 'git diff HEAD', unless
|
1587 | 1532 |
# we are in an aborted merge state
|
1588 | |
if (!@$revs && !$unstaged && !$staged && !$no_index) {
|
|
1533 |
if (!@$revs && !$unstaged && !$staged && !$no_index && !$ours && !$theirs) {
|
1589 | 1534 |
if (-f "$self->{git_dir}/MERGE_HEAD") {
|
1590 | 1535 |
my @merge_branches = RepoUtil::merge_branches();
|
1591 | 1536 |
my $list = join(", ", @merge_branches);
|
|
1602 | 1547 |
EOF
|
1603 | 1548 |
exit 1;
|
1604 | 1549 |
}
|
|
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 |
}
|
1605 | 1558 |
push(@$revs, "HEAD")
|
1606 | 1559 |
}
|
1607 | 1560 |
|
|
1612 | 1565 |
}
|
1613 | 1566 |
|
1614 | 1567 |
###########################################################################
|
|
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 |
###########################################################################
|
1615 | 1622 |
# gc #
|
1616 | 1623 |
###########################################################################
|
1617 | 1624 |
package gc;
|
1618 | 1625 |
@gc::ISA = qw(subcommand);
|
1619 | 1626 |
INIT {
|
1620 | |
$command{gc} = {
|
|
1627 |
$COMMAND{gc} = {
|
1621 | 1628 |
unmodified_behavior => 1,
|
1622 | 1629 |
extra => 1,
|
1623 | 1630 |
section => 'timesavers',
|
|
1653 | 1660 |
package help;
|
1654 | 1661 |
@help::ISA = qw(subcommand);
|
1655 | 1662 |
INIT {
|
1656 | |
$command{help} = {
|
|
1663 |
$COMMAND{help} = {
|
1657 | 1664 |
section => 'misc',
|
1658 | 1665 |
about => 'Get command syntax and examples'
|
1659 | 1666 |
};
|
|
1715 | 1722 |
|
1716 | 1723 |
sub preprocess {
|
1717 | 1724 |
my $self = shift;
|
1718 | |
my $package_name = ref($self);
|
1719 | 1725 |
|
1720 | 1726 |
$self->{all} = 0;
|
1721 | 1727 |
my $result=main::GetOptions("--help" => sub { $self->help() },
|
|
1724 | 1730 |
|
1725 | 1731 |
sub run {
|
1726 | 1732 |
my $self = shift;
|
1727 | |
my $package_name = ref($self);
|
1728 | |
|
1729 | |
if ($debug == 2) {
|
|
1733 |
|
|
1734 |
if ($DEBUG == 2) {
|
1730 | 1735 |
print " >>(No commands to run, just data to print)<<\n";
|
1731 | 1736 |
return;
|
1732 | 1737 |
}
|
1733 | 1738 |
|
1734 | 1739 |
# Check if we were asked to get help on a subtopic rather than toplevel help
|
1735 | 1740 |
if (@ARGV > 0) {
|
1736 | |
my $subcommand = shift @ARGV;
|
|
1741 |
my $orig_subcommand = shift @ARGV;
|
|
1742 |
my $subcommand = $orig_subcommand;
|
1737 | 1743 |
$subcommand =~ s/-/_/; # Packages use underscores, commands use dashes
|
1738 | 1744 |
if (@ARGV != 0 && ($subcommand ne 'topic' || @ARGV != 1)) {
|
1739 | 1745 |
die "Too many arguments to help.\n";
|
|
1742 | 1748 |
$subcommand = "help::topic" if $subcommand eq 'topic';
|
1743 | 1749 |
|
1744 | 1750 |
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";
|
1748 | 1754 |
sleep 2;
|
1749 | |
exit ExecUtil::execute("$git_cmd help $subcommand");
|
|
1755 |
exit ExecUtil::execute("$GIT_CMD help $orig_subcommand");
|
1750 | 1756 |
}
|
1751 | 1757 |
|
1752 | 1758 |
my $subcommand_obj = $subcommand->new(initial_commit_error_msg => '',
|
|
1756 | 1762 |
|
1757 | 1763 |
# Set up a pager, if wanted
|
1758 | 1764 |
$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";
|
1762 | 1768 |
chomp($less);
|
1763 | 1769 |
open(OUTPUT, "| $less");
|
1764 | 1770 |
|
|
1769 | 1775 |
|
1770 | 1776 |
# Print valid subcommands sorted by section
|
1771 | 1777 |
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};
|
1781 | 1787 |
}
|
1782 | 1788 |
print OUTPUT "\n";
|
1783 | 1789 |
}
|
1784 | 1790 |
|
1785 | 1791 |
# Check to see if someone added a command with an invalid section
|
1786 | 1792 |
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});
|
1791 | 1797 |
$broken_commands .= $tmp;
|
1792 | 1798 |
}
|
1793 | 1799 |
if ($broken_commands) {
|
|
1864 | 1870 |
without resolving all conflicts, the command will error out and tell you
|
1865 | 1871 |
that some conflicts remain to be resolved.";
|
1866 | 1872 |
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";
|
1880 | 1875 |
return _conflict_resolution_message(op => "merge",
|
1881 | 1876 |
show_empty_case => 0,
|
1882 | 1877 |
continue_text => $completion_text,
|
|
1932 | 1927 |
);
|
1933 | 1928 |
}
|
1934 | 1929 |
|
1935 | |
sub _conflict_resolution_message {
|
|
1930 |
sub _conflict_resolution_message (%) {
|
1936 | 1931 |
my $opts = {op => "!!!FIXME!!!",
|
1937 | 1932 |
show_empty_case => 0,
|
1938 | 1933 |
extra_stop_info => '',
|
|
2052 | 2047 |
100644 -- Normal, non-executable file
|
2053 | 2048 |
100755 -- File with the executable bit set
|
2054 | 2049 |
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)
|
2056 | 2051 |
|
2057 | 2052 |
******************** Tell git to continue the operation ********************
|
2058 | 2053 |
$opts->{continue_text}
|
|
2085 | 2080 |
|
2086 | 2081 |
eg bisect reset
|
2087 | 2082 |
|
2088 | |
See 'man git-bisect' for more details."
|
|
2083 |
See 'git help bisect' for more details."
|
2089 | 2084 |
}
|
2090 | 2085 |
|
2091 | 2086 |
sub refspecs {
|
|
2449 | 2444 |
eg stage -p
|
2450 | 2445 |
(You will be asked whether to stage each change, listed in diff format;
|
2451 | 2446 |
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
|
2453 | 2448 |
more details).
|
2454 | 2449 |
|
2455 | 2450 |
Get all unstaged changes to bar.C and foo.pl
|
|
2696 | 2691 |
}
|
2697 | 2692 |
|
2698 | 2693 |
$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";
|
2702 | 2697 |
chomp($less);
|
2703 | 2698 |
open(OUTPUT, "| $less");
|
2704 | 2699 |
print OUTPUT "$topic\n";
|
|
2714 | 2709 |
package info;
|
2715 | 2710 |
@info::ISA = qw(subcommand);
|
2716 | 2711 |
INIT {
|
2717 | |
$command{info} = {
|
|
2712 |
$COMMAND{info} = {
|
2718 | 2713 |
new_command => 1,
|
2719 | 2714 |
section => 'discovery',
|
2720 | 2715 |
extra => 1,
|
|
2810 | 2805 |
if ($path) {
|
2811 | 2806 |
die "$path does not look like a directory.\n" if ! -d $path;
|
2812 | 2807 |
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);
|
2814 | 2809 |
if ($ret != 0) {
|
2815 | 2810 |
die "$path does not appear to be a git archive " .
|
2816 | 2811 |
"(maybe it has no commits yet?).\n";
|
|
2832 | 2827 |
# Special case the situation of no commits being present
|
2833 | 2828 |
#
|
2834 | 2829 |
if (RepoUtil::initial_commit()) {
|
2835 | |
if ($debug < 2) {
|
|
2830 |
if ($DEBUG < 2) {
|
2836 | 2831 |
print STDERR <<EOF;
|
2837 | 2832 |
Total commits: 0
|
2838 | 2833 |
Local repository: $self->{git_dir}
|
|
2848 | 2843 |
#
|
2849 | 2844 |
|
2850 | 2845 |
# 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;
|
2853 | 2848 |
|
2854 | 2849 |
# local repo
|
2855 | |
print "Local repository: $self->{git_dir}\n" if $debug < 2;
|
|
2850 |
print "Local repository: $self->{git_dir}\n" if $DEBUG < 2;
|
2856 | 2851 |
|
2857 | 2852 |
# named remote repos
|
2858 | 2853 |
my %remotes;
|
2859 | 2854 |
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) {
|
2862 | 2857 |
chomp($remote);
|
2863 | 2858 |
my $url = RepoUtil::get_config("remote.$remote.url");
|
2864 | 2859 |
$remotes{$remote} = $url;
|
2865 | 2860 |
$longest = main::max($longest, length($remote));
|
2866 | 2861 |
}
|
2867 | |
if (scalar keys %remotes > 0 && $debug < 2) {
|
|
2862 |
if (scalar keys %remotes > 0 && $DEBUG < 2) {
|
2868 | 2863 |
print "Named remote repositories: (name -> location)\n";
|
2869 | 2864 |
foreach my $remote (sort keys %remotes) {
|
2870 | 2865 |
printf " %${longest}s -> %s\n", $remote, $remotes{$remote};
|
|
2881 | 2876 |
chdir($top_dir);
|
2882 | 2877 |
|
2883 | 2878 |
# Name
|
2884 | |
print "Current branch: $branch\n" if $debug < 2;
|
|
2879 |
print "Current branch: $branch\n" if $DEBUG < 2;
|
2885 | 2880 |
|
2886 | 2881 |
# 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;
|
2889 | 2884 |
|
2890 | 2885 |
# Default pull/push options
|
2891 | 2886 |
my $default = "-None-";
|
2892 | 2887 |
my $print_config_options = 0;
|
2893 | 2888 |
my ($ret, $options) =
|
2894 | |
ExecUtil::execute_captured("$git_cmd config --get-regexp " .
|
|
2889 |
ExecUtil::execute_captured("$GIT_CMD config --get-regexp " .
|
2895 | 2890 |
"^branch\.$branch\.*", ignore_ret => 1);
|
2896 | 2891 |
chomp($options);
|
2897 | 2892 |
my @lines;
|
|
2904 | 2899 |
$default = $1;
|
2905 | 2900 |
$print_config_options = ($line_count > 1);
|
2906 | 2901 |
} else {
|
2907 | |
my @output = `$git_cmd config --get-regexp remote.origin.*`;
|
|
2902 |
my @output = `$GIT_CMD config --get-regexp remote.origin.*`;
|
2908 | 2903 |
$default = "origin" if @output;
|
2909 | 2904 |
}
|
2910 | 2905 |
}
|
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) {
|
2913 | 2908 |
print " Default pull/push options:\n";
|
2914 | 2909 |
foreach my $line (@lines) {
|
2915 | 2910 |
$line =~ s/\s+/ = /;
|
|
2918 | 2913 |
}
|
2919 | 2914 |
|
2920 | 2915 |
# 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;
|
2923 | 2918 |
|
2924 | 2919 |
# 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;
|
2927 | 2922 |
|
2928 | 2923 |
# No. dirs
|
2929 | 2924 |
my $num_dirs = ExecUtil::output(
|
2930 | |
"$git_cmd ls-tree -r -t HEAD " .
|
|
2925 |
"$GIT_CMD ls-tree -r -t HEAD " .
|
2931 | 2926 |
" | grep -E '[0-9]+ tree'" .
|
2932 | 2927 |
" | wc -l");
|
2933 | |
print " Number of directories: $num_dirs\n" if $debug < 2;
|
|
2928 |
print " Number of directories: $num_dirs\n" if $DEBUG < 2;
|
2934 | 2929 |
|
2935 | 2930 |
# Some ugly, nasty code to get the biggest file. Seems to be the only
|
2936 | 2931 |
# method I could find that would work given the corner case filenames
|
2937 | 2932 |
# (spaces and unicode chars) in the git.git repo (Try eg info on repo
|
2938 | 2933 |
# 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`;
|
2940 | 2935 |
my %biggest = (name => '', size => 0);
|
2941 | 2936 |
foreach my $line (@files) {
|
2942 | 2937 |
if ($line =~ m#^[0-9]+ [a-z]+ [0-9a-f]+[ ]*(\d+)[ \t]*(.*)$#) {
|
|
2949 | 2944 |
}
|
2950 | 2945 |
}
|
2951 | 2946 |
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;
|
2953 | 2948 |
|
2954 | 2949 |
# 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;
|
2957 | 2952 |
|
2958 | 2953 |
# Other possibilities:
|
2959 | 2954 |
# Disk space used by respository (du -hs .git, or packfile size?)
|
|
2972 | 2967 |
package init;
|
2973 | 2968 |
@init::ISA = qw(subcommand);
|
2974 | 2969 |
INIT {
|
2975 | |
$command{init} = {
|
|
2970 |
$COMMAND{init} = {
|
2976 | 2971 |
unmodified_behavior => 1,
|
2977 | 2972 |
section => 'creation',
|
2978 | 2973 |
about => 'Create a new repository'
|
|
2991 | 2986 |
Creates a new repository.
|
2992 | 2987 |
|
2993 | 2988 |
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.
|
2995 | 2990 |
|
2996 | 2991 |
Note for cvs/svn users: With cvs or svn it is common to create an empty
|
2997 | 2992 |
repository on \"the server\", then check it out locally and start adding
|
|
3043 | 3038 |
package log;
|
3044 | 3039 |
@log::ISA = qw(subcommand);
|
3045 | 3040 |
INIT {
|
3046 | |
$command{log} = {
|
|
3041 |
$COMMAND{log} = {
|
3047 | 3042 |
section => 'discovery',
|
3048 | 3043 |
about => 'Show history of recorded changes'
|
3049 | 3044 |
};
|
|
3077 | 3072 |
return $self;
|
3078 | 3073 |
}
|
3079 | 3074 |
|
3080 | |
sub _get_values {
|
|
3075 |
sub _get_values ($$) {
|
3081 | 3076 |
my ($names, $sha1sum) = @_;
|
3082 | 3077 |
my ($name, $distance);
|
3083 | 3078 |
if (defined($names->{$sha1sum})) {
|
|
3086 | 3081 |
return ($name, $distance);
|
3087 | 3082 |
}
|
3088 | 3083 |
|
3089 | |
sub _path_count {
|
|
3084 |
sub _path_count ($) {
|
3090 | 3085 |
my ($name) = @_;
|
3091 | 3086 |
my @matches = ($name =~ m/[~^]/g);
|
3092 | 3087 |
return scalar @matches;
|
3093 | 3088 |
}
|
3094 | 3089 |
|
3095 | |
sub _get_revision_name {
|
|
3090 |
sub _get_revision_name ($$$) {
|
3096 | 3091 |
my ($sha1sum, $filehandle, $names) = @_;
|
3097 | 3092 |
my ($name, $distance);
|
3098 | 3093 |
|
|
3117 | 3112 |
# Determine any name we previously determined for $parent, the name we
|
3118 | 3113 |
# would give it relative to $child, and determine which should "win"
|
3119 | 3114 |
my ($orig_parent_name, $orig_parent_distance) = _get_values($names, $parent);
|
|
3115 |
my $parent_name;
|
3120 | 3116 |
if ($cur_name =~ /^(.*)~(\d+)$/) {
|
3121 | 3117 |
my $count = $2 + 1;
|
3122 | 3118 |
$parent_name = "$1~$count";
|
3123 | 3119 |
} else {
|
3124 | 3120 |
$parent_name = "$cur_name~1";
|
3125 | 3121 |
}
|
3126 | |
$parent_distance = $distance + 1;
|
|
3122 |
my $parent_distance = $distance + 1;
|
3127 | 3123 |
if (!$orig_parent_name ||
|
3128 | 3124 |
_path_count($orig_parent_name) > _path_count($parent_name)) {
|
3129 | 3125 |
$names->{$parent} = [$parent_name, $parent_distance];
|
|
3166 | 3162 |
# all the arguments to determine if there is a valid revision listed on the
|
3167 | 3163 |
# command line (or, failing that, whether HEAD reference a valid revision);
|
3168 | 3164 |
# 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`) {
|
3170 | 3166 |
die "Error: No recorded commits to show yet.\n";
|
3171 | 3167 |
}
|
3172 | 3168 |
|
3173 | 3169 |
# We can just run plain git log if there's not current branch
|
3174 | 3170 |
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);
|
3176 | 3172 |
}
|
3177 | 3173 |
|
3178 | 3174 |
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");
|
3180 | 3176 |
exit $ret if $ret;
|
3181 | 3177 |
chomp($revision);
|
3182 | 3178 |
|
3183 | 3179 |
# 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 " .
|
3187 | 3183 |
"--refs=refs/heads/$branch | \\\n" .
|
3188 | 3184 |
" less\n";
|
3189 | |
return if $debug == 2;
|
|
3185 |
return 0 if $DEBUG == 2;
|
3190 | 3186 |
}
|
3191 | 3187 |
|
3192 | 3188 |
# Setup name determination via output from git rev-list
|
3193 | 3189 |
my %names;
|
3194 | |
open(REV_LIST_INPUT, "$git_cmd rev-list --parents $branch | ");
|
|
3190 |
open(REV_LIST_INPUT, "$GIT_CMD rev-list --parents $branch -- | ");
|
3195 | 3191 |
$names{$revision} = [$branch, 0];
|
3196 | 3192 |
|
3197 | 3193 |
# 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 | ");
|
3200 | 3200 |
$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";
|
3204 | 3204 |
chomp($less);
|
3205 | 3205 |
my $pid = open(OUTPUT, "| $less");
|
3206 | 3206 |
|
3207 | 3207 |
# Make sure that we don't leave the terminal in a weird state if the user
|
3208 | 3208 |
# hits Ctrl-C during eg log
|
3209 | 3209 |
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);
|
3211 | 3211 |
exit(0); };
|
3212 | 3212 |
|
3213 | 3213 |
#open(OUTPUT, ">&STDOUT");
|
|
3215 | 3215 |
# If it's a commit line, determine the name of the commit and print it too
|
3216 | 3216 |
# ANSI color escape sequences make this regex kind of ugly...
|
3217 | 3217 |
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);
|
3219 | 3219 |
print OUTPUT "$1 ($name)$3\n" if $name;
|
3220 | 3220 |
print OUTPUT "$1$3\n" if !$name;
|
3221 | 3221 |
} else {
|
|
3223 | 3223 |
}
|
3224 | 3224 |
}
|
3225 | 3225 |
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;
|
3228 | 3229 |
|
3229 | 3230 |
# Make sure we close the pipe from rev-list too; We use "$? && $!"
|
3230 | 3231 |
# instead of "$?" because we don't care about the return value of the
|
|
3235 | 3236 |
# (This is my best guess at what to do given the random failures from
|
3236 | 3237 |
# t1411-reflog-show.sh, and reading 'man perlfunc' under 'close'; it seems
|
3237 | 3238 |
# to work.)
|
3238 | |
close(REV_LIST_INPUT); $ret3 = $? && $!;
|
|
3239 |
close(REV_LIST_INPUT); $ret3 = ($? >> 8) && $!;
|
3239 | 3240 |
|
3240 | 3241 |
return $ret1 || $ret2 || $ret3;
|
3241 | 3242 |
}
|
|
3246 | 3247 |
package merge;
|
3247 | 3248 |
@merge::ISA = qw(subcommand);
|
3248 | 3249 |
INIT {
|
3249 | |
$command{merge} = {
|
|
3250 |
$COMMAND{merge} = {
|
3250 | 3251 |
unmodified_behavior => 1,
|
3251 | 3252 |
section => 'projects',
|
3252 | 3253 |
about => 'Join two or more development histories (branches) together'
|
|
3310 | 3311 |
package publish;
|
3311 | 3312 |
@publish::ISA = qw(subcommand);
|
3312 | 3313 |
INIT {
|
3313 | |
$command{publish} = {
|
|
3314 |
$COMMAND{publish} = {
|
3314 | 3315 |
extra => 1,
|
3315 | 3316 |
new_command => 1,
|
3316 | 3317 |
section => 'collaboration',
|
|
3328 | 3329 |
bless($self, $class);
|
3329 | 3330 |
$self->{'help'} = "
|
3330 | 3331 |
Usage:
|
3331 | |
eg publish [--bypass-modification-check] REMOTE_DIRECTORY
|
|
3332 |
eg publish [-b|--bypass-modification-check] [-g|--group GROUP]
|
|
3333 |
[REMOTE_ALIAS] SSH_URL
|
3332 | 3334 |
|
3333 | 3335 |
Description:
|
3334 | 3336 |
Publishes a copy of the current repository on a remote machine. Note
|
3335 | 3337 |
that local changes will be ignored; only committed changes will be
|
3336 | 3338 |
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.
|
3352 | 3355 |
|
3353 | 3356 |
Examples:
|
3354 | 3357 |
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 ..
|
3359 | 3363 |
\$ eg clone myserver.com:/var/scratch/git-stuff/my-repo.git
|
3360 | 3364 |
|
3361 | 3365 |
Publish a copy of the current repository on the machine www.gnome.org, in
|
|
3363 | 3367 |
user fake, then immediately clone it again into a separate directory
|
3364 | 3368 |
named another-myproj.
|
3365 | 3369 |
\$ eg publish fake\@www.gnome.org:public_html/myproj
|
3366 | |
\$ cd
|
|
3370 |
\$ cd ..
|
3367 | 3371 |
\$ eg clone http://www.gnome.org/~fake/myproj another-myproj
|
3368 | 3372 |
|
3369 | 3373 |
Options
|
|
3374 | 3378 |
option.
|
3375 | 3379 |
";
|
3376 | 3380 |
$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.
|
3396 | 3416 |
';
|
3397 | 3417 |
return $self;
|
3398 | 3418 |
}
|
|
3402 | 3422 |
my $package_name = ref($self);
|
3403 | 3423 |
|
3404 | 3424 |
my $bypass_modification_check = 0;
|
|
3425 |
my $group;
|
3405 | 3426 |
my $result = main::GetOptions(
|
3406 | 3427 |
"--help" => sub { $self->help() },
|
3407 | 3428 |
"bypass-modification-check|b" => \$bypass_modification_check,
|
|
3429 |
"group|g=s" => \$group,
|
3408 | 3430 |
);
|
3409 | 3431 |
|
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");
|
3412 | 3441 |
|
3413 | 3442 |
if (!$bypass_modification_check) {
|
3414 | 3443 |
my $status = RepoUtil::commit_push_checks($package_name,
|
3415 | |
{unknown => 1, changes => 1});
|
|
3444 |
{unknown => 1,
|
|
3445 |
changes => 1,
|
|
3446 |
unmerged_changes => 1});
|
3416 | 3447 |
} else {
|
3417 | 3448 |
# Record the set of unknown files we ignored with -b, so the -b flag
|
3418 | 3449 |
# isn't needed next time.
|
|
3423 | 3454 |
sub run {
|
3424 | 3455 |
my $self = shift;
|
3425 | 3456 |
|
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);
|
3494 | 3494 |
} 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 |
|
3513 | 3524 |
return 0;
|
3514 | 3525 |
}
|
3515 | 3526 |
|
|
3519 | 3530 |
package pull;
|
3520 | 3531 |
@pull::ISA = qw(subcommand);
|
3521 | 3532 |
INIT {
|
3522 | |
$command{pull} = {
|
|
3533 |
$COMMAND{pull} = {
|
3523 | 3534 |
section => 'collaboration',
|
3524 | 3535 |
about => 'Get updates from another repository and merge them'
|
3525 | 3536 |
};
|
|
3541 | 3552 |
See 'eg help topic remote-urls' for valid syntax for remote repositories.
|
3542 | 3553 |
If you frequently pull from the same repository, you may want to set up a
|
3543 | 3554 |
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'.
|
3545 | 3558 |
|
3546 | 3559 |
By default, tags in the remote repository associated with commits that
|
3547 | 3560 |
are pulled, will themselves be pulled. One can specify to pull
|
|
3623 | 3636 |
history so that your recent local commits become commits on top of the
|
3624 | 3637 |
changes downloaded from the remote repository.
|
3625 | 3638 |
|
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.
|
3632 | 3647 |
";
|
3633 | 3648 |
$self->{'differences'} = "
|
3634 | 3649 |
eg pull and git pull are nearly identical. eg provides a slightly more
|
|
3651 | 3666 |
|
3652 | 3667 |
sub preprocess {
|
3653 | 3668 |
my $self = shift;
|
3654 | |
my $package_name = ref($self);
|
3655 | 3669 |
|
3656 | 3670 |
#
|
3657 | 3671 |
# Parse options
|
|
3691 | 3705 |
);
|
3692 | 3706 |
die "Cannot specify both --all-tags and --no-tags!\n"
|
3693 | 3707 |
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"
|
3695 | 3709 |
if @tags && ($all_tags || $no_tags);
|
3696 | 3710 |
my $repository = shift @ARGV;
|
3697 | 3711 |
my @git_refspecs = @ARGV;
|
|
3703 | 3717 |
#
|
3704 | 3718 |
# Get the repository to pull from
|
3705 | 3719 |
#
|
|
3720 |
my $repo_is_a_remote = 1;
|
|
3721 |
my $repo_specified = defined($repository);
|
3706 | 3722 |
if ($repository) {
|
3707 | 3723 |
push(@{$self->{args}}, $repository);
|
3708 | |
} elsif (!$repository && @branches) {
|
|
3724 |
$repo_is_a_remote = 0 if !RepoUtil::get_config("remote.$repository.url");
|
|
3725 |
} else {
|
3709 | 3726 |
$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);
|
3713 | 3732 |
}
|
3714 | 3733 |
|
3715 | 3734 |
#
|
|
3717 | 3736 |
#
|
3718 | 3737 |
push(@branches, @git_refspecs);
|
3719 | 3738 |
|
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
|
3721 | 3749 |
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);
|
3733 | 3786 |
}
|
3734 | 3787 |
}
|
3735 | 3788 |
|
|
3744 | 3797 |
sub run {
|
3745 | 3798 |
my $self = shift;
|
3746 | 3799 |
|
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);
|
3749 | 3802 |
}
|
3750 | 3803 |
|
3751 | 3804 |
###########################################################################
|
|
3754 | 3807 |
package push;
|
3755 | 3808 |
@push::ISA = qw(subcommand);
|
3756 | 3809 |
INIT {
|
3757 | |
$command{push} = {
|
|
3810 |
$COMMAND{push} = {
|
3758 | 3811 |
section => 'collaboration',
|
3759 | 3812 |
about => 'Push local commits to a published repository'
|
3760 | 3813 |
};
|
|
3774 | 3827 |
|
3775 | 3828 |
Description:
|
3776 | 3829 |
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
|
3778 | 3835 |
in the current repository; this can be fixed by pulling and merging
|
3779 | 3836 |
changes from the remote repository (use eg pull for this) and then
|
3780 | 3837 |
repeating the push. Note that for getting changes directly to a fellow
|
|
3791 | 3848 |
|
3792 | 3849 |
If you frequently push to the same repository, you may want to set up a
|
3793 | 3850 |
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'.
|
3795 | 3853 |
|
3796 | 3854 |
Examples:
|
3797 | 3855 |
Push commits in the current branch
|
|
3854 | 3912 |
the remote repository.
|
3855 | 3913 |
";
|
3856 | 3914 |
$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'.
|
3884 | 3954 |
";
|
3885 | 3955 |
return $self;
|
3886 | 3956 |
}
|
3887 | 3957 |
|
3888 | |
sub _get_push_repository {
|
|
3958 |
sub _get_push_repository ($) {
|
3889 | 3959 |
my ($repository) = @_;
|
3890 | 3960 |
|
3891 | 3961 |
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;
|
3893 | 3965 |
} else {
|
3894 | 3966 |
return RepoUtil::get_config("remote.origin.url")
|
3895 | 3967 |
}
|
|
3899 | 3971 |
# undef the repository doesn't specify a valid repository or the repository
|
3900 | 3972 |
# is not of a type where we can determine bare-ness. Otherwise returns
|
3901 | 3973 |
# either the string "true" or "false".
|
3902 | |
sub _check_if_bare {
|
|
3974 |
sub _check_if_bare ($) {
|
3903 | 3975 |
my $repository = shift;
|
3904 | 3976 |
|
3905 | 3977 |
# Don't know how to check rsync, http, https, or git repositories to see
|
|
3917 | 3989 |
chdir($repository);
|
3918 | 3990 |
|
3919 | 3991 |
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",
|
3921 | 3993 |
ignore_ret => 1);
|
3922 | 3994 |
|
3923 | 3995 |
chdir($orig_dir);
|
|
3929 | 4001 |
#
|
3930 | 4002 |
# Check ssh systems
|
3931 | 4003 |
#
|
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);
|
3943 | 4005 |
return undef if !defined $machine || !defined $path;
|
3944 | 4006 |
|
3945 | 4007 |
my ($ret, $output) =
|
3946 | 4008 |
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'",
|
3948 | 4010 |
ignore_ret => 1);
|
3949 | 4011 |
return undef if $ret != 0;
|
3950 | 4012 |
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;
|
3952 | 4019 |
}
|
3953 | 4020 |
|
3954 | 4021 |
sub preprocess {
|
|
3973 | 4040 |
"--all-branches|all" => \$all_branches,
|
3974 | 4041 |
"--matching-branches" => \$matching_branches,
|
3975 | 4042 |
"--all-tags" => \$all_tags,
|
|
4043 |
"tags" => \$all_tags,
|
3976 | 4044 |
"--mirror" => \$mirror,
|
3977 | 4045 |
"--dry-run" => sub { &$record_arg(@_) },
|
|
4046 |
"--porcelain" => sub { &$record_arg(@_) },
|
|
4047 |
"--progress" => sub { &$record_arg(@_) },
|
3978 | 4048 |
"--receive-pack=s" => sub { &$record_args(@_) },
|
3979 | 4049 |
"force|f" => sub { &$record_arg(@_) },
|
3980 | 4050 |
"repo=s" => \$repo,
|
3981 | 4051 |
"thin" => sub { &$record_arg(@_) },
|
3982 | 4052 |
"no-thin" => sub { &$record_arg(@_) },
|
3983 | 4053 |
"verbose|v" => sub { &$record_arg(@_) },
|
|
4054 |
"set-upstream|u" => sub { &$record_arg(@_) },
|
3984 | 4055 |
"bypass-modification-check|b" => \$bypass_modification_check,
|
3985 | 4056 |
);
|
3986 | 4057 |
die "Cannot specify individual branches and request all branches too!\n"
|
|
3992 | 4063 |
|
3993 | 4064 |
if (!$bypass_modification_check) {
|
3994 | 4065 |
my $status = RepoUtil::commit_push_checks($package_name,
|
3995 | |
{unknown => 1, changes => 1});
|
|
4066 |
{unknown => 1,
|
|
4067 |
changes => 1,
|
|
4068 |
unmerged_changes => 1});
|
3996 | 4069 |
} else {
|
3997 | 4070 |
# Record the set of unknown files we ignored with -b, so the -b flag
|
3998 | 4071 |
# isn't needed next time.
|
|
4005 | 4078 |
|
4006 | 4079 |
my $default_specified = 0;
|
4007 | 4080 |
$default_specified = 1 if $all_branches;
|
4008 | |
$default_specified = 1 if $matching_branches;
|
4009 | 4081 |
$default_specified = 1 if $all_tags;
|
4010 | 4082 |
$default_specified = 1 if $mirror;
|
4011 | 4083 |
|
|
4023 | 4095 |
push(@{$self->{args}}, $repository);
|
4024 | 4096 |
$remote = $repository;
|
4025 | 4097 |
} else {
|
4026 | |
# Just drop through to the git pull defaults.
|
|
4098 |
# Just drop through to the git push defaults.
|
4027 | 4099 |
}
|
4028 | 4100 |
|
4029 | 4101 |
#
|
|
4031 | 4103 |
# ssh; I don't know how to detect other cases)...unless user explicitly
|
4032 | 4104 |
# specifies both source and destination references explicitly
|
4033 | 4105 |
#
|
|
4106 |
my $remote_chk = $remote || $repository;
|
4034 | 4107 |
$repository = _get_push_repository($repository);
|
4035 | 4108 |
my $push_to_non_bare_repo;
|
4036 | 4109 |
if ($repository) {
|
|
4076 | 4149 |
#
|
4077 | 4150 |
# Get the default branch to push to, if needed
|
4078 | 4151 |
#
|
|
4152 |
push(@{$self->{args}}, ":") if $matching_branches;
|
|
4153 |
$default_specified = 1 if $matching_branches;
|
|
4154 |
|
4079 | 4155 |
if (!@branches && !@tags && !@git_refspecs && !$default_specified) {
|
4080 | 4156 |
# User hasn't specified what to push; default choices:
|
4081 | 4157 |
# 1 - remote.$remote.(push|mirror) options
|
4082 | 4158 |
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
|
4090 | 4168 |
if (!$default_known) {
|
4091 | 4169 |
my $branch = RepoUtil::current_branch();
|
4092 | |
$push_branch = RepoUtil::get_config("branch.$branch.merge");
|
|
4170 |
my $push_branch = RepoUtil::get_config("branch.$branch.merge");
|
4093 | 4171 |
if (defined $push_branch) {
|
4094 | 4172 |
$push_branch =~ s#refs/heads/##;
|
4095 | 4173 |
push(@{$self->{args}}, "$branch:$push_branch");
|
4096 | 4174 |
$default_known = 1;
|
4097 | 4175 |
}
|
4098 | 4176 |
}
|
4099 | |
# 3 - the only branch that exists at the remote end
|
|
4177 |
# 4 - the only branch that exists at the remote end
|
4100 | 4178 |
if (!$default_known && defined $repository) {
|
4101 | 4179 |
my $only_branch = RepoUtil::get_only_branch($repository, "push");
|
4102 | 4180 |
push(@{$self->{args}}, $only_branch);
|
|
4116 | 4194 |
sub run {
|
4117 | 4195 |
my $self = shift;
|
4118 | 4196 |
|
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);
|
4121 | 4199 |
}
|
4122 | 4200 |
|
4123 | 4201 |
###########################################################################
|
|
4126 | 4204 |
package rebase;
|
4127 | 4205 |
@rebase::ISA = qw(subcommand);
|
4128 | 4206 |
INIT {
|
4129 | |
$command{rebase} = {
|
|
4207 |
$COMMAND{rebase} = {
|
4130 | 4208 |
extra => 1,
|
4131 | 4209 |
section => 'timesavers',
|
4132 | 4210 |
about => "Port local commits, making them be based on a different\n" .
|
|
4340 | 4418 |
|
4341 | 4419 |
sub preprocess {
|
4342 | 4420 |
my $self = shift;
|
4343 | |
my $package_name = ref($self);
|
4344 | 4421 |
|
4345 | 4422 |
#
|
4346 | 4423 |
# Parse options
|
|
4373 | 4450 |
sub run {
|
4374 | 4451 |
my $self = shift;
|
4375 | 4452 |
|
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);
|
4378 | 4455 |
}
|
4379 | 4456 |
|
4380 | 4457 |
###########################################################################
|
|
4383 | 4460 |
package remote;
|
4384 | 4461 |
@remote::ISA = qw(subcommand);
|
4385 | 4462 |
INIT {
|
4386 | |
$command{remote} = {
|
|
4463 |
$COMMAND{remote} = {
|
4387 | 4464 |
unmodified_behavior => 1,
|
4388 | 4465 |
extra => 1,
|
4389 | 4466 |
section => 'collaboration',
|
|
4489 | 4566 |
package reset;
|
4490 | 4567 |
@reset::ISA = qw(subcommand);
|
4491 | 4568 |
INIT {
|
4492 | |
$command{reset} = {
|
|
4569 |
$COMMAND{reset} = {
|
4493 | 4570 |
extra => 1,
|
4494 | 4571 |
section => 'modification',
|
4495 | 4572 |
about => 'Forget local commits and (optionally) undo their changes'
|
|
4599 | 4676 |
|
4600 | 4677 |
sub preprocess {
|
4601 | 4678 |
my $self = shift;
|
4602 | |
my $package_name = ref($self);
|
4603 | 4679 |
|
4604 | 4680 |
#
|
4605 | 4681 |
# Parse options
|
|
4624 | 4700 |
package resolved;
|
4625 | 4701 |
@resolved::ISA = qw(subcommand);
|
4626 | 4702 |
INIT {
|
4627 | |
$command{resolved} = {
|
|
4703 |
$COMMAND{resolved} = {
|
4628 | 4704 |
new_command => 1,
|
4629 | 4705 |
extra => 1,
|
4630 | 4706 |
section => 'compatibility',
|
|
4651 | 4727 |
be done and the contents ready to commit.
|
4652 | 4728 |
\$ eg resolved foo.c
|
4653 | 4729 |
";
|
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 |
";
|
4658 | 4740 |
return $self;
|
4659 | 4741 |
}
|
4660 | 4742 |
|
4661 | 4743 |
sub run {
|
4662 | 4744 |
my $self = shift;
|
4663 | |
my $package_name = ref($self);
|
4664 | |
|
|
4745 |
|
|
4746 |
die "Error: Must specify paths to resolve.\n" if !@ARGV;
|
4665 | 4747 |
@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);
|
4667 | 4786 |
}
|
4668 | 4787 |
|
4669 | 4788 |
###########################################################################
|
|
4672 | 4791 |
package revert;
|
4673 | 4792 |
@revert::ISA = qw(subcommand);
|
4674 | 4793 |
INIT {
|
4675 | |
$command{revert} = {
|
4676 | |
extra => 1,
|
|
4794 |
$COMMAND{revert} = {
|
4677 | 4795 |
section => 'modification',
|
4678 | 4796 |
about => 'Revert local changes and/or changes from previous commits'
|
4679 | 4797 |
};
|
|
4866 | 4984 |
|
4867 | 4985 |
sub preprocess {
|
4868 | 4986 |
my $self = shift;
|
4869 | |
my $package_name = ref($self);
|
4870 | 4987 |
|
4871 | 4988 |
my ($cur_dir, $top_dir, $git_dir) = RepoUtil::get_dirs();
|
4872 | 4989 |
my $initial_commit = RepoUtil::initial_commit();
|
|
4885 | 5002 |
);
|
4886 | 5003 |
|
4887 | 5004 |
# Parsing revs and files
|
4888 | |
my ($opts, $revs, $files) = RepoUtil::parse_args(@ARGV);
|
|
5005 |
my ($opts, $revs, $files) = RepoUtil::parse_args([], @ARGV);
|
4889 | 5006 |
unshift(@$revs, $rev) if defined($rev);
|
4890 | 5007 |
|
4891 | 5008 |
#
|
|
4917 | 5034 |
Alternatively, if you want to modify commits instead of just the working copy
|
4918 | 5035 |
then use reset instead of revert:
|
4919 | 5036 |
|
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
|
4922 | 5041 |
If you want to undo the last commit (but keep its changes in the working copy),
|
4923 | 5042 |
try:
|
4924 | 5043 |
eg reset $active_branch~1
|
|
4977 | 5096 |
to your flags to eg revert, where BRANCH is one of
|
4978 | 5097 |
$list
|
4979 | 5098 |
If you simply want to abort your merge and undo its conflicts, run
|
4980 | |
eg revert --since HEAD
|
|
5099 |
eg merge --abort
|
4981 | 5100 |
EOF
|
4982 | 5101 |
exit 1;
|
4983 | 5102 |
}
|
|
4990 | 5109 |
}
|
4991 | 5110 |
|
4992 | 5111 |
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`;
|
4994 | 5113 |
if (@unmerged_files && $in) {
|
4995 | 5114 |
die "Aborting: please clear conflicts from @unmerged_files before " .
|
4996 | 5115 |
"proceeding.\n";
|
|
5008 | 5127 |
# Get the revision whose changes we want to revert, and its parents
|
5009 | 5128 |
Util::push_debug(new_value => 0);
|
5010 | 5129 |
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}");
|
5012 | 5131 |
Util::pop_debug();
|
5013 | 5132 |
my @list = split(' ', $links); # commit id + parent ids
|
5014 | 5133 |
|
|
5036 | 5155 |
# Determine some other stuff needed
|
5037 | 5156 |
$self->{files} = \@quoted_files;
|
5038 | 5157 |
my ($new_files, $newish_files, $revert_files);
|
|
5158 |
my ($newly_added_files, $new_since_rev_files, $other_files);
|
5039 | 5159 |
if (!$in && !$initial_commit) {
|
5040 | 5160 |
my $revision = (@$revs) ? $revs->[0] : "HEAD";
|
5041 | 5161 |
($newly_added_files, $new_since_rev_files, $other_files) =
|
|
5068 | 5188 |
# Case: Initial commit
|
5069 | 5189 |
#
|
5070 | 5190 |
if ($self->{initial_commit}) {
|
5071 | |
$ret = ExecUtil::execute("$git_cmd rm --cached --quiet -- " .
|
|
5191 |
$ret = ExecUtil::execute("$GIT_CMD rm --cached --quiet -- " .
|
5072 | 5192 |
"@newly_added_files", ignore_ret => 1);
|
5073 | 5193 |
exit $ret if $ret;
|
5074 | 5194 |
}
|
|
5086 | 5206 |
# state; if three files have conflicts and I try to reset some
|
5087 | 5207 |
# file other than those three, the command is successful but it
|
5088 | 5208 |
# spews warnings and gives a bad exit status
|
5089 | |
ExecUtil::execute("$git_cmd reset -q $revision --" .
|
|
5209 |
ExecUtil::execute("$GIT_CMD reset -q $revision --" .
|
5090 | 5210 |
" @newly_added_files >/dev/null",
|
5091 | 5211 |
ignore_ret => 1);
|
5092 | 5212 |
}
|
|
5095 | 5215 |
if ($revision ne "HEAD") {
|
5096 | 5216 |
Util::push_debug(new_value => 0);
|
5097 | 5217 |
($temp_ret, $revision_sha1) =
|
5098 | |
ExecUtil::execute_captured("$git_cmd rev-parse --verify $revision",
|
|
5218 |
ExecUtil::execute_captured("$GIT_CMD rev-parse --verify $revision",
|
5099 | 5219 |
ignore_ret => 1);
|
5100 | 5220 |
($temp_ret, $head_sha1) =
|
5101 | |
ExecUtil::execute_captured("$git_cmd rev-parse --verify HEAD",
|
|
5221 |
ExecUtil::execute_captured("$GIT_CMD rev-parse --verify HEAD",
|
5102 | 5222 |
ignore_ret => 1);
|
5103 | 5223 |
Util::pop_debug();
|
5104 | 5224 |
}
|
5105 | 5225 |
|
5106 | |
$ret = ExecUtil::execute("$git_cmd reset --hard $revision",
|
|
5226 |
$ret = ExecUtil::execute("$GIT_CMD reset --hard $revision",
|
5107 | 5227 |
ignore_ret => 1);
|
5108 | 5228 |
exit $ret if $ret;
|
5109 | 5229 |
|
5110 | 5230 |
if ($revision ne "HEAD" && $revision_sha1 ne $head_sha1) {
|
5111 | 5231 |
# Note, cannot git reset --soft HEAD, since HEAD has changed in
|
5112 | 5232 |
# the above reset...
|
5113 | |
$ret = ExecUtil::execute("$git_cmd reset --soft HEAD\@{1}",
|
|
5233 |
$ret = ExecUtil::execute("$GIT_CMD reset --soft HEAD\@{1}",
|
5114 | 5234 |
ignore_ret => 1);
|
5115 | 5235 |
exit $ret if $ret;
|
5116 | 5236 |
}
|
|
5122 | 5242 |
if ($paths_specified) {
|
5123 | 5243 |
if (@newly_added_files) {
|
5124 | 5244 |
# 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 --" .
|
5126 | 5246 |
" @newly_added_files >/dev/null",
|
5127 | 5247 |
ignore_ret => 1);
|
5128 | 5248 |
}
|
5129 | 5249 |
if (@new_since_rev_files) {
|
5130 | 5250 |
# Ugh, when --quiet doesn't actually mean "quiet".
|
5131 | 5251 |
# (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 " .
|
5133 | 5253 |
"--ignore-unmatch -- @new_since_rev_files" .
|
5134 | 5254 |
" > /dev/null",
|
5135 | 5255 |
ignore_ret => 1);
|
5136 | 5256 |
exit $ret if $ret;
|
5137 | 5257 |
}
|
5138 | 5258 |
if (@other_files) {
|
5139 | |
$ret = ExecUtil::execute("$git_cmd checkout $revision -- " .
|
|
5259 |
$ret = ExecUtil::execute("$GIT_CMD checkout $revision -- " .
|
5140 | 5260 |
"@other_files", ignore_ret => 1);
|
5141 | 5261 |
exit $ret if $ret;
|
5142 | 5262 |
}
|
|
5148 | 5268 |
#
|
5149 | 5269 |
elsif ($self->{staged}) {
|
5150 | 5270 |
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",
|
5152 | 5272 |
ignore_ret => 1);
|
5153 | 5273 |
exit $ret if $ret;
|
5154 | 5274 |
} else {
|
5155 | |
$ret = ExecUtil::execute("$git_cmd read-tree $revision",
|
|
5275 |
$ret = ExecUtil::execute("$GIT_CMD read-tree $revision",
|
5156 | 5276 |
ignore_ret => 1);
|
5157 | 5277 |
exit $ret if $ret;
|
5158 | 5278 |
}
|
|
5164 | 5284 |
elsif ($self->{unstaged}) {
|
5165 | 5285 |
if ($self->{just_recent_unstaged}) {
|
5166 | 5286 |
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",
|
5168 | 5288 |
ignore_ret => 1);
|
5169 | 5289 |
exit $ret if $ret;
|
5170 | 5290 |
}
|
|
5182 | 5302 |
}
|
5183 | 5303 |
|
5184 | 5304 |
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 | |
|
5188 | 5305 |
my $cf = "@other_files";
|
5189 | 5306 |
if (!$paths_specified) {
|
5190 | 5307 |
my ($cur_dir, $top_dir, $git_dir) = RepoUtil::get_dirs();
|
5191 | 5308 |
($cf) = Util::reroot_paths__from_to_files($top_dir, $cur_dir, '.');
|
5192 | 5309 |
}
|
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 " .
|
5194 | 5313 |
"-- $cf", ignore_ret => 1);
|
|
5314 |
$ENV{"GIT_INDEX_FILE"} = $old_git_index_file;
|
5195 | 5315 |
exit $ret if $ret;
|
5196 | 5316 |
|
|
5317 |
my ($tmp_index) = Util::quote_args("$git_dir/tmp_index");
|
5197 | 5318 |
$ret = ExecUtil::execute("rm $tmp_index");
|
5198 | 5319 |
exit $ret if $ret;
|
5199 | 5320 |
}
|
|
5220 | 5341 |
|
5221 | 5342 |
# Print out the (nearly) equivalent commands if the user asked for
|
5222 | 5343 |
# debugging information
|
5223 | |
if ($debug) {
|
|
5344 |
if ($DEBUG) {
|
5224 | 5345 |
print " >>Running: " .
|
5225 | |
"$git_cmd diff @diff_flags $self->{revs} ${marker}@files | ";
|
|
5346 |
"$GIT_CMD diff @diff_flags $self->{revs} ${marker}@files | ";
|
5226 | 5347 |
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";
|
5228 | 5349 |
print ")" if ($top_dir ne $cur_dir);
|
5229 | 5350 |
print "\n";
|
5230 | 5351 |
}
|
|
5234 | 5355 |
# we have to run diff, slurp in its output, check if its nonempty,
|
5235 | 5356 |
# and then only pipe that output back out to git apply if we have
|
5236 | 5357 |
# 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 |");
|
5239 | 5360 |
my @output = <DIFF>;
|
5240 | 5361 |
my $diff = join("", @output);
|
5241 | 5362 |
# Listing unmerged paths doesn't count as nonempty
|
|
5246 | 5367 |
|
5247 | 5368 |
if ($diff) {
|
5248 | 5369 |
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");
|
5250 | 5371 |
print APPLY $diff;
|
5251 | 5372 |
close(APPLY);
|
5252 | 5373 |
chdir($cur_dir) if $top_dir ne $cur_dir;
|
|
5263 | 5384 |
package rm;
|
5264 | 5385 |
@rm::ISA = qw(subcommand);
|
5265 | 5386 |
INIT {
|
5266 | |
$command{rm} = {
|
|
5387 |
$COMMAND{rm} = {
|
5267 | 5388 |
extra => 1,
|
5268 | 5389 |
section => 'modification',
|
5269 | 5390 |
about => 'Remove files from subsequent commits and the working copy'
|
|
5330 | 5451 |
|
5331 | 5452 |
sub preprocess {
|
5332 | 5453 |
my $self = shift;
|
5333 | |
my $package_name = ref($self);
|
5334 | 5454 |
|
5335 | 5455 |
return if (scalar(@ARGV) > 0 && $ARGV[0] eq "--");
|
5336 | 5456 |
my $result = main::GetOptions("--help" => sub { $self->help() });
|
|
5346 | 5466 |
package squash;
|
5347 | 5467 |
@squash::ISA = qw(subcommand);
|
5348 | 5468 |
INIT {
|
5349 | |
$command{squash} = {
|
|
5469 |
$COMMAND{squash} = {
|
5350 | 5470 |
new_command => 1,
|
5351 | 5471 |
extra => 1,
|
5352 | 5472 |
section => 'modification',
|
|
5391 | 5511 |
|
5392 | 5512 |
sub preprocess {
|
5393 | 5513 |
my $self = shift;
|
5394 | |
my $package_name = ref($self);
|
5395 | 5514 |
|
5396 | 5515 |
my $since;
|
5397 | 5516 |
my $result = main::GetOptions(
|
|
5403 | 5522 |
if (!defined($since)) {
|
5404 | 5523 |
my $branch = RepoUtil::current_branch();
|
5405 | 5524 |
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");
|
5408 | 5527 |
die "Aborting: No revision specified.\n" if !defined($merge_branch);
|
5409 | 5528 |
$merge_branch =~ s#^refs/heads/##;
|
5410 | 5529 |
$since = "$merge_remote/$merge_branch";
|
|
5417 | 5536 |
|
5418 | 5537 |
# Get the sha1sum where HEAD points now, make sure HEAD is valid
|
5419 | 5538 |
($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);
|
5421 | 5540 |
die "Aborting: You have no commits on HEAD.\n" if $retval != 0;
|
5422 | 5541 |
chomp($orig_head);
|
5423 | 5542 |
$self->{orig_head} = $orig_head;
|
5424 | 5543 |
|
5425 | 5544 |
# Get the sha1sum where $since points now, make sure it is valid
|
5426 | 5545 |
($retval, $since_sha1sum) =
|
5427 | |
ExecUtil::execute_captured("$git_cmd rev-parse $self->{since}",
|
|
5546 |
ExecUtil::execute_captured("$GIT_CMD rev-parse $self->{since}",
|
5428 | 5547 |
ignore_ret => 1);
|
5429 | 5548 |
die "Invalid revision reference: $self->{since}\n" if $retval != 0;
|
5430 | 5549 |
chomp($since_sha1sum);
|
5431 | 5550 |
|
5432 | 5551 |
# Make sure user has no staged changes
|
5433 | |
my $output = `$git_cmd diff --cached --quiet`;
|
|
5552 |
my $output = `$GIT_CMD diff --cached --quiet`;
|
5434 | 5553 |
die "Aborting: You have staged changes; please commit them first.\n" if $?;
|
5435 | 5554 |
|
5436 | 5555 |
# 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;
|
5442 | 5562 |
die "Aborting: There are no commits since $self->{since}.\n"
|
5443 | 5563 |
if $orig_head eq $since_sha1sum;
|
5444 | 5564 |
|
|
5447 | 5567 |
|
5448 | 5568 |
sub run {
|
5449 | 5569 |
my $self = shift;
|
5450 | |
my $package_name = ref($self);
|
5451 | 5570 |
|
5452 | 5571 |
# Fill out a basic log message
|
5453 | 5572 |
my ($fh, $filename) = main::tempfile();
|
|
5457 | 5576 |
|
5458 | 5577 |
EOF
|
5459 | 5578 |
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:" .
|
5461 | 5580 |
"'#commit %H%n#Author: %an <%ae>%n#Date: %ad%n%n%s%n%n%b' " .
|
5462 | 5581 |
" $self->{since}..$self->{orig_head} >> $filename");
|
5463 | 5582 |
exit $ret if $ret;
|
5464 | 5583 |
|
5465 | 5584 |
# Now, reset and commit
|
5466 | |
$ret = ExecUtil::execute("$git_cmd reset --soft $self->{since}");
|
|
5585 |
$ret = ExecUtil::execute("$GIT_CMD reset --soft $self->{since}");
|
5467 | 5586 |
exit $ret if $ret;
|
5468 | |
$ret = ExecUtil::execute("$git_cmd commit -F $filename --edit",
|
|
5587 |
$ret = ExecUtil::execute("$GIT_CMD commit -F $filename --edit",
|
5469 | 5588 |
ignore_ret => 1);
|
5470 | 5589 |
|
5471 | 5590 |
# 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;
|
5473 | 5592 |
|
5474 | 5593 |
unlink($filename);
|
5475 | 5594 |
return 0;
|
|
5481 | 5600 |
package stage;
|
5482 | 5601 |
@stage::ISA = qw(subcommand);
|
5483 | 5602 |
INIT {
|
5484 | |
$command{stage} = {
|
|
5603 |
$COMMAND{stage} = {
|
5485 | 5604 |
new_command => 1,
|
5486 | 5605 |
section => 'modification',
|
5487 | 5606 |
about => 'Mark content in files as being ready for commit'
|
|
5501 | 5620 |
Description:
|
5502 | 5621 |
Marks the contents of the specified files as being ready to commit,
|
5503 | 5622 |
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.
|
5506 | 5627 |
|
5507 | 5628 |
You can use 'eg unstage PATH...' to unstage files.
|
5508 | 5629 |
|
|
5547 | 5668 |
|
5548 | 5669 |
sub run {
|
5549 | 5670 |
my $self = shift;
|
5550 | |
my $package_name = ref($self);
|
5551 | 5671 |
|
5552 | 5672 |
@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);
|
5554 | 5674 |
}
|
5555 | 5675 |
|
5556 | 5676 |
###########################################################################
|
|
5559 | 5679 |
package stash;
|
5560 | 5680 |
@stash::ISA = qw(subcommand);
|
5561 | 5681 |
INIT {
|
5562 | |
$command{stash} = {
|
|
5682 |
$COMMAND{stash} = {
|
5563 | 5683 |
section => 'timesavers',
|
5564 | 5684 |
about => 'Save and revert local changes, or apply stashed changes',
|
5565 | 5685 |
};
|
|
5593 | 5713 |
When no arguments are specified to eg stash, the current changes are
|
5594 | 5714 |
saved away with a default description.
|
5595 | 5715 |
|
|
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 |
|
5596 | 5724 |
Examples:
|
5597 | 5725 |
You have lots of changes that you're working on, then get an important
|
5598 | 5726 |
but simple bug report. You can stash away your current changes, fix the
|
|
5679 | 5807 |
# Parse options
|
5680 | 5808 |
#
|
5681 | 5809 |
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 |
}
|
5683 | 5813 |
|
5684 | 5814 |
# Get the (sub)subcommand
|
5685 | 5815 |
if (scalar @ARGV == 0) {
|
5686 | 5816 |
$self->{subcommand} = 'save';
|
|
5817 |
} elsif ($ARGV[0] =~ /^--/) {
|
|
5818 |
$self->{subcommand} = "save";
|
5687 | 5819 |
} else {
|
5688 | 5820 |
$self->{subcommand} = shift @ARGV;
|
|
5821 |
if ($self->{subcommand} eq '-k') {
|
|
5822 |
$self->{subcommand} = "save";
|
|
5823 |
unshift @ARGV, '-k';
|
|
5824 |
}
|
5689 | 5825 |
push(@args, $self->{subcommand});
|
5690 | 5826 |
|
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 {
|
5700 | 5836 |
push(@args, shift @ARGV);
|
5701 | 5837 |
}
|
5702 | 5838 |
}
|
|
5719 | 5855 |
if ((grep {$_ eq $self->{subcommand}} @commands_accepting_existing_stash) &&
|
5720 | 5856 |
scalar @ARGV > 0) {
|
5721 | 5857 |
my $stash_description = "@ARGV";
|
|
5858 |
my $single_word_desc = (scalar(@ARGV) == 1);
|
5722 | 5859 |
@ARGV = ();
|
5723 | |
if ($stash_description =~ m#^stash\@{[^{]+}$#) {
|
|
5860 |
if ($stash_description =~ m#^stash\@{[^{]+}$# ||
|
|
5861 |
($single_word_desc && RepoUtil::valid_ref($stash_description))) {
|
5724 | 5862 |
push(@args, $stash_description)
|
5725 | 5863 |
} else {
|
5726 | 5864 |
# Will need to compare arguments to existing stash descriptions...
|
5727 | 5865 |
print " >>Getting stash descriptions to compare to arguments:\n"
|
5728 | |
if $debug;
|
|
5866 |
if $DEBUG;
|
5729 | 5867 |
my ($retval, $output) =
|
5730 | |
ExecUtil::execute_captured("$eg_exec stash list --refs");
|
|
5868 |
ExecUtil::execute_captured("$EG_EXEC stash list --refs");
|
5731 | 5869 |
my @lines = split('\n', $output);
|
5732 | 5870 |
my %refs;
|
5733 | 5871 |
my %bad_refs;
|
|
5760 | 5898 |
|
5761 | 5899 |
push(@args, $refs{$stash_description});
|
5762 | 5900 |
}
|
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 | |
}
|
5772 | 5901 |
}
|
5773 | 5902 |
|
5774 | 5903 |
# Add any unprocessed args to the arguments to use
|
|
5778 | 5907 |
@ARGV = @args;
|
5779 | 5908 |
}
|
5780 | 5909 |
|
5781 | |
sub postprocess {
|
|
5910 |
sub run {
|
5782 | 5911 |
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);
|
5791 | 5916 |
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);
|
5792 | 5924 |
my $regex =
|
5793 | |
qr#(stash\@{[^}]+}): (?:WIP )?[Oo]n [^ ]*: (?:[0-9a-f]+\.\.\. )?#;
|
|
5925 |
qr#(stash\@{[^}]+}): (?:WIP )?[Oo]n [^:]*: (?:[0-9a-f]+\.\.\. )?#;
|
5794 | 5926 |
foreach my $line (@lines) {
|
5795 | 5927 |
if ($self->{show_details}) {
|
5796 | 5928 |
print "$line\n";
|
|
5801 | 5933 |
}
|
5802 | 5934 |
}
|
5803 | 5935 |
} 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;
|
5808 | 5939 |
}
|
5809 | 5940 |
|
5810 | 5941 |
###########################################################################
|
|
5813 | 5944 |
package status;
|
5814 | 5945 |
@status::ISA = qw(subcommand);
|
5815 | 5946 |
INIT {
|
5816 | |
$command{status} = {
|
|
5947 |
$COMMAND{status} = {
|
5817 | 5948 |
section => 'discovery',
|
5818 | 5949 |
about => 'Summarize current changes'
|
5819 | 5950 |
};
|
|
5951 |
$ALIAS{'st'} = "status";
|
5820 | 5952 |
}
|
5821 | 5953 |
|
5822 | 5954 |
sub new {
|
|
5829 | 5961 |
|
5830 | 5962 |
Description:
|
5831 | 5963 |
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:
|
5834 | 5968 |
|
5835 | 5969 |
Unknown files
|
5836 | 5970 |
Files that are not explicitly ignored (i.e. do not appear in an
|
|
5852 | 5986 |
subdirectories that are tracked under their own git repository, and
|
5853 | 5987 |
that are being tracked via use of the 'git submodule' command.
|
5854 | 5988 |
|
5855 | |
Changed but not updated (\"unstaged\")
|
|
5989 |
Changes not staged for commit (\"unstaged\")
|
5856 | 5990 |
Files whose contents have been modified in the working copy.
|
5857 | 5991 |
|
5858 | 5992 |
(Advanced usage note) If you explicitly mark all the changes in a
|
|
5860 | 5994 |
list and will instead appear in the \"staged\" list (see below).
|
5861 | 5995 |
However, a file can appear in both the unstaged and staged lists if
|
5862 | 5996 |
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'.
|
5863 | 6004 |
|
5864 | 6005 |
Changes ready to be committed (\"staged\")
|
5865 | 6006 |
Files with content changes that have explicitly been marked as ready
|
|
5911 | 6052 |
sub preprocess {
|
5912 | 6053 |
my $self = shift;
|
5913 | 6054 |
|
|
6055 |
my @old_argv = @ARGV;
|
|
6056 |
my $no_filter = 0;
|
5914 | 6057 |
Getopt::Long::Configure("permute");
|
5915 | 6058 |
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 },
|
5918 | 6064 |
);
|
|
6065 |
@ARGV = @old_argv;
|
|
6066 |
$self->{no_filter} = $no_filter;
|
5919 | 6067 |
}
|
5920 | 6068 |
|
5921 | 6069 |
sub run {
|
5922 | 6070 |
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};
|
5924 | 6074 |
|
5925 | 6075 |
$self->{special_state} = RepoUtil::get_special_state($self->{git_dir});
|
5926 | 6076 |
|
5927 | 6077 |
@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);
|
5930 | 6079 |
}
|
5931 | 6080 |
|
5932 | 6081 |
sub postprocess {
|
5933 | 6082 |
my $self = shift;
|
5934 | 6083 |
my $output = shift;
|
5935 | 6084 |
|
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) {
|
5937 | 6090 |
print " >>(No commands to run, just data to print)<<\n";
|
5938 | 6091 |
return;
|
5939 | 6092 |
}
|
5940 | 6093 |
|
|
6094 |
if ($self->{no_filter}) {
|
|
6095 |
print $output;
|
|
6096 |
return;
|
|
6097 |
}
|
|
6098 |
|
5941 | 6099 |
my $branch;
|
5942 | 6100 |
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 |
|
5944 | 6111 |
my @basic_info;
|
|
6112 |
my @diff_info;
|
5945 | 6113 |
|
5946 | 6114 |
# Exit early if git status had an error
|
5947 | 6115 |
if ($output =~ m/^fatal:/) {
|
5948 | 6116 |
print STDERR $output;
|
5949 | 6117 |
exit 128;
|
5950 | 6118 |
}
|
|
6119 |
|
|
6120 |
my $status_hints = RepoUtil::get_config("advice.statusHints");
|
|
6121 |
$status_hints = (!defined($status_hints) || $status_hints eq "true");
|
5951 | 6122 |
|
5952 | 6123 |
# Parse the output
|
5953 | 6124 |
my @lines = split('\n', $output);
|
|
5955 | 6126 |
while (@lines) {
|
5956 | 6127 |
my $line = shift @lines;
|
5957 | 6128 |
my $section = undef;
|
|
6129 |
my $title;
|
5958 | 6130 |
|
5959 | 6131 |
if ($line =~ m/^# On branch (.*)$/) {
|
5960 | 6132 |
$branch = $1;
|
5961 | 6133 |
} elsif ($line =~ m/^# Initial commit$/) {
|
5962 | 6134 |
$initial_commit = 1;
|
5963 | |
} elsif ($line =~ m/^# Untracked files:$/) {
|
|
6135 |
} elsif ($line =~ m/^# ([A-Z].*:)$/) {
|
5964 | 6136 |
$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;
|
5979 | 6139 |
} elsif ($cur_state < 0) {
|
5980 | 6140 |
next if $line !~ m/^# (.+)/;
|
5981 | 6141 |
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";
|
5982 | 6153 |
}
|
5983 | 6154 |
|
5984 | 6155 |
# If we're inside a section type, parse it
|
5985 | 6156 |
if ($cur_state > 0) {
|
|
6157 |
push (@sections, $section);
|
5986 | 6158 |
my @section_files;
|
5987 | 6159 |
my $hints;
|
5988 | 6160 |
|
5989 | 6161 |
# Parse the hints first
|
5990 | 6162 |
$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
|
5994 | 6170 |
}
|
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";
|
6003 | 6184 |
}
|
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";
|
6014 | 6186 |
}
|
6015 | 6187 |
$line = shift @lines;
|
|
6188 |
unshift(@lines, $line) if $line && $line =~ m#^(?:\e\[.*?m)?diff --git a/#;
|
6016 | 6189 |
}
|
6017 | 6190 |
|
6018 | 6191 |
if (defined($files{$section})) {
|
6019 | 6192 |
push(@{$files{$section}{'file_list'}}, @section_files);
|
6020 | 6193 |
} else {
|
6021 | |
$files{$section} = { title => $title,
|
|
6194 |
$files{$section} = { title => $title, # may be undef
|
6022 | 6195 |
hint => $hints,
|
6023 | 6196 |
file_list => \@section_files };
|
6024 | 6197 |
}
|
6025 | 6198 |
|
6026 | 6199 |
# Record that we finished parsing this section
|
6027 | 6200 |
$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');
|
6028 | 6241 |
}
|
6029 | 6242 |
}
|
6030 | 6243 |
|
|
6037 | 6250 |
foreach my $line (@basic_info) {
|
6038 | 6251 |
print "($line)\n";
|
6039 | 6252 |
}
|
|
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 |
}
|
6040 | 6259 |
|
6041 | 6260 |
# Print out info about any special state we're in
|
6042 | 6261 |
my $notice = "";
|
6043 | 6262 |
if (defined $self->{special_state}) {
|
6044 | 6263 |
my ($highlight, $reset) = ("", "");
|
6045 | 6264 |
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"`;
|
6048 | 6267 |
}
|
6049 | 6268 |
|
6050 | 6269 |
$notice .= "($highlight";
|
|
6068 | 6287 |
print $notice;
|
6069 | 6288 |
}
|
6070 | 6289 |
|
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 | |
|
6106 | 6290 |
# Print out all the various changes
|
6107 | 6291 |
my $linecount = 0;
|
6108 | |
foreach my $section ('staged', 'unstaged', 'submodules',
|
6109 | |
'new_unknowns', 'old_unknowns') {
|
|
6292 |
foreach my $section (@sections) {
|
6110 | 6293 |
if (defined($files{$section})) {
|
6111 | 6294 |
print "$files{$section}{'title'}\n";
|
6112 | 6295 |
$linecount += 1;
|
|
6122 | 6305 |
print $notice;
|
6123 | 6306 |
}
|
6124 | 6307 |
|
|
6308 |
# Print the diff if we're running with the -v option
|
|
6309 |
print join("\n", @diff_info)."\n" if (@diff_info);
|
6125 | 6310 |
}
|
6126 | 6311 |
|
6127 | 6312 |
###########################################################################
|
|
6130 | 6315 |
package switch;
|
6131 | 6316 |
@switch::ISA = qw(subcommand);
|
6132 | 6317 |
INIT {
|
6133 | |
$command{switch} = {
|
|
6318 |
$COMMAND{switch} = {
|
6134 | 6319 |
new_command => 1,
|
6135 | 6320 |
section => 'projects',
|
6136 | 6321 |
about => 'Switch the working copy to another branch'
|
6137 | 6322 |
};
|
|
6323 |
$ALIAS{'sw'} = "switch";
|
6138 | 6324 |
}
|
6139 | 6325 |
|
6140 | 6326 |
sub new {
|
|
6210 | 6396 |
|
6211 | 6397 |
sub run {
|
6212 | 6398 |
my $self = shift;
|
6213 | |
my $package_name = ref($self);
|
6214 | 6399 |
|
6215 | 6400 |
@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);
|
6217 | 6402 |
}
|
6218 | 6403 |
|
6219 | 6404 |
###########################################################################
|
|
6222 | 6407 |
package tag;
|
6223 | 6408 |
@tag::ISA = qw(subcommand);
|
6224 | 6409 |
INIT {
|
6225 | |
$command{tag} = {
|
|
6410 |
$COMMAND{tag} = {
|
6226 | 6411 |
unmodified_behavior => 1,
|
6227 | 6412 |
extra => 1,
|
6228 | 6413 |
section => 'modification',
|
|
6274 | 6459 |
}
|
6275 | 6460 |
|
6276 | 6461 |
###########################################################################
|
|
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 |
###########################################################################
|
6277 | 6664 |
# unstage #
|
6278 | 6665 |
###########################################################################
|
6279 | 6666 |
package unstage;
|
6280 | 6667 |
@unstage::ISA = qw(revert);
|
6281 | 6668 |
INIT {
|
6282 | |
$command{unstage} = {
|
|
6669 |
$COMMAND{unstage} = {
|
6283 | 6670 |
new_command => 1,
|
6284 | 6671 |
extra => 1,
|
6285 | 6672 |
section => 'modification',
|
|
6339 | 6726 |
package update;
|
6340 | 6727 |
@update::ISA = qw(subcommand);
|
6341 | 6728 |
INIT {
|
6342 | |
$command{update} = {
|
|
6729 |
$COMMAND{update} = {
|
6343 | 6730 |
new_command => 1,
|
6344 | 6731 |
extra => 1,
|
6345 | 6732 |
section => 'compatibility',
|
|
6436 | 6823 |
exit 1;
|
6437 | 6824 |
}
|
6438 | 6825 |
|
6439 | |
if ($debug) {
|
|
6826 |
if ($DEBUG) {
|
6440 | 6827 |
print " >>Commands to determine where to update from:\n";
|
6441 | 6828 |
}
|
6442 | 6829 |
|
|
6454 | 6841 |
# otherwise throw an error
|
6455 | 6842 |
my ($quoted_repo) = Util::quote_args("$self->{repository}");
|
6456 | 6843 |
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");
|
6458 | 6845 |
if ($ret == 0) {
|
6459 | 6846 |
my @remote_refs = split('\n', $output);
|
6460 | 6847 |
if (@remote_refs == 1) {
|
|
6486 | 6873 |
# Get value to set ORIG_HEAD to (unless we are on the initial commit)
|
6487 | 6874 |
Util::push_debug(new_value => 0);
|
6488 | 6875 |
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);
|
6490 | 6877 |
my $has_orig_head = ($retval == 0);
|
6491 | 6878 |
Util::pop_debug();
|
6492 | 6879 |
|
6493 | 6880 |
# Do the fetch && reset, making sure to set ORIG_HEAD
|
6494 | 6881 |
my ($ret, $output) =
|
6495 | |
ExecUtil::execute_captured("$git_cmd fetch $self->{repository} " .
|
|
6882 |
ExecUtil::execute_captured("$GIT_CMD fetch $self->{repository} " .
|
6496 | 6883 |
"$self->{merge_branch}:$self->{local_branch}",
|
6497 | 6884 |
ignore_ret => 1);
|
6498 | 6885 |
if ($output =~ /\[rejected\].*\(non fast forward\)/) {
|
|
6502 | 6889 |
die "Error updating (output = $output); please report the bug, and\n" .
|
6503 | 6890 |
"try using 'eg pull' instead.\n";
|
6504 | 6891 |
} else {
|
6505 | |
$ret = ExecUtil::execute_captured("$git_cmd reset --hard " .
|
|
6892 |
$ret = ExecUtil::execute_captured("$GIT_CMD reset --hard " .
|
6506 | 6893 |
"$self->{local_branch}");
|
6507 | |
if ($has_orig_head && $debug < 2) {
|
|
6894 |
if ($has_orig_head && $DEBUG < 2) {
|
6508 | 6895 |
open(ORIG_HEAD, "> $self->{git_dir}/ORIG_HEAD");
|
6509 | 6896 |
print ORIG_HEAD $output;
|
6510 | 6897 |
close(ORIG_HEAD);
|
6511 | 6898 |
}
|
6512 | |
print "Updated the current branch.\n" if ($debug < 2);
|
|
6899 |
print "Updated the current branch.\n" if ($DEBUG < 2);
|
6513 | 6900 |
}
|
6514 | 6901 |
return $ret;
|
6515 | 6902 |
}
|
|
6530 | 6917 |
bless($self, $class);
|
6531 | 6918 |
}
|
6532 | 6919 |
|
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}
|
6534 | 6921 |
sub help {
|
6535 | 6922 |
my $self = shift;
|
6536 | 6923 |
|
|
6551 | 6938 |
sub run {
|
6552 | 6939 |
my $self = shift;
|
6553 | 6940 |
|
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;
|
6556 | 6943 |
return $self->SUPER::run();
|
6557 | 6944 |
}
|
6558 | 6945 |
|
|
6572 | 6959 |
package ExecUtil;
|
6573 | 6960 |
|
6574 | 6961 |
# _execute_impl is the guts for execute() and execute_captured()
|
6575 | |
sub _execute_impl {
|
|
6962 |
sub _execute_impl ($@) {
|
6576 | 6963 |
my ($command, @opts) = @_;
|
6577 | 6964 |
my ($ret, $output);
|
6578 | 6965 |
my %options = ( ignore_ret => 0, capture_output => 0, @opts );
|
6579 | 6966 |
|
6580 | |
if ($debug) {
|
|
6967 |
if ($DEBUG) {
|
6581 | 6968 |
print " >>Running: '$command'<<\n";
|
6582 | |
return $options{capture_output} ? (0, "") : 0 if $debug == 2;
|
|
6969 |
return $options{capture_output} ? (0, "") : 0 if $DEBUG == 2;
|
6583 | 6970 |
}
|
6584 | 6971 |
|
6585 | 6972 |
#
|
|
6593 | 6980 |
$output = `$command 2>&1`;
|
6594 | 6981 |
}
|
6595 | 6982 |
$ret = $?;
|
6596 | |
} elsif (defined $outfh) {
|
|
6983 |
} elsif (defined $OUTFH) {
|
6597 | 6984 |
open(OUTPUT, "$command 2>&1 |");
|
6598 | 6985 |
while (<OUTPUT>) {
|
6599 | |
print $outfh $_;
|
|
6986 |
print $OUTFH $_;
|
6600 | 6987 |
}
|
6601 | 6988 |
close(OUTPUT);
|
6602 | 6989 |
$ret = $?;
|
|
6618 | 7005 |
else {
|
6619 | 7006 |
$ret = ($ret >> 8);
|
6620 | 7007 |
if (! $options{ignore_ret}) {
|
6621 | |
print STDERR "eg: failed ($ret)\n" if $debug;
|
|
7008 |
print STDERR "eg: failed ($ret)\n" if $DEBUG;
|
6622 | 7009 |
if ($ret >> 8 != 0) {
|
6623 | 7010 |
print STDERR "eg: command ($command) failed\n";
|
6624 | 7011 |
}
|
|
6634 | 7021 |
|
6635 | 7022 |
# executes a command, capturing its output (both STDOUT and STDERR),
|
6636 | 7023 |
# returning both the return value and the output
|
6637 | |
sub execute_captured {
|
|
7024 |
sub execute_captured ($@) {
|
6638 | 7025 |
my ($command, @options) = @_;
|
6639 | 7026 |
return _execute_impl($command, capture_output => 1, @options);
|
6640 | 7027 |
}
|
6641 | 7028 |
|
6642 | 7029 |
# executes a command, returning its chomped output
|
6643 | |
sub output {
|
|
7030 |
sub output ($@) {
|
6644 | 7031 |
my ($command, @options) = @_;
|
6645 | 7032 |
my ($ret, $output) = execute_captured($command, @options);
|
6646 | 7033 |
die "Failed executing '$command'!\n" if $ret != 0;
|
|
6649 | 7036 |
}
|
6650 | 7037 |
|
6651 | 7038 |
# executes a command (output not captured), returning its return value
|
6652 | |
sub execute {
|
|
7039 |
sub execute ($@) {
|
6653 | 7040 |
my ($command, @options) = @_;
|
6654 | 7041 |
return _execute_impl($command, @options);
|
6655 | 7042 |
}
|
|
6660 | 7047 |
package RepoUtil;
|
6661 | 7048 |
|
6662 | 7049 |
# 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",
|
6666 | 7053 |
ignore_ret => 1);
|
6667 | 7054 |
Util::pop_debug();
|
6668 | 7055 |
|
|
6672 | 7059 |
return $output;
|
6673 | 7060 |
}
|
6674 | 7061 |
|
6675 | |
sub git_dir {
|
|
7062 |
sub git_dir (%) {
|
6676 | 7063 |
my $options = {force => 0, @_}; # Hashref initialized as we're told
|
6677 | 7064 |
if (!$options->{force}) {
|
6678 | |
return $gitdir if ($gitdir);
|
|
7065 |
return $GITDIR if ($GITDIR);
|
6679 | 7066 |
}
|
6680 | 7067 |
|
6681 | 7068 |
Util::push_debug(new_value => 0);
|
6682 | 7069 |
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);
|
6684 | 7071 |
Util::pop_debug();
|
6685 | 7072 |
|
6686 | 7073 |
return undef if $ret != 0;
|
|
6688 | 7075 |
return $output;
|
6689 | 7076 |
}
|
6690 | 7077 |
|
6691 | |
sub get_dirs {
|
|
7078 |
sub get_dirs () {
|
6692 | 7079 |
my $options = {force => 0, @_}; # Hashref initialized as we're told
|
6693 | 7080 |
|
6694 | |
if ($curdir && !$options->{force}) {
|
6695 | |
return ($curdir, $topdir, $gitdir);
|
|
7081 |
if ($CURDIR && !$options->{force}) {
|
|
7082 |
return ($CURDIR, $TOPDIR, $GITDIR);
|
6696 | 7083 |
}
|
6697 | 7084 |
|
6698 | 7085 |
Util::push_debug(new_value => 0);
|
6699 | 7086 |
|
6700 | |
$curdir = main::getcwd();
|
|
7087 |
$CURDIR = main::getcwd();
|
6701 | 7088 |
|
6702 | 7089 |
# Get the toplevel repository directory
|
6703 | |
$topdir = $curdir;
|
|
7090 |
$TOPDIR = $CURDIR;
|
6704 | 7091 |
my ($ret, $rel_dir) =
|
6705 | |
ExecUtil::execute_captured("$git_cmd rev-parse --show-prefix",
|
|
7092 |
ExecUtil::execute_captured("$GIT_CMD rev-parse --show-prefix",
|
6706 | 7093 |
ignore_ret => 1);
|
6707 | 7094 |
chomp($rel_dir);
|
6708 | 7095 |
if ($ret != 0) {
|
6709 | |
$topdir = undef;
|
|
7096 |
$TOPDIR = undef;
|
6710 | 7097 |
} elsif ($rel_dir) {
|
6711 | 7098 |
$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});
|
6717 | 7104 |
|
6718 | 7105 |
Util::pop_debug();
|
6719 | 7106 |
|
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`;
|
6725 | 7112 |
return $?;
|
6726 | 7113 |
}
|
6727 | 7114 |
|
6728 | |
sub valid_ref {
|
|
7115 |
sub valid_ref ($) {
|
6729 | 7116 |
my ($ref) = @_;
|
6730 | 7117 |
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",
|
6732 | 7119 |
ignore_ret => 1);
|
6733 | 7120 |
return $ret == 0;
|
6734 | 7121 |
}
|
6735 | 7122 |
|
6736 | |
sub files_modified() {
|
6737 | |
my @output = `$git_cmd status -a`;
|
|
7123 |
sub files_modified () {
|
|
7124 |
my @output = `$GIT_CMD commit --dry-run -a`;
|
6738 | 7125 |
return $? == 0;
|
6739 | 7126 |
}
|
6740 | 7127 |
|
6741 | |
sub merge_branches {
|
|
7128 |
sub merge_branches () {
|
6742 | 7129 |
my $git_dir = RepoUtil::git_dir();
|
6743 | 7130 |
my $active_branch = RepoUtil::current_branch() || 'HEAD';
|
6744 | 7131 |
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`;
|
6746 | 7133 |
@merge_branches = map { /^[0-9a-f]* \((.*)\)$/ && $1 } @merge_branches;
|
6747 | 7134 |
my @all_merge_branches = ($active_branch, @merge_branches);
|
6748 | 7135 |
return @all_merge_branches;
|
6749 | 7136 |
}
|
6750 | 7137 |
|
6751 | |
sub get_special_state {
|
|
7138 |
sub get_special_state ($) {
|
6752 | 7139 |
my $git_dir = shift;
|
6753 | 7140 |
|
6754 | 7141 |
my $special_state;
|
|
6772 | 7159 |
return $special_state;
|
6773 | 7160 |
}
|
6774 | 7161 |
|
6775 | |
sub get_config {
|
|
7162 |
sub get_config ($) {
|
6776 | 7163 |
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",
|
6778 | 7165 |
ignore_ret => 1);
|
6779 | 7166 |
return undef if $ret != 0;
|
6780 | 7167 |
chomp($output);
|
6781 | 7168 |
return $output;
|
6782 | 7169 |
}
|
6783 | 7170 |
|
6784 | |
sub set_config {
|
|
7171 |
sub set_config ($$) {
|
6785 | 7172 |
my $key = shift;
|
6786 | 7173 |
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 ($) {
|
6791 | 7179 |
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 ($$) {
|
6796 | 7184 |
my $repository = shift;
|
6797 | 7185 |
my $check_type = shift;
|
6798 | 7186 |
|
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";
|
6801 | 7189 |
return;
|
6802 | 7190 |
}
|
6803 | 7191 |
|
|
6805 | 7193 |
# otherwise throw an error
|
6806 | 7194 |
my ($quoted_repo) = Util::quote_args("$repository");
|
6807 | 7195 |
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",
|
6809 | 7197 |
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;
|
6812 | 7200 |
my @remote_refs = split('\n', $output);
|
6813 | 7201 |
|
6814 | 7202 |
die "'$repository' has no branches to $check_type!\n" if @remote_refs == 0;
|
|
6838 | 7226 |
return $remote_branches[0];
|
6839 | 7227 |
}
|
6840 | 7228 |
|
6841 | |
sub get_default_push_pull_repository {
|
|
7229 |
sub get_default_push_pull_repository () {
|
6842 | 7230 |
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\.*`;
|
6852 | 7241 |
if (@output) {
|
6853 | 7242 |
return "origin";
|
6854 | 7243 |
} else {
|
|
6857 | 7246 |
repository. Please specify a repository or setup "origin" by running
|
6858 | 7247 |
eg remote add origin URL
|
6859 | 7248 |
EOF
|
6860 | |
}
|
6861 | |
}
|
6862 | |
|
6863 | |
sub print_new_unknowns {
|
|
7249 |
exit 1;
|
|
7250 |
}
|
|
7251 |
}
|
|
7252 |
|
|
7253 |
sub print_new_unknowns ($) {
|
6864 | 7254 |
my ($new_unknowns) = @_;
|
6865 | 7255 |
my $num = scalar(@$new_unknowns);
|
6866 | 7256 |
print STDERR "New unknown files";
|
|
6878 | 7268 |
}
|
6879 | 7269 |
|
6880 | 7270 |
# Error messages spewed by commit with non-clean working copies
|
6881 | |
sub commit_error_message_checks {
|
|
7271 |
sub commit_error_message_checks ($$$$) {
|
6882 | 7272 |
my ($commit_type, $check_for, $status, $new_unknown) = @_;
|
6883 | 7273 |
|
6884 | |
if ($status->{has_unmerged_changes}) {
|
|
7274 |
if ($check_for->{unmerged_changes} && $status->{has_unmerged_changes}) {
|
6885 | 7275 |
print STDERR <<EOF;
|
6886 | 7276 |
Aborting: You have unresolved conflicts from your merge (run 'eg status' to get
|
6887 | 7277 |
the list of files with conflicts). You must first resolve any conflicts and
|
|
6892 | 7282 |
}
|
6893 | 7283 |
|
6894 | 7284 |
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 |
}
|
6905 | 7291 |
}
|
6906 | 7292 |
elsif ($check_for->{unknown} && $check_for->{partially_staged} &&
|
6907 | 7293 |
$status->{has_new_unknown_files} &&
|
|
6935 | 7321 |
}
|
6936 | 7322 |
|
6937 | 7323 |
# Error messages spewed by push, publish for non-clean working copies
|
6938 | |
sub push_error_message_checks {
|
|
7324 |
sub push_error_message_checks ($$$$) {
|
6939 | 7325 |
my ($clean_check_type, $check_for, $status, $new_unknown) = @_;
|
6940 | 7326 |
|
6941 | |
if ($status->{has_unmerged_changes}) {
|
|
7327 |
if ($check_for->{unmerged_changes} && $status->{has_unmerged_changes}) {
|
6942 | 7328 |
print STDERR <<EOF;
|
6943 | 7329 |
Aborting: You have unresolved conflicts from your merge (run 'eg status' to get
|
6944 | 7330 |
the list of files with conflicts). You should first resolve any conflicts
|
|
6976 | 7362 |
}
|
6977 | 7363 |
}
|
6978 | 7364 |
|
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 || {};
|
6981 | 7369 |
my %status;
|
6982 | 7370 |
|
6983 | 7371 |
# Determine some useful directories
|
6984 | 7372 |
my ($cur_dir, $top_dir, $git_dir) = RepoUtil::get_dirs();
|
6985 | 7373 |
|
6986 | 7374 |
# Save debug mode, print out commands used up front
|
6987 | |
if ($debug) {
|
|
7375 |
if ($DEBUG) {
|
6988 | 7376 |
Util::push_debug(new_value => 0);
|
6989 | 7377 |
if ($clean_check_type) {
|
6990 | 7378 |
print " >>Commands to gather data for pre-$clean_check_type sanity checks:\n";
|
6991 | 7379 |
} else {
|
6992 | 7380 |
print " >>Commands to gather data for sanity checks:\n";
|
6993 | 7381 |
}
|
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";
|
6998 | 7386 |
} else {
|
6999 | 7387 |
Util::push_debug(new_value => 0);
|
7000 | 7388 |
}
|
|
7002 | 7390 |
#
|
7003 | 7391 |
# Determine which types of changes are present
|
7004 | 7392 |
#
|
7005 | |
my ($ret, $output) = ExecUtil::execute_captured("$eg_exec status",
|
|
7393 |
my ($ret, $output) = ExecUtil::execute_captured("$EG_EXEC status",
|
7006 | 7394 |
ignore_ret => 1);
|
7007 | |
my @unmerged_files = `$git_cmd ls-files --unmerged`;
|
|
7395 |
my @unmerged_files = `$GIT_CMD ls-files --unmerged`;
|
7008 | 7396 |
$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);
|
7010 | 7398 |
$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);
|
7013 | 7399 |
$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};
|
7014 | 7403 |
$status{output} = $output;
|
7015 | 7404 |
|
7016 | 7405 |
#
|
7017 | 7406 |
# Determine which unknown files are "newly created"
|
7018 | 7407 |
#
|
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)`;
|
7020 | 7409 |
chomp(@new_unknown);
|
7021 | 7410 |
if ($check_for->{unknown} && $status{has_new_unknown_files} &&
|
7022 | 7411 |
-f "$git_dir/info/ignored-unknown") {
|
|
7031 | 7420 |
Util::pop_debug();
|
7032 | 7421 |
|
7033 | 7422 |
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');
|
7035 | 7424 |
$status{has_no_branch} = $rc >> 8;
|
7036 | 7425 |
}
|
7037 | 7426 |
|
|
7054 | 7443 |
return \%status;
|
7055 | 7444 |
}
|
7056 | 7445 |
|
7057 | |
sub record_ignored_unknowns {
|
|
7446 |
sub record_ignored_unknowns () {
|
7058 | 7447 |
# Determine some useful directories
|
7059 | 7448 |
my ($cur_dir, $top_dir, $git_dir) = RepoUtil::get_dirs();
|
7060 | 7449 |
|
7061 | 7450 |
mkdir "$git_dir/info" unless -d "$git_dir/info";
|
7062 | 7451 |
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`;
|
7064 | 7453 |
foreach my $file (@unknown_files) {
|
7065 | 7454 |
print OUTPUT $file;
|
7066 | 7455 |
}
|
7067 | 7456 |
close(OUTPUT);
|
7068 | 7457 |
}
|
7069 | 7458 |
|
7070 | |
sub parse_args {
|
|
7459 |
sub parse_args ($@) {
|
|
7460 |
my $multi_args = shift;
|
7071 | 7461 |
my (@args) = @_;
|
7072 | 7462 |
|
7073 | 7463 |
Util::push_debug(new_value => 0);
|
|
7085 | 7475 |
|
7086 | 7476 |
if ($arg =~ /^-/) {
|
7087 | 7477 |
push(@opts, $arg);
|
|
7478 |
push(@opts, shift @args) if (grep {$arg eq $_} @$multi_args);
|
7088 | 7479 |
} else {
|
7089 | 7480 |
unshift(@args, $arg);
|
7090 | 7481 |
last;
|
|
7144 | 7535 |
return (\@opts, \@revs, \@files);
|
7145 | 7536 |
}
|
7146 | 7537 |
|
7147 | |
sub get_revert_info {
|
|
7538 |
sub get_revert_info ($@) {
|
7148 | 7539 |
my ($revision, @quoted_files) = @_;
|
7149 | 7540 |
|
7150 | 7541 |
my $marker = "";
|
|
7169 | 7560 |
my $ref2 = shift;
|
7170 | 7561 |
my (@files, @lines);
|
7171 | 7562 |
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`;
|
7173 | 7564 |
} else {
|
7174 | |
@lines = `$git_cmd diff-index --cached $ref1 $marker @quoted_files`;
|
|
7565 |
@lines = `$GIT_CMD diff-index --cached $ref1 $marker @quoted_files`;
|
7175 | 7566 |
}
|
7176 | 7567 |
foreach my $line (@lines) {
|
7177 | 7568 |
# Check for newly added files (not previously tracked but now staged)
|
|
7225 | 7616 |
package Util;
|
7226 | 7617 |
|
7227 | 7618 |
# Return items in @$lista but not in @$listb
|
7228 | |
sub difference {
|
|
7619 |
sub difference ($$) {
|
7229 | 7620 |
my ($lista, $listb) = @_;
|
7230 | 7621 |
my %count;
|
7231 | 7622 |
|
|
7236 | 7627 |
}
|
7237 | 7628 |
|
7238 | 7629 |
# Return items in both @$lista and in @$listb
|
7239 | |
sub intersect {
|
|
7630 |
sub intersect ($$) {
|
7240 | 7631 |
my ($lista, $listb) = @_;
|
7241 | 7632 |
my %original;
|
7242 | 7633 |
my @both = ();
|
|
7248 | 7639 |
}
|
7249 | 7640 |
|
7250 | 7641 |
# Return items in either @$lista or @$listb
|
7251 | |
sub union {
|
|
7642 |
sub union ($$) {
|
7252 | 7643 |
my ($lista, $listb) = @_;
|
7253 | 7644 |
my %either;
|
7254 | 7645 |
|
|
7259 | 7650 |
}
|
7260 | 7651 |
|
7261 | 7652 |
# Returns whether @$list contains $item
|
7262 | |
sub contains {
|
|
7653 |
sub contains ($$) {
|
7263 | 7654 |
my ($list, $item) = @_;
|
7264 | 7655 |
my $found = 0;
|
7265 | 7656 |
foreach my $elem (@$list) {
|
|
7272 | 7663 |
return $found;
|
7273 | 7664 |
}
|
7274 | 7665 |
|
7275 | |
sub uniquify_list {
|
|
7666 |
sub uniquify_list (@) {
|
7276 | 7667 |
my @list = @_;
|
7277 | 7668 |
my %unique;
|
7278 | 7669 |
@unique{@list} = @list;
|
7279 | 7670 |
return keys %unique;
|
7280 | 7671 |
}
|
7281 | 7672 |
|
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 (@) {
|
7283 | 7692 |
my @args = @_;
|
7284 | 7693 |
|
7285 | 7694 |
# Quote arguments with special characters so that when we
|
|
7289 | 7698 |
my @newargs;
|
7290 | 7699 |
foreach my $arg (@args) {
|
7291 | 7700 |
my $quotes_needed = 0;
|
7292 | |
if (!$arg || $arg =~ /[;'"<>()\[\]|`* \n\$\\]/) {
|
|
7701 |
if (!$arg || $arg =~ /[;'"<>()\[\]|`* \n\$\\~]/) {
|
7293 | 7702 |
$quotes_needed = 1;
|
7294 | 7703 |
}
|
7295 | 7704 |
|
|
7308 | 7717 |
# Have git's rev-parse command parse @args and decide which part is files,
|
7309 | 7718 |
# which is options, and which are revisions. Further, have git translate
|
7310 | 7719 |
# revisions into full 40-character hexadecimal commit ids.
|
7311 | |
sub git_rev_parse {
|
|
7720 |
sub git_rev_parse (@) {
|
7312 | 7721 |
my @args = @_;
|
7313 | 7722 |
|
7314 | 7723 |
Util::push_debug(new_value => 0);
|
7315 | 7724 |
|
7316 | 7725 |
my @quoted_args = Util::quote_args(@args);
|
7317 | 7726 |
my ($ret, $output) =
|
7318 | |
ExecUtil::execute_captured("$git_cmd rev-parse @quoted_args",
|
|
7727 |
ExecUtil::execute_captured("$GIT_CMD rev-parse @quoted_args",
|
7319 | 7728 |
ignore_ret => 1);
|
7320 | 7729 |
if ($ret != 0) {
|
7321 | 7730 |
$output =~ /^(fatal:.*)$/m && print STDERR "$1\n";
|
|
7323 | 7732 |
exit 1;
|
7324 | 7733 |
}
|
7325 | 7734 |
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`);
|
7327 | 7736 |
my @revs =
|
7328 | |
split('\n', `$git_cmd rev-parse --revs-only @quoted_args`);
|
|
7737 |
split('\n', `$GIT_CMD rev-parse --revs-only @quoted_args`);
|
7329 | 7738 |
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`);
|
7331 | 7740 |
|
7332 | 7741 |
# Translate sha1sums back to human specified version of revisions. Note that
|
7333 | 7742 |
# something like "REV1...REV2" is translated into "SHA1 SHA2 ^SHA3", so one
|
|
7354 | 7763 |
# reroot_paths__from_to_files("/tmp/junk", "/tmp", ('bar', '../foo'))
|
7355 | 7764 |
# would return
|
7356 | 7765 |
# ('junk/bar', 'foo')
|
7357 | |
sub reroot_paths__from_to_files {
|
|
7766 |
sub reroot_paths__from_to_files ($$@) {
|
7358 | 7767 |
my ($from, $to, @files) = @_;
|
7359 | 7768 |
$from =~ s#/*$#/#; # Make sure $from ends with exactly 1 slash
|
7360 | 7769 |
$to =~ s#/*$#/#; # Make sure $to ends with exactly 1 slash
|
|
7369 | 7778 |
# Find what $oldpath and $to have in common
|
7370 | 7779 |
my $common_leading_path = "";
|
7371 | 7780 |
my $combined = "$oldpath\n$to";
|
7372 | |
if ($combined =~ /^(.*).*\n\1.*$/) {
|
|
7781 |
if ($combined =~ m#^(.*/).*\n\1.*$#) {
|
7373 | 7782 |
$common_leading_path = $1;
|
7374 | 7783 |
}
|
7375 | 7784 |
|
|
7389 | 7798 |
|
7390 | 7799 |
{
|
7391 | 7800 |
my @debug_values;
|
7392 | |
sub push_debug {
|
|
7801 |
sub push_debug (@) {
|
7393 | 7802 |
my @opts = @_;
|
7394 | 7803 |
my %options = ( @opts );
|
7395 | 7804 |
die "Called without new_value!" if !defined($options{new_value});
|
7396 | 7805 |
|
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};
|
7400 | 7809 |
return $old_value;
|
7401 | 7810 |
}
|
7402 | 7811 |
|
7403 | |
sub pop_debug {
|
7404 | |
$debug = pop @debug_values;
|
|
7812 |
sub pop_debug () {
|
|
7813 |
$DEBUG = pop @debug_values;
|
7405 | 7814 |
}
|
7406 | 7815 |
}
|
7407 | 7816 |
|
|
7416 | 7825 |
|
7417 | 7826 |
package main;
|
7418 | 7827 |
|
7419 | |
sub launch {
|
|
7828 |
sub launch ($) {
|
7420 | 7829 |
my $job=shift;
|
|
7830 |
$job = $ALIAS{$job} || $job;
|
7421 | 7831 |
my $orig_job = $job;
|
7422 | 7832 |
$job =~ s/-/_/; # Packages must have underscores, commands often have dashes
|
7423 | 7833 |
|
|
7431 | 7841 |
if ($action->can("preprocess")) {
|
7432 | 7842 |
# Do not skip commands normally executed during the preprocess stage,
|
7433 | 7843 |
# 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;
|
7437 | 7847 |
$action->preprocess();
|
7438 | 7848 |
|
7439 | 7849 |
Util::pop_debug();
|
|
7441 | 7851 |
|
7442 | 7852 |
# run & postprocess
|
7443 | 7853 |
if (!$action->can("postprocess")) {
|
7444 | |
print ">>Stage: Run<<\n" if $debug;
|
|
7854 |
print ">>Stage: Run<<\n" if $DEBUG;
|
7445 | 7855 |
$ret = $action->run();
|
7446 | 7856 |
} else {
|
7447 | 7857 |
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;
|
7450 | 7860 |
$ret = $action->run();
|
7451 | |
print ">>Stage: Postprocess<<\n" if $debug;
|
|
7861 |
print ">>Stage: Postprocess<<\n" if $DEBUG;
|
7452 | 7862 |
$action->postprocess($output);
|
7453 | 7863 |
}
|
7454 | 7864 |
|
7455 | 7865 |
# wrapup
|
7456 | 7866 |
if ($action->can("wrapup")) {
|
7457 | |
print ">>Stage: Wrapup<<\n" if $debug;
|
|
7867 |
print ">>Stage: Wrapup<<\n" if $DEBUG;
|
7458 | 7868 |
$action->wrapup();
|
7459 | 7869 |
}
|
7460 | 7870 |
|
|
7871 |
$ret = 0 unless ($ret);
|
7461 | 7872 |
exit $ret;
|
7462 | 7873 |
}
|
7463 | 7874 |
|
7464 | |
sub version {
|
|
7875 |
sub version () {
|
7465 | 7876 |
my $version_obj = "version"->new();
|
7466 | 7877 |
$version_obj->run();
|
7467 | 7878 |
exit 0;
|
7468 | 7879 |
}
|
7469 | 7880 |
|
7470 | 7881 |
# User gave invalid input; print an error_message, then show command usage
|
7471 | |
sub help {
|
|
7882 |
sub help (;$) {
|
7472 | 7883 |
my $error_message = shift;
|
7473 | 7884 |
my %extra_args;
|
7474 | 7885 |
|
|
7487 | 7898 |
$help_obj->run();
|
7488 | 7899 |
}
|
7489 | 7900 |
|
7490 | |
sub main {
|
|
7901 |
sub main () {
|
7491 | 7902 |
#
|
7492 | 7903 |
# Get any global options
|
7493 | 7904 |
#
|
7494 | 7905 |
Getopt::Long::Configure("no_bundling", "no_permute",
|
7495 | 7906 |
"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]"); };
|
7498 | 7910 |
my $result=GetOptions(
|
7499 | |
"--debug" => sub { $debug = 1 },
|
|
7911 |
"--debug" => sub { $DEBUG = 1 },
|
7500 | 7912 |
"--help" => sub { help() },
|
7501 | |
"--translate" => sub { $debug = 2 },
|
|
7913 |
"--translate" => sub { $DEBUG = 2 },
|
7502 | 7914 |
"--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(@_) },
|
7506 | 7920 |
"bare" => sub { &$record_arg(@_) },
|
7507 | 7921 |
"git-dir=s" => sub { &$record_args(@_) },
|
7508 | 7922 |
"work-tree=s" => sub { &$record_args(@_) },
|
|
7923 |
"no-replace-objects" => sub { &$record_arg(@_) },
|
7509 | 7924 |
);
|
7510 | 7925 |
# 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 |
}
|
7512 | 7940 |
|
7513 | 7941 |
# Sanity check the arguments
|
|
7942 |
exit ExecUtil::execute($GIT_CMD) if $GIT_CMD =~ m#--exec-path$#;
|
7514 | 7943 |
die "eg: Error parsing arguments. (Try 'eg help')\n" if !$result;
|
7515 | 7944 |
die "eg: No subcommand specified. (Try 'eg help')\n" if @ARGV < 1;
|
7516 | 7945 |
die "eg: Invalid argument '$ARGV[0]'. (Try 'eg help')\n"
|
7517 | 7946 |
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 | |
}
|
7533 | 7947 |
|
7534 | 7948 |
#
|
7535 | 7949 |
# Now execute the action
|