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:
commit
b5233a6195
19 changed files with 842 additions and 465 deletions
4
Makefile
4
Makefile
|
@ -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)
|
||||
|
|
2
cache.h
2
cache.h
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
-----------------
|
||||
|
|
1
epoch.c
1
epoch.c
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "revision.h"
|
||||
#include "epoch.h"
|
||||
|
||||
struct fraction {
|
||||
|
|
1
epoch.h
1
epoch.h
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
if (defined $starting_rev) {
|
||||
@filelines = git_cat_file($starting_rev, $filename);
|
||||
} else {
|
||||
open(F,"<",$filename)
|
||||
or die "Failed to open filename: $!";
|
||||
|
||||
while(<F>) {
|
||||
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}});
|
||||
}
|
||||
|
|
|
@ -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
151
git.c
|
@ -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);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
char *arg = argv[i];
|
||||
|
||||
if (!strcmp(arg, "help")) {
|
||||
show_help = 1;
|
||||
continue;
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
if (strncmp(arg, "--", 2))
|
||||
/*
|
||||
* "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);
|
||||
}
|
||||
|
||||
/* Default command: "help" */
|
||||
cmd = "help";
|
||||
|
||||
/* 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 {
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
puts(git_exec_path());
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(arg, "version")) {
|
||||
printf("git version %s\n", GIT_VERSION);
|
||||
exit(0);
|
||||
}
|
||||
else if (!strcmp(arg, "help"))
|
||||
show_help = 1;
|
||||
else if (!show_help)
|
||||
cmd_usage(NULL, NULL);
|
||||
}
|
||||
argv[0] = cmd;
|
||||
|
||||
if (i >= argc || show_help) {
|
||||
if (i >= argc)
|
||||
cmd_usage(git_exec_path(), NULL);
|
||||
|
||||
show_man_page(argv[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
391
rev-list.c
391
rev-list.c
|
@ -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);
|
||||
list = revs.commits;
|
||||
if (list && list->next)
|
||||
limited = 1;
|
||||
handle_one_commit(exclude, &list);
|
||||
handle_one_commit(include, &list);
|
||||
continue;
|
||||
}
|
||||
*dotdot = '.';
|
||||
}
|
||||
if (*arg == '^') {
|
||||
flags = UNINTERESTING;
|
||||
arg++;
|
||||
|
||||
if (revs.topo_order)
|
||||
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))
|
||||
(!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects))
|
||||
usage(rev_list_usage);
|
||||
|
||||
paths = get_pathspec(prefix, argv + i);
|
||||
if (paths) {
|
||||
if (revs.paths) {
|
||||
limited = 1;
|
||||
diff_tree_setup_paths(paths);
|
||||
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
370
revision.c
Normal 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
48
revision.h
Normal 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
|
|
@ -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+"$@"}
|
||||
:
|
||||
|
|
|
@ -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+"$@"}
|
||||
:
|
||||
|
|
Loading…
Reference in a new issue