1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-11-06 01:03:02 +01:00

Merge branch 'lt/rev-list' into next

* lt/rev-list:
  First cut at libifying revlist generation
  Merge branch 'maint'
  sample hooks template.
  Teach the "git" command to handle some commands internally
  Use setenv(), fix warnings
  contrib/git-svn: version 0.10.0
  contrib/git-svn: optimize sequential commits to svn
  contrib/git-svn: add show-ignore command
  annotate: Use qx{} for pipes on activestate.
  annotate: Convert all -| calls to use a helper open_pipe().
  annotate: Handle dirty state and arbitrary revisions.
  git-fetch: print the new and old ref when fast-forwarding
This commit is contained in:
Junio C Hamano 2006-02-26 15:33:49 -08:00
commit b5233a6195
19 changed files with 842 additions and 465 deletions

View file

@ -192,7 +192,7 @@ LIB_FILE=libgit.a
LIB_H = \
blob.h cache.h commit.h count-delta.h csum-file.h delta.h \
diff.h epoch.h object.h pack.h pkt-line.h quote.h refs.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h
DIFF_OBJS = \
diff.o diffcore-break.o diffcore-order.o diffcore-pathspec.o \
@ -205,7 +205,7 @@ LIB_OBJS = \
quote.o read-cache.o refs.o run-command.o \
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
fetch-clone.o \
fetch-clone.o revision.o \
$(DIFF_OBJS)
LIBS = $(LIB_FILE)

View file

@ -10,7 +10,7 @@
#define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
#endif
#if defined(DT_UNKNOWN) && !NO_D_TYPE_IN_DIRENT
#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
#define DTYPE(de) ((de)->d_type)
#else
#undef DT_UNKNOWN

View file

@ -8,7 +8,7 @@
$GIT_SVN_INDEX $GIT_SVN
$GIT_DIR $REV_DIR/;
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
$VERSION = '0.9.1';
$VERSION = '0.10.0';
$GIT_DIR = $ENV{GIT_DIR} || "$ENV{PWD}/.git";
$GIT_SVN = $ENV{GIT_SVN_ID} || 'git-svn';
$GIT_SVN_INDEX = "$GIT_DIR/$GIT_SVN/index";
@ -30,6 +30,7 @@
use File::Path qw/mkpath/;
use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
use File::Spec qw//;
use POSIX qw/strftime/;
my $sha1 = qr/[a-f\d]{40}/;
my $sha1_short = qr/[a-f\d]{6,40}/;
my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
@ -49,6 +50,7 @@
fetch => [ \&fetch, "Download new revisions from SVN" ],
init => [ \&init, "Initialize and fetch (import)"],
commit => [ \&commit, "Commit git revisions to SVN" ],
'show-ignore' => [ \&show_ignore, "Show svn:ignore listings" ],
rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)" ],
help => [ \&usage, "Show help" ],
);
@ -258,6 +260,30 @@ sub commit {
}
sub show_ignore {
require File::Find or die $!;
my $exclude_file = "$GIT_DIR/info/exclude";
open my $fh, '<', $exclude_file or croak $!;
chomp(my @excludes = (<$fh>));
close $fh or croak $!;
$SVN_URL ||= file_to_s("$GIT_DIR/$GIT_SVN/info/url");
chdir $SVN_WC or croak $!;
my %ign;
File::Find::find({wanted=>sub{if(lstat $_ && -d _ && -d "$_/.svn"){
s#^\./##;
@{$ign{$_}} = safe_qx(qw(svn propget svn:ignore),$_);
}}, no_chdir=>1},'.');
print "\n# /\n";
foreach (@{$ign{'.'}}) { print '/',$_ if /\S/ }
delete $ign{'.'};
foreach my $i (sort keys %ign) {
print "\n# ",$i,"\n";
foreach (@{$ign{$i}}) { print '/',$i,'/',$_ if /\S/ }
}
}
########################### utility functions #########################
sub setup_git_svn {
@ -566,6 +592,7 @@ sub handle_rmdir {
sub svn_commit_tree {
my ($svn_rev, $commit) = @_;
my $commit_msg = "$GIT_DIR/$GIT_SVN/.svn-commit.tmp.$$";
my %log_msg = ( msg => '' );
open my $msg, '>', $commit_msg or croak $!;
chomp(my $type = `git-cat-file -t $commit`);
@ -581,6 +608,7 @@ sub svn_commit_tree {
if (!$in_msg) {
$in_msg = 1 if (/^\s*$/);
} else {
$log_msg{msg} .= $_;
print $msg $_ or croak $!;
}
}
@ -600,9 +628,30 @@ sub svn_commit_tree {
join("\n",@ci_output),"\n";
my ($rev_committed) = ($committed =~ /^Committed revision (\d+)\./);
# resync immediately
my @svn_up = (qw(svn up), "-r$svn_rev");
my @svn_up = qw(svn up);
push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
if ($rev_committed == ($svn_rev + 1)) {
push @svn_up, "-r$rev_committed";
sys(@svn_up);
my $info = svn_info('.');
my $date = $info->{'Last Changed Date'} or die "Missing date\n";
if ($info->{'Last Changed Rev'} != $rev_committed) {
croak "$info->{'Last Changed Rev'} != $rev_committed\n"
}
my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~
/(\d{4})\-(\d\d)\-(\d\d)\s
(\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x)
or croak "Failed to parse date: $date\n";
$log_msg{date} = "$tz $Y-$m-$d $H:$M:$S";
$log_msg{author} = $info->{'Last Changed Author'};
$log_msg{revision} = $rev_committed;
$log_msg{msg} .= "\n";
my $parent = file_to_s("$REV_DIR/$svn_rev");
git_commit(\%log_msg, $parent, $commit);
return $rev_committed;
}
# resync immediately
push @svn_up, "-r$svn_rev";
sys(@svn_up);
return fetch("$rev_committed=$commit")->{revision};
}
@ -699,7 +748,7 @@ sub svn_info {
# only single-lines seem to exist in svn info output
while (<$info_fh>) {
chomp $_;
if (m#^([^:]+)\s*:\s*(\S*)$#) {
if (m#^([^:]+)\s*:\s*(\S.*)$#) {
$ret->{$1} = $2;
push @{$ret->{-order}}, $1;
}

View file

@ -61,6 +61,11 @@ rebuild::
the directory/repository you're tracking has moved or changed
protocols.
show-ignore::
Recursively finds and lists the svn:ignore property on
directories. The output is suitable for appending to
the $GIT_DIR/info/exclude file.
OPTIONS
-------
-r <ARG>::
@ -152,6 +157,8 @@ Tracking and contributing to an Subversion managed-project:
git commit git-svn-HEAD..my-branch
# Something is committed to SVN, pull the latest into your branch::
git-svn fetch && git pull . git-svn-HEAD
# Append svn:ignore settings to the default git exclude file:
git-svn show-ignore >> .git/info/exclude
DESIGN PHILOSOPHY
-----------------

View file

@ -15,6 +15,7 @@
#include "cache.h"
#include "commit.h"
#include "revision.h"
#include "epoch.h"
struct fraction {

View file

@ -11,7 +11,6 @@ typedef int (*emitter_func) (struct commit *);
int sort_list_in_merge_order(struct commit_list *list, emitter_func emitter);
/* Low bits are used by rev-list */
#define UNINTERESTING (1u<<10)
#define BOUNDARY (1u<<11)
#define VISITED (1u<<12)
#define DISCONTINUITY (1u<<13)

View file

@ -13,7 +13,7 @@ void git_set_exec_path(const char *exec_path)
/* Returns the highest-priority, location to look for git programs. */
const char *git_exec_path()
const char *git_exec_path(void)
{
const char *env;

View file

@ -82,7 +82,7 @@ static void mark_common(struct commit *commit,
Get the next rev to send, ignoring the common.
*/
static const unsigned char* get_rev()
static const unsigned char* get_rev(void)
{
struct commit *commit = NULL;

View file

@ -20,7 +20,7 @@ static int check_strict = 0;
static int keep_cache_objects = 0;
static unsigned char head_sha1[20];
#if NO_D_INO_IN_DIRENT
#ifdef NO_D_INO_IN_DIRENT
#define SORT_DIRENT 0
#define DIRENT_SORT_HINT(de) 0
#else

View file

@ -8,44 +8,62 @@
use warnings;
use strict;
use Getopt::Std;
use Getopt::Long;
use POSIX qw(strftime gmtime);
sub usage() {
print STDERR 'Usage: ${\basename $0} [-s] [-S revs-file] file
-l show long rev
-r follow renames
-S commit use revs from revs-file instead of calling git-rev-list
print STDERR 'Usage: ${\basename $0} [-s] [-S revs-file] file [ revision ]
-l, --long
Show long rev (Defaults off)
-r, --rename
Follow renames (Defaults on).
-S, --rev-file revs-file
use revs from revs-file instead of calling git-rev-list
-h, --help
This message.
';
exit(1);
}
our ($opt_h, $opt_l, $opt_r, $opt_S);
getopts("hlrS:") or usage();
$opt_h && usage();
our ($help, $longrev, $rename, $starting_rev, $rev_file) = (0, 0, 1);
my $rc = GetOptions( "long|l" => \$longrev,
"help|h" => \$help,
"rename|r" => \$rename,
"rev-file|S" => \$rev_file);
if (!$rc or $help) {
usage();
}
my $filename = shift @ARGV;
if (@ARGV) {
$starting_rev = shift @ARGV;
}
my @stack = (
{
'rev' => "HEAD",
'rev' => defined $starting_rev ? $starting_rev : "HEAD",
'filename' => $filename,
},
);
our (@lineoffsets, @pendinglineoffsets);
our @filelines = ();
open(F,"<",$filename)
or die "Failed to open filename: $!";
while(<F>) {
chomp;
push @filelines, $_;
if (defined $starting_rev) {
@filelines = git_cat_file($starting_rev, $filename);
} else {
open(F,"<",$filename)
or die "Failed to open filename: $!";
while(<F>) {
chomp;
push @filelines, $_;
}
close(F);
}
close(F);
our $leftover_lines = @filelines;
our %revs;
our @revqueue;
our $head;
@ -66,7 +84,7 @@ ()
next;
}
if (!$opt_r) {
if (!$rename) {
next;
}
@ -78,8 +96,18 @@ ()
}
}
push @revqueue, $head;
init_claim($head);
$revs{$head}{'lineoffsets'} = {};
init_claim( defined $starting_rev ? $starting_rev : 'dirty');
unless (defined $starting_rev) {
my $diff = open_pipe("git","diff","-R", "HEAD", "--",$filename)
or die "Failed to call git diff to check for dirty state: $!";
_git_diff_parse($diff, $head, "dirty", (
'author' => gitvar_name("GIT_AUTHOR_IDENT"),
'author_date' => sprintf("%s +0000",time()),
)
);
close($diff);
}
handle_rev();
@ -88,7 +116,7 @@ ()
my ($output, $rev, $committer, $date);
if (ref $l eq 'ARRAY') {
($output, $rev, $committer, $date) = @$l;
if (!$opt_l && length($rev) > 8) {
if (!$longrev && length($rev) > 8) {
$rev = substr($rev,0,8);
}
} else {
@ -102,7 +130,6 @@ ()
sub init_claim {
my ($rev) = @_;
my %revinfo = git_commit_info($rev);
for (my $i = 0; $i < @filelines; $i++) {
$filelines[$i] = [ $filelines[$i], '', '', '', 1];
# line,
@ -117,7 +144,9 @@ sub init_claim {
sub handle_rev {
my $i = 0;
my %seen;
while (my $rev = shift @revqueue) {
next if $seen{$rev}++;
my %revinfo = git_commit_info($rev);
@ -143,20 +172,21 @@ sub handle_rev {
sub git_rev_list {
my ($rev, $file) = @_;
if ($opt_S) {
open(P, '<' . $opt_S);
my $revlist;
if ($rev_file) {
open($revlist, '<' . $rev_file);
} else {
open(P,"-|","git-rev-list","--parents","--remove-empty",$rev,"--",$file)
$revlist = open_pipe("git-rev-list","--parents","--remove-empty",$rev,"--",$file)
or die "Failed to exec git-rev-list: $!";
}
my @revs;
while(my $line = <P>) {
while(my $line = <$revlist>) {
chomp $line;
my ($rev, @parents) = split /\s+/, $line;
push @revs, [ $rev, @parents ];
}
close(P);
close($revlist);
printf("0 revs found for rev %s (%s)\n", $rev, $file) if (@revs == 0);
return @revs;
@ -165,22 +195,22 @@ sub git_rev_list {
sub find_parent_renames {
my ($rev, $file) = @_;
open(P,"-|","git-diff-tree", "-M50", "-r","--name-status", "-z","$rev")
my $patch = open_pipe("git-diff-tree", "-M50", "-r","--name-status", "-z","$rev")
or die "Failed to exec git-diff: $!";
local $/ = "\0";
my %bound;
my $junk = <P>;
while (my $change = <P>) {
my $junk = <$patch>;
while (my $change = <$patch>) {
chomp $change;
my $filename = <P>;
my $filename = <$patch>;
chomp $filename;
if ($change =~ m/^[AMD]$/ ) {
next;
} elsif ($change =~ m/^R/ ) {
my $oldfilename = $filename;
$filename = <P>;
$filename = <$patch>;
chomp $filename;
if ( $file eq $filename ) {
my $parent = git_find_parent($rev, $oldfilename);
@ -189,7 +219,7 @@ sub find_parent_renames {
}
}
}
close(P);
close($patch);
return \%bound;
}
@ -198,14 +228,14 @@ sub find_parent_renames {
sub git_find_parent {
my ($rev, $filename) = @_;
open(REVPARENT,"-|","git-rev-list","--remove-empty", "--parents","--max-count=1","$rev","--",$filename)
my $revparent = open_pipe("git-rev-list","--remove-empty", "--parents","--max-count=1","$rev","--",$filename)
or die "Failed to open git-rev-list to find a single parent: $!";
my $parentline = <REVPARENT>;
my $parentline = <$revparent>;
chomp $parentline;
my ($revfound,$parent) = split m/\s+/, $parentline;
close(REVPARENT);
close($revparent);
return $parent;
}
@ -216,24 +246,31 @@ sub git_find_parent {
sub git_diff_parse {
my ($parent, $rev, %revinfo) = @_;
my ($ri, $pi) = (0,0);
open(DIFF,"-|","git-diff-tree","-M","-p",$rev,$parent,"--",
my $diff = open_pipe("git-diff-tree","-M","-p",$rev,$parent,"--",
$revs{$rev}{'filename'}, $revs{$parent}{'filename'})
or die "Failed to call git-diff for annotation: $!";
_git_diff_parse($diff, $parent, $rev, %revinfo);
close($diff);
}
sub _git_diff_parse {
my ($diff, $parent, $rev, %revinfo) = @_;
my ($ri, $pi) = (0,0);
my $slines = $revs{$rev}{'lines'};
my @plines;
my $gotheader = 0;
my ($remstart, $remlength, $addstart, $addlength);
my ($hunk_start, $hunk_index, $hunk_adds);
while(<DIFF>) {
my ($remstart);
my ($hunk_start, $hunk_index);
while(<$diff>) {
chomp;
if (m/^@@ -(\d+),(\d+) \+(\d+),(\d+)/) {
($remstart, $remlength, $addstart, $addlength) = ($1, $2, $3, $4);
$remstart = $1;
# Adjust for 0-based arrays
$remstart--;
$addstart--;
# Reinit hunk tracking.
$hunk_start = $remstart;
$hunk_index = 0;
@ -279,7 +316,6 @@ sub git_diff_parse {
}
$hunk_index++;
}
close(DIFF);
for (my $i = $ri; $i < @{$slines} ; $i++) {
push @plines, $slines->[$ri++];
}
@ -295,24 +331,43 @@ sub get_line {
}
sub git_cat_file {
my ($parent, $filename) = @_;
return () unless defined $parent && defined $filename;
my $blobline = `git-ls-tree $parent $filename`;
my ($mode, $type, $blob, $tfilename) = split(/\s+/, $blobline, 4);
my ($rev, $filename) = @_;
return () unless defined $rev && defined $filename;
open(C,"-|","git-cat-file", "blob", $blob)
or die "Failed to git-cat-file blob $blob (rev $parent, file $filename): " . $!;
my $blob = git_ls_tree($rev, $filename);
my $catfile = open_pipe("git","cat-file", "blob", $blob)
or die "Failed to git-cat-file blob $blob (rev $rev, file $filename): " . $!;
my @lines;
while(<C>) {
while(<$catfile>) {
chomp;
push @lines, $_;
}
close(C);
close($catfile);
return @lines;
}
sub git_ls_tree {
my ($rev, $filename) = @_;
my $lstree = open_pipe("git","ls-tree",$rev,$filename)
or die "Failed to call git ls-tree: $!";
my ($mode, $type, $blob, $tfilename);
while(<$lstree>) {
($mode, $type, $blob, $tfilename) = split(/\s+/, $_, 4);
last if ($tfilename eq $filename);
}
close($lstree);
return $blob if $filename eq $filename;
die "git-ls-tree failed to find blob for $filename";
}
sub claim_line {
my ($floffset, $rev, $lines, %revinfo) = @_;
@ -325,11 +380,11 @@ sub claim_line {
sub git_commit_info {
my ($rev) = @_;
open(COMMIT, "-|","git-cat-file", "commit", $rev)
my $commit = open_pipe("git-cat-file", "commit", $rev)
or die "Failed to call git-cat-file: $!";
my %info;
while(<COMMIT>) {
while(<$commit>) {
chomp;
last if (length $_ == 0);
@ -343,7 +398,7 @@ sub git_commit_info {
$info{'committer_date'} = $3;
}
}
close(COMMIT);
close($commit);
return %info;
}
@ -354,3 +409,80 @@ sub format_date {
return strftime("%Y-%m-%d %H:%M:%S " . $timezone, gmtime($timestamp));
}
# Copied from git-send-email.perl - We need a Git.pm module..
sub gitvar {
my ($var) = @_;
my $fh;
my $pid = open($fh, '-|');
die "$!" unless defined $pid;
if (!$pid) {
exec('git-var', $var) or die "$!";
}
my ($val) = <$fh>;
close $fh or die "$!";
chomp($val);
return $val;
}
sub gitvar_name {
my ($name) = @_;
my $val = gitvar($name);
my @field = split(/\s+/, $val);
return join(' ', @field[0...(@field-4)]);
}
sub open_pipe {
if ($^O eq '##INSERT_ACTIVESTATE_STRING_HERE##') {
return open_pipe_activestate(@_);
} else {
return open_pipe_normal(@_);
}
}
sub open_pipe_activestate {
tie *fh, "Git::ActiveStatePipe", @_;
return *fh;
}
sub open_pipe_normal {
my (@execlist) = @_;
my $pid = open my $kid, "-|";
defined $pid or die "Cannot fork: $!";
unless ($pid) {
exec @execlist;
die "Cannot exec @execlist: $!";
}
return $kid;
}
package Git::ActiveStatePipe;
use strict;
sub TIEHANDLE {
my ($class, @params) = @_;
my $cmdline = join " ", @params;
my @data = qx{$cmdline};
bless { i => 0, data => \@data }, $class;
}
sub READLINE {
my $self = shift;
if ($self->{i} >= scalar @{$self->{data}}) {
return undef;
}
return $self->{'data'}->[ $self->{i}++ ];
}
sub CLOSE {
my $self = shift;
delete $self->{data};
delete $self->{i};
}
sub EOF {
my $self = shift;
return ($self->{i} >= scalar @{$self->{data}});
}

View file

@ -164,6 +164,7 @@ fast_forward_local () {
;;
*,$local)
echo >&2 "* $1: fast forward to $3"
echo >&2 " from $local to $2"
git-update-ref "$1" "$2" "$local"
;;
*)

151
git.c
View file

@ -230,62 +230,141 @@ static void show_man_page(char *git_cmd)
execlp("man", "man", page, NULL);
}
static int cmd_version(int argc, char **argv, char **envp)
{
printf("git version %s\n", GIT_VERSION);
return 0;
}
static int cmd_help(int argc, char **argv, char **envp)
{
char *help_cmd = argv[1];
if (!help_cmd)
cmd_usage(git_exec_path(), NULL);
show_man_page(help_cmd);
return 0;
}
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
static void handle_internal_command(int argc, char **argv, char **envp)
{
const char *cmd = argv[0];
static struct cmd_struct {
const char *cmd;
int (*fn)(int, char **, char **);
} commands[] = {
{ "version", cmd_version },
{ "help", cmd_help },
};
int i;
for (i = 0; i < ARRAY_SIZE(commands); i++) {
struct cmd_struct *p = commands+i;
if (strcmp(p->cmd, cmd))
continue;
exit(p->fn(argc, argv, envp));
}
}
int main(int argc, char **argv, char **envp)
{
char *cmd = argv[0];
char *slash = strrchr(cmd, '/');
char git_command[PATH_MAX + 1];
char wd[PATH_MAX + 1];
int i, show_help = 0;
const char *exec_path;
const char *exec_path = NULL;
getcwd(wd, PATH_MAX);
/*
* Take the basename of argv[0] as the command
* name, and the dirname as the default exec_path
* if it's an absolute path and we don't have
* anything better.
*/
if (slash) {
*slash++ = 0;
if (*cmd == '/')
exec_path = cmd;
cmd = slash;
}
for (i = 1; i < argc; i++) {
char *arg = argv[i];
/*
* "git-xxxx" is the same as "git xxxx", but we obviously:
*
* - cannot take flags in between the "git" and the "xxxx".
* - cannot execute it externally (since it would just do
* the same thing over again)
*
* So we just directly call the internal command handler, and
* die if that one cannot handle it.
*/
if (!strncmp(cmd, "git-", 4)) {
cmd += 4;
argv[0] = cmd;
handle_internal_command(argc, argv, envp);
die("cannot handle %s internally", cmd);
}
if (!strcmp(arg, "help")) {
show_help = 1;
continue;
}
/* Default command: "help" */
cmd = "help";
if (strncmp(arg, "--", 2))
/* Look for flags.. */
while (argc > 1) {
cmd = *++argv;
argc--;
if (strncmp(cmd, "--", 2))
break;
arg += 2;
cmd += 2;
if (!strncmp(arg, "exec-path", 9)) {
arg += 9;
if (*arg == '=') {
exec_path = arg + 1;
git_set_exec_path(exec_path);
} else {
puts(git_exec_path());
exit(0);
/*
* For legacy reasons, the "version" and "help"
* commands can be written with "--" prepended
* to make them look like flags.
*/
if (!strcmp(cmd, "help"))
break;
if (!strcmp(cmd, "version"))
break;
/*
* Check remaining flags (which by now must be
* "--exec-path", but maybe we will accept
* other arguments some day)
*/
if (!strncmp(cmd, "exec-path", 9)) {
cmd += 9;
if (*cmd == '=') {
git_set_exec_path(cmd + 1);
continue;
}
}
else if (!strcmp(arg, "version")) {
printf("git version %s\n", GIT_VERSION);
puts(git_exec_path());
exit(0);
}
else if (!strcmp(arg, "help"))
show_help = 1;
else if (!show_help)
cmd_usage(NULL, NULL);
}
if (i >= argc || show_help) {
if (i >= argc)
cmd_usage(git_exec_path(), NULL);
show_man_page(argv[i]);
cmd_usage(NULL, NULL);
}
argv[0] = cmd;
/*
* We search for git commands in the following order:
* - git_exec_path()
* - the path of the "git" command if we could find it
* in $0
* - the regular PATH.
*/
if (exec_path)
prepend_to_path(exec_path, strlen(exec_path));
exec_path = git_exec_path();
prepend_to_path(exec_path, strlen(exec_path));
execv_git_cmd(argv + i);
/* See if it's an internal command */
handle_internal_command(argc, argv, envp);
/* .. then try the external ones */
execv_git_cmd(argv);
if (errno == ENOENT)
cmd_usage(exec_path, "'%s' is not a git-command", argv[i]);
cmd_usage(exec_path, "'%s' is not a git-command", cmd);
fprintf(stderr, "Failed to run command '%s': %s\n",
git_command, strerror(errno));

View file

@ -768,7 +768,7 @@ static int sha1_sort(const struct object_entry *a, const struct object_entry *b)
return memcmp(a->sha1, b->sha1, 20);
}
static struct object_entry **create_final_object_list()
static struct object_entry **create_final_object_list(void)
{
struct object_entry **list;
int i, j;

View file

@ -45,7 +45,7 @@ static inline void llist_item_put(struct llist_item *item)
free_nodes = item;
}
static inline struct llist_item *llist_item_get()
static inline struct llist_item *llist_item_get(void)
{
struct llist_item *new;
if ( free_nodes ) {
@ -275,7 +275,7 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
}
}
void pll_free(struct pll *l)
static void pll_free(struct pll *l)
{
struct pll *old;
struct pack_list *opl;

View file

@ -6,9 +6,10 @@
#include "blob.h"
#include "epoch.h"
#include "diff.h"
#include "revision.h"
/* bits #0 and #1 in revision.h */
#define SEEN (1u << 0)
#define INTERESTING (1u << 1)
#define COUNTED (1u << 2)
#define SHOWN (1u << 3)
#define TREECHANGE (1u << 4)
@ -38,60 +39,20 @@ static const char rev_list_usage[] =
" --bisect"
;
static int dense = 1;
struct rev_info revs;
static int unpacked = 0;
static int bisect_list = 0;
static int tag_objects = 0;
static int tree_objects = 0;
static int blob_objects = 0;
static int edge_hint = 0;
static int verbose_header = 0;
static int abbrev = DEFAULT_ABBREV;
static int show_parents = 0;
static int hdr_termination = 0;
static const char *commit_prefix = "";
static unsigned long max_age = -1;
static unsigned long min_age = -1;
static int max_count = -1;
static enum cmit_fmt commit_format = CMIT_FMT_RAW;
static int merge_order = 0;
static int show_breaks = 0;
static int stop_traversal = 0;
static int topo_order = 0;
static int lifo = 1;
static int no_merges = 0;
static const char **paths = NULL;
static int remove_empty_trees = 0;
struct name_path {
struct name_path *up;
int elem_len;
const char *elem;
};
static char *path_name(struct name_path *path, const char *name)
{
struct name_path *p;
char *n, *m;
int nlen = strlen(name);
int len = nlen + 1;
for (p = path; p; p = p->up) {
if (p->elem_len)
len += p->elem_len + 1;
}
n = xmalloc(len);
m = n + len - (nlen + 1);
strcpy(m, name);
for (p = path; p; p = p->up) {
if (p->elem_len) {
m -= p->elem_len + 1;
memcpy(m, p->elem, p->elem_len);
m[p->elem_len] = '/';
}
}
return n;
}
static void show_commit(struct commit *commit)
{
@ -168,15 +129,15 @@ static int filter_commit(struct commit * commit)
return STOP;
if (commit->object.flags & (UNINTERESTING|SHOWN))
return CONTINUE;
if (min_age != -1 && (commit->date > min_age))
if (revs.min_age != -1 && (commit->date > revs.min_age))
return CONTINUE;
if (max_age != -1 && (commit->date < max_age)) {
if (revs.max_age != -1 && (commit->date < revs.max_age)) {
stop_traversal=1;
return CONTINUE;
}
if (no_merges && (commit->parents && commit->parents->next))
return CONTINUE;
if (paths && dense) {
if (revs.paths && revs.dense) {
if (!(commit->object.flags & TREECHANGE))
return CONTINUE;
rewrite_parents(commit);
@ -196,7 +157,7 @@ static int process_commit(struct commit * commit)
return CONTINUE;
}
if (max_count != -1 && !max_count--)
if (revs.max_count != -1 && !revs.max_count--)
return STOP;
show_commit(commit);
@ -204,19 +165,6 @@ static int process_commit(struct commit * commit)
return CONTINUE;
}
static struct object_list **add_object(struct object *obj,
struct object_list **p,
struct name_path *path,
const char *name)
{
struct object_list *entry = xmalloc(sizeof(*entry));
entry->item = obj;
entry->next = *p;
entry->name = path_name(path, name);
*p = entry;
return &entry->next;
}
static struct object_list **process_blob(struct blob *blob,
struct object_list **p,
struct name_path *path,
@ -224,7 +172,7 @@ static struct object_list **process_blob(struct blob *blob,
{
struct object *obj = &blob->object;
if (!blob_objects)
if (!revs.blob_objects)
return p;
if (obj->flags & (UNINTERESTING | SEEN))
return p;
@ -241,7 +189,7 @@ static struct object_list **process_tree(struct tree *tree,
struct tree_entry_list *entry;
struct name_path me;
if (!tree_objects)
if (!revs.tree_objects)
return p;
if (obj->flags & (UNINTERESTING | SEEN))
return p;
@ -314,75 +262,6 @@ static void show_commit_list(struct commit_list *list)
}
}
static void mark_blob_uninteresting(struct blob *blob)
{
if (!blob_objects)
return;
if (blob->object.flags & UNINTERESTING)
return;
blob->object.flags |= UNINTERESTING;
}
static void mark_tree_uninteresting(struct tree *tree)
{
struct object *obj = &tree->object;
struct tree_entry_list *entry;
if (!tree_objects)
return;
if (obj->flags & UNINTERESTING)
return;
obj->flags |= UNINTERESTING;
if (!has_sha1_file(obj->sha1))
return;
if (parse_tree(tree) < 0)
die("bad tree %s", sha1_to_hex(obj->sha1));
entry = tree->entries;
tree->entries = NULL;
while (entry) {
struct tree_entry_list *next = entry->next;
if (entry->directory)
mark_tree_uninteresting(entry->item.tree);
else
mark_blob_uninteresting(entry->item.blob);
free(entry);
entry = next;
}
}
static void mark_parents_uninteresting(struct commit *commit)
{
struct commit_list *parents = commit->parents;
while (parents) {
struct commit *commit = parents->item;
commit->object.flags |= UNINTERESTING;
/*
* Normally we haven't parsed the parent
* yet, so we won't have a parent of a parent
* here. However, it may turn out that we've
* reached this commit some other way (where it
* wasn't uninteresting), in which case we need
* to mark its parents recursively too..
*/
if (commit->parents)
mark_parents_uninteresting(commit);
/*
* A missing commit is ok iff its parent is marked
* uninteresting.
*
* We just mark such a thing parsed, so that when
* it is popped next time around, we won't be trying
* to parse it and get an error.
*/
if (!has_sha1_file(commit->object.sha1))
commit->object.parsed = 1;
parents = parents->next;
}
}
static int everybody_uninteresting(struct commit_list *orig)
{
struct commit_list *list = orig;
@ -413,7 +292,7 @@ static int count_distance(struct commit_list *entry)
if (commit->object.flags & (UNINTERESTING | COUNTED))
break;
if (!paths || (commit->object.flags & TREECHANGE))
if (!revs.paths || (commit->object.flags & TREECHANGE))
nr++;
commit->object.flags |= COUNTED;
p = commit->parents;
@ -447,7 +326,7 @@ static struct commit_list *find_bisection(struct commit_list *list)
nr = 0;
p = list;
while (p) {
if (!paths || (p->item->object.flags & TREECHANGE))
if (!revs.paths || (p->item->object.flags & TREECHANGE))
nr++;
p = p->next;
}
@ -457,7 +336,7 @@ static struct commit_list *find_bisection(struct commit_list *list)
for (p = list; p; p = p->next) {
int distance;
if (paths && !(p->item->object.flags & TREECHANGE))
if (revs.paths && !(p->item->object.flags & TREECHANGE))
continue;
distance = count_distance(p);
@ -483,7 +362,7 @@ static void mark_edge_parents_uninteresting(struct commit *commit)
if (!(parent->object.flags & UNINTERESTING))
continue;
mark_tree_uninteresting(parent->tree);
if (edge_hint && !(parent->object.flags & SHOWN)) {
if (revs.edge_hint && !(parent->object.flags & SHOWN)) {
parent->object.flags |= SHOWN;
printf("-%s\n", sha1_to_hex(parent->object.sha1));
}
@ -613,7 +492,7 @@ static void try_to_simplify_commit(struct commit *commit)
return;
case TREE_NEW:
if (remove_empty_trees && same_tree_as_empty(p->tree)) {
if (revs.remove_empty_trees && same_tree_as_empty(p->tree)) {
*pp = parent->next;
continue;
}
@ -664,7 +543,7 @@ static void add_parents_to_list(struct commit *commit, struct commit_list **list
* simplify the commit history and find the parent
* that has no differences in the path set if one exists.
*/
if (paths)
if (revs.paths)
try_to_simplify_commit(commit);
parent = commit->parents;
@ -693,7 +572,7 @@ static struct commit_list *limit_list(struct commit_list *list)
list = list->next;
free(entry);
if (max_age != -1 && (commit->date < max_age))
if (revs.max_age != -1 && (commit->date < revs.max_age))
obj->flags |= UNINTERESTING;
if (unpacked && has_sha1_pack(obj->sha1))
obj->flags |= UNINTERESTING;
@ -704,155 +583,40 @@ static struct commit_list *limit_list(struct commit_list *list)
break;
continue;
}
if (min_age != -1 && (commit->date > min_age))
if (revs.min_age != -1 && (commit->date > revs.min_age))
continue;
p = &commit_list_insert(commit, p)->next;
}
if (tree_objects)
if (revs.tree_objects)
mark_edges_uninteresting(newlist);
if (bisect_list)
newlist = find_bisection(newlist);
return newlist;
}
static void add_pending_object(struct object *obj, const char *name)
{
add_object(obj, &pending_objects, NULL, name);
}
static struct commit *get_commit_reference(const char *name, const unsigned char *sha1, unsigned int flags)
{
struct object *object;
object = parse_object(sha1);
if (!object)
die("bad object %s", name);
/*
* Tag object? Look what it points to..
*/
while (object->type == tag_type) {
struct tag *tag = (struct tag *) object;
object->flags |= flags;
if (tag_objects && !(object->flags & UNINTERESTING))
add_pending_object(object, tag->tag);
object = parse_object(tag->tagged->sha1);
if (!object)
die("bad object %s", sha1_to_hex(tag->tagged->sha1));
}
/*
* Commit object? Just return it, we'll do all the complex
* reachability crud.
*/
if (object->type == commit_type) {
struct commit *commit = (struct commit *)object;
object->flags |= flags;
if (parse_commit(commit) < 0)
die("unable to parse commit %s", name);
if (flags & UNINTERESTING)
mark_parents_uninteresting(commit);
return commit;
}
/*
* Tree object? Either mark it uniniteresting, or add it
* to the list of objects to look at later..
*/
if (object->type == tree_type) {
struct tree *tree = (struct tree *)object;
if (!tree_objects)
return NULL;
if (flags & UNINTERESTING) {
mark_tree_uninteresting(tree);
return NULL;
}
add_pending_object(object, "");
return NULL;
}
/*
* Blob object? You know the drill by now..
*/
if (object->type == blob_type) {
struct blob *blob = (struct blob *)object;
if (!blob_objects)
return NULL;
if (flags & UNINTERESTING) {
mark_blob_uninteresting(blob);
return NULL;
}
add_pending_object(object, "");
return NULL;
}
die("%s is unknown object", name);
}
static void handle_one_commit(struct commit *com, struct commit_list **lst)
{
if (!com || com->object.flags & SEEN)
return;
com->object.flags |= SEEN;
commit_list_insert(com, lst);
}
/* for_each_ref() callback does not allow user data -- Yuck. */
static struct commit_list **global_lst;
static int include_one_commit(const char *path, const unsigned char *sha1)
{
struct commit *com = get_commit_reference(path, sha1, 0);
handle_one_commit(com, global_lst);
return 0;
}
static void handle_all(struct commit_list **lst)
{
global_lst = lst;
for_each_ref(include_one_commit);
global_lst = NULL;
}
int main(int argc, const char **argv)
{
const char *prefix = setup_git_directory();
struct commit_list *list = NULL;
struct commit_list *list;
int i, limited = 0;
argc = setup_revisions(argc, argv, &revs);
for (i = 1 ; i < argc; i++) {
int flags;
const char *arg = argv[i];
char *dotdot;
struct commit *commit;
unsigned char sha1[20];
/* accept -<digit>, like traditilnal "head" */
if ((*arg == '-') && isdigit(arg[1])) {
max_count = atoi(arg + 1);
revs.max_count = atoi(arg + 1);
continue;
}
if (!strcmp(arg, "-n")) {
if (++i >= argc)
die("-n requires an argument");
max_count = atoi(argv[i]);
revs.max_count = atoi(argv[i]);
continue;
}
if (!strncmp(arg,"-n",2)) {
max_count = atoi(arg + 2);
continue;
}
if (!strncmp(arg, "--max-count=", 12)) {
max_count = atoi(arg + 12);
continue;
}
if (!strncmp(arg, "--max-age=", 10)) {
max_age = atoi(arg + 10);
limited = 1;
continue;
}
if (!strncmp(arg, "--min-age=", 10)) {
min_age = atoi(arg + 10);
limited = 1;
revs.max_count = atoi(arg + 2);
continue;
}
if (!strcmp(arg, "--header")) {
@ -893,23 +657,6 @@ int main(int argc, const char **argv)
bisect_list = 1;
continue;
}
if (!strcmp(arg, "--all")) {
handle_all(&list);
continue;
}
if (!strcmp(arg, "--objects")) {
tag_objects = 1;
tree_objects = 1;
blob_objects = 1;
continue;
}
if (!strcmp(arg, "--objects-edge")) {
tag_objects = 1;
tree_objects = 1;
blob_objects = 1;
edge_hint = 1;
continue;
}
if (!strcmp(arg, "--unpacked")) {
unpacked = 1;
limited = 1;
@ -923,100 +670,42 @@ int main(int argc, const char **argv)
show_breaks = 1;
continue;
}
if (!strcmp(arg, "--topo-order")) {
topo_order = 1;
lifo = 1;
limited = 1;
continue;
}
if (!strcmp(arg, "--date-order")) {
topo_order = 1;
lifo = 0;
limited = 1;
continue;
}
if (!strcmp(arg, "--dense")) {
dense = 1;
continue;
}
if (!strcmp(arg, "--sparse")) {
dense = 0;
continue;
}
if (!strcmp(arg, "--remove-empty")) {
remove_empty_trees = 1;
continue;
}
if (!strcmp(arg, "--")) {
i++;
break;
}
if (show_breaks && !merge_order)
usage(rev_list_usage);
flags = 0;
dotdot = strstr(arg, "..");
if (dotdot) {
unsigned char from_sha1[20];
char *next = dotdot + 2;
*dotdot = 0;
if (!*next)
next = "HEAD";
if (!get_sha1(arg, from_sha1) && !get_sha1(next, sha1)) {
struct commit *exclude;
struct commit *include;
exclude = get_commit_reference(arg, from_sha1, UNINTERESTING);
include = get_commit_reference(next, sha1, 0);
if (!exclude || !include)
die("Invalid revision range %s..%s", arg, next);
limited = 1;
handle_one_commit(exclude, &list);
handle_one_commit(include, &list);
continue;
}
*dotdot = '.';
}
if (*arg == '^') {
flags = UNINTERESTING;
arg++;
limited = 1;
}
if (get_sha1(arg, sha1) < 0) {
struct stat st;
if (lstat(arg, &st) < 0)
die("'%s': %s", arg, strerror(errno));
break;
}
commit = get_commit_reference(arg, sha1, flags);
handle_one_commit(commit, &list);
}
if (!list &&
(!(tag_objects||tree_objects||blob_objects) && !pending_objects))
usage(rev_list_usage);
paths = get_pathspec(prefix, argv + i);
if (paths) {
limited = 1;
diff_tree_setup_paths(paths);
}
list = revs.commits;
if (list && list->next)
limited = 1;
if (revs.topo_order)
limited = 1;
if (!list &&
(!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects))
usage(rev_list_usage);
if (revs.paths) {
limited = 1;
diff_tree_setup_paths(revs.paths);
}
if (revs.max_age || revs.min_age)
limited = 1;
save_commit_buffer = verbose_header;
track_object_refs = 0;
if (!merge_order) {
sort_by_date(&list);
if (list && !limited && max_count == 1 &&
!tag_objects && !tree_objects && !blob_objects) {
if (list && !limited && revs.max_count == 1 &&
!revs.tag_objects && !revs.tree_objects && !revs.blob_objects) {
show_commit(list->item);
return 0;
}
if (limited)
list = limit_list(list);
if (topo_order)
sort_in_topological_order(&list, lifo);
if (revs.topo_order)
sort_in_topological_order(&list, revs.lifo);
show_commit_list(list);
} else {
#ifndef NO_OPENSSL

370
revision.c Normal file
View file

@ -0,0 +1,370 @@
#include "cache.h"
#include "tag.h"
#include "blob.h"
#include "tree.h"
#include "commit.h"
#include "refs.h"
#include "revision.h"
static char *path_name(struct name_path *path, const char *name)
{
struct name_path *p;
char *n, *m;
int nlen = strlen(name);
int len = nlen + 1;
for (p = path; p; p = p->up) {
if (p->elem_len)
len += p->elem_len + 1;
}
n = xmalloc(len);
m = n + len - (nlen + 1);
strcpy(m, name);
for (p = path; p; p = p->up) {
if (p->elem_len) {
m -= p->elem_len + 1;
memcpy(m, p->elem, p->elem_len);
m[p->elem_len] = '/';
}
}
return n;
}
struct object_list **add_object(struct object *obj,
struct object_list **p,
struct name_path *path,
const char *name)
{
struct object_list *entry = xmalloc(sizeof(*entry));
entry->item = obj;
entry->next = *p;
entry->name = path_name(path, name);
*p = entry;
return &entry->next;
}
static void mark_blob_uninteresting(struct blob *blob)
{
if (blob->object.flags & UNINTERESTING)
return;
blob->object.flags |= UNINTERESTING;
}
void mark_tree_uninteresting(struct tree *tree)
{
struct object *obj = &tree->object;
struct tree_entry_list *entry;
if (obj->flags & UNINTERESTING)
return;
obj->flags |= UNINTERESTING;
if (!has_sha1_file(obj->sha1))
return;
if (parse_tree(tree) < 0)
die("bad tree %s", sha1_to_hex(obj->sha1));
entry = tree->entries;
tree->entries = NULL;
while (entry) {
struct tree_entry_list *next = entry->next;
if (entry->directory)
mark_tree_uninteresting(entry->item.tree);
else
mark_blob_uninteresting(entry->item.blob);
free(entry);
entry = next;
}
}
void mark_parents_uninteresting(struct commit *commit)
{
struct commit_list *parents = commit->parents;
while (parents) {
struct commit *commit = parents->item;
commit->object.flags |= UNINTERESTING;
/*
* Normally we haven't parsed the parent
* yet, so we won't have a parent of a parent
* here. However, it may turn out that we've
* reached this commit some other way (where it
* wasn't uninteresting), in which case we need
* to mark its parents recursively too..
*/
if (commit->parents)
mark_parents_uninteresting(commit);
/*
* A missing commit is ok iff its parent is marked
* uninteresting.
*
* We just mark such a thing parsed, so that when
* it is popped next time around, we won't be trying
* to parse it and get an error.
*/
if (!has_sha1_file(commit->object.sha1))
commit->object.parsed = 1;
parents = parents->next;
}
}
static void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
{
add_object(obj, &revs->pending_objects, NULL, name);
}
static struct commit *get_commit_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
{
struct object *object;
object = parse_object(sha1);
if (!object)
die("bad object %s", name);
/*
* Tag object? Look what it points to..
*/
while (object->type == tag_type) {
struct tag *tag = (struct tag *) object;
object->flags |= flags;
if (revs->tag_objects && !(object->flags & UNINTERESTING))
add_pending_object(revs, object, tag->tag);
object = parse_object(tag->tagged->sha1);
if (!object)
die("bad object %s", sha1_to_hex(tag->tagged->sha1));
}
/*
* Commit object? Just return it, we'll do all the complex
* reachability crud.
*/
if (object->type == commit_type) {
struct commit *commit = (struct commit *)object;
object->flags |= flags;
if (parse_commit(commit) < 0)
die("unable to parse commit %s", name);
if (flags & UNINTERESTING)
mark_parents_uninteresting(commit);
return commit;
}
/*
* Tree object? Either mark it uniniteresting, or add it
* to the list of objects to look at later..
*/
if (object->type == tree_type) {
struct tree *tree = (struct tree *)object;
if (!revs->tree_objects)
return NULL;
if (flags & UNINTERESTING) {
mark_tree_uninteresting(tree);
return NULL;
}
add_pending_object(revs, object, "");
return NULL;
}
/*
* Blob object? You know the drill by now..
*/
if (object->type == blob_type) {
struct blob *blob = (struct blob *)object;
if (!revs->blob_objects)
return NULL;
if (flags & UNINTERESTING) {
mark_blob_uninteresting(blob);
return NULL;
}
add_pending_object(revs, object, "");
return NULL;
}
die("%s is unknown object", name);
}
static void add_one_commit(struct commit *commit, struct rev_info *revs)
{
if (!commit || (commit->object.flags & SEEN))
return;
commit->object.flags |= SEEN;
commit_list_insert(commit, &revs->commits);
}
static int all_flags;
static struct rev_info *all_revs;
static int handle_one_ref(const char *path, const unsigned char *sha1)
{
struct commit *commit = get_commit_reference(all_revs, path, sha1, all_flags);
add_one_commit(commit, all_revs);
return 0;
}
static void handle_all(struct rev_info *revs, unsigned flags)
{
all_revs = revs;
all_flags = flags;
for_each_ref(handle_one_ref);
}
/*
* Parse revision information, filling in the "rev_info" structure,
* and removing the used arguments from the argument list.
*
* Returns the number of arguments left ("new argc").
*/
int setup_revisions(int argc, const char **argv, struct rev_info *revs)
{
int i, flags, seen_dashdash;
const char *def = NULL;
const char **unrecognized = argv+1;
int left = 1;
memset(revs, 0, sizeof(*revs));
revs->lifo = 1;
revs->dense = 1;
revs->prefix = setup_git_directory();
revs->max_age = -1;
revs->min_age = -1;
revs->max_count = -1;
/* First, search for "--" */
seen_dashdash = 0;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (strcmp(arg, "--"))
continue;
argv[i] = NULL;
argc = i;
revs->paths = get_pathspec(revs->prefix, argv + i + 1);
seen_dashdash = 1;
break;
}
flags = 0;
for (i = 1; i < argc; i++) {
struct commit *commit;
const char *arg = argv[i];
unsigned char sha1[20];
char *dotdot;
int local_flags;
if (*arg == '-') {
if (!strncmp(arg, "--max-count=", 12)) {
revs->max_count = atoi(arg + 12);
continue;
}
if (!strncmp(arg, "--max-age=", 10)) {
revs->max_age = atoi(arg + 10);
continue;
}
if (!strncmp(arg, "--min-age=", 10)) {
revs->min_age = atoi(arg + 10);
continue;
}
if (!strcmp(arg, "--all")) {
handle_all(revs, flags);
continue;
}
if (!strcmp(arg, "--not")) {
flags ^= UNINTERESTING;
continue;
}
if (!strcmp(arg, "--default")) {
if (++i >= argc)
die("bad --default argument");
def = argv[i];
continue;
}
if (!strcmp(arg, "--topo-order")) {
revs->topo_order = 1;
continue;
}
if (!strcmp(arg, "--date-order")) {
revs->lifo = 0;
revs->topo_order = 1;
continue;
}
if (!strcmp(arg, "--dense")) {
revs->dense = 1;
continue;
}
if (!strcmp(arg, "--sparse")) {
revs->dense = 0;
continue;
}
if (!strcmp(arg, "--remove-empty")) {
revs->remove_empty_trees = 1;
continue;
}
if (!strcmp(arg, "--objects")) {
revs->tag_objects = 1;
revs->tree_objects = 1;
revs->blob_objects = 1;
continue;
}
if (!strcmp(arg, "--objects-edge")) {
revs->tag_objects = 1;
revs->tree_objects = 1;
revs->blob_objects = 1;
revs->edge_hint = 1;
continue;
}
*unrecognized++ = arg;
left++;
continue;
}
dotdot = strstr(arg, "..");
if (dotdot) {
unsigned char from_sha1[20];
char *next = dotdot + 2;
*dotdot = 0;
if (!*next)
next = "HEAD";
if (!get_sha1(arg, from_sha1) && !get_sha1(next, sha1)) {
struct commit *exclude;
struct commit *include;
exclude = get_commit_reference(revs, arg, from_sha1, flags ^ UNINTERESTING);
include = get_commit_reference(revs, next, sha1, flags);
if (!exclude || !include)
die("Invalid revision range %s..%s", arg, next);
add_one_commit(exclude, revs);
add_one_commit(include, revs);
continue;
}
*dotdot = '.';
}
local_flags = 0;
if (*arg == '^') {
local_flags = UNINTERESTING;
arg++;
}
if (get_sha1(arg, sha1) < 0) {
struct stat st;
int j;
if (seen_dashdash || local_flags)
die("bad revision '%s'", arg);
/* If we didn't have a "--", all filenames must exist */
for (j = i; j < argc; j++) {
if (lstat(argv[j], &st) < 0)
die("'%s': %s", arg, strerror(errno));
}
revs->paths = get_pathspec(revs->prefix, argv + i);
break;
}
commit = get_commit_reference(revs, arg, sha1, flags ^ local_flags);
add_one_commit(commit, revs);
}
if (def && !revs->commits) {
unsigned char sha1[20];
struct commit *commit;
if (get_sha1(def, sha1) < 0)
die("bad default revision '%s'", def);
commit = get_commit_reference(revs, def, sha1, 0);
add_one_commit(commit, revs);
}
*unrecognized = NULL;
return left;
}

48
revision.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef REVISION_H
#define REVISION_H
#define SEEN (1u<<0)
#define UNINTERESTING (1u<<1)
struct rev_info {
/* Starting list */
struct commit_list *commits;
struct object_list *pending_objects;
/* Basic information */
const char *prefix;
const char **paths;
/* Traversal flags */
unsigned int dense:1,
remove_empty_trees:1,
lifo:1,
topo_order:1,
tag_objects:1,
tree_objects:1,
blob_objects:1,
edge_hint:1;
/* special limits */
int max_count;
unsigned long max_age;
unsigned long min_age;
};
/* revision.c */
extern int setup_revisions(int argc, const char **argv, struct rev_info *revs);
extern void mark_parents_uninteresting(struct commit *commit);
extern void mark_tree_uninteresting(struct tree *tree);
struct name_path {
struct name_path *up;
int elem_len;
const char *elem;
};
extern struct object_list **add_object(struct object *obj,
struct object_list **p,
struct name_path *path,
const char *name);
#endif

View file

@ -9,6 +9,7 @@
#
# To enable this hook, make this file executable.
. git-sh-setup
test -x "$GIT_DIR/hooks/commit-msg" &&
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
:

View file

@ -8,6 +8,7 @@
#
# To enable this hook, make this file executable.
. git-sh-setup
test -x "$GIT_DIR/hooks/pre-commit" &&
exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
: