From: Thomas Glanzmann <sithglan@stud.uni-erlangen.de>
To: Junio C Hamano <junkio@cox.net>, Linus Torvalds <torvalds@osdl.org>
Cc: Git Mailing List <git@vger.kernel.org>
Subject: Re: More gitweb queries..
Date: Mon, 30 May 2005 01:02:40 +0200 [thread overview]
Message-ID: <20050529230240.GB12290@cip.informatik.uni-erlangen.de> (raw)
In-Reply-To: <20050527203227.GA11139@cip.informatik.uni-erlangen.de>
[-- Attachment #1: Type: text/plain, Size: 4310 bytes --]
Hello,
here we go! gitk screenshot:
http://wwwcip.informatik.uni-erlangen.de/~sithglan/shot.png (64k)
(faui00u) [~/work/mutt/git/mutt-test] ../../../git/yagf/git pull ../mutt-attach-file/ ../mutt-collapse-flags/ ../mutt-cstatus/ ../mutt-cvs/ ../mutt-edit-threads/ ../mutt-hcache/ ../mutt-headers/ ../mutt-imap/ ../mutt-maildir-mtime/ ../mutt-move-hook/ ../mutt-setenv-hack/ ../mutt-thread-pattern/ ../mutt-menu-move/
<chatty output>
(faui00u) [~/work/mutt/git/mutt-test] git treediff ../mutt-tg-solaris
(faui00u) [~/work/mutt/git/mutt-test] git changes -m | perl -pe '$a += /^diff-tree/; exit if $a==2'
diff-tree 5b22da9792b6f6a968dc0a916275d6c301575f75 (from e8f4a291a81f0a8fb24555f0e36e4b75e2d3f4c8)
Author: Thomas Glanzmann <sithglan@stud.uni-erlangen.de>
Date: Mon May 30 00:28:53 2005 +0200
=> faui00u:/home/cip/adm/sithglan/work/mutt/git/mutt-test
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-attach-file/.git (bringing head ahead)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-collapse-flags/.git (automatic merge)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-cstatus/.git (threeway merge)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-cvs/.git (nothing to merge)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-edit-threads/.git (threeway merge)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-hcache/.git (threeway merge)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-headers/.git (threeway merge)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-imap/.git (automatic merge)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-maildir-mtime/.git (threeway merge)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-move-hook/.git (nothing to merge)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-setenv-hack/.git (automatic merge)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-thread-pattern/.git (threeway merge)
<= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-menu-move/.git (threeway merge)
Could please someone who has a clue how this merge should work (which it
hopefully already does) look at my code to check for obvious mistakes in
the merge *logic*. I call 'merge' with all involved trees (with current
head first, if there is a current head). If everything is fine. I would
like to write a seperate git-resolve script in sh or adopt the current
to multi head merge - whatever you please.
sub
merge
{
my $message = undef;
my $head = undef;
my $last_tree = undef;
my $fh;
my @heads = ();
foreach my $r (@_) {
my $current_head = @{$r}[0];
my $current_url = @{$r}[1];
print "current_head => $current_head\ncurrent_url => $current_url\n";
push(@heads, '-p', ${current_head});
if (! defined($last_tree)) {
$message = "=> ${current_url}\n";
$head = $current_head;
$last_tree = $current_head;
if (@_ == 1) {
head($head);
return;
}
next;
}
my $merge_base = gitcmdout('git-merge-base', $head, $current_head)
|| die ("no merge-base");
chomp($merge_base);
print "head => $head\nremote => $current_head\nbase => $merge_base\n";
if ($merge_base eq $current_head) {
$message .= "<= ${current_url} (nothing to merge)\n";
if (@_ == 2) {
return;
}
next;
}
if ($merge_base eq $head) {
$message .= "<= ${current_url} (bringing head ahead)\n";
$head = ${current_head};
$last_tree = ${current_head};
if (@_ == 2) {
head($head);
return;
}
next;
}
gitcmd('git-read-tree', '-m', $merge_base, $last_tree, $current_head);
if (! defined($last_tree = write_tree())) {
system('git-merge-cache', '-o', 'git-merge-one-file-script', '-a');
if (! defined($last_tree = write_tree())) {
# FIXME: Make manual intervention possible
# --tg 23:11 05-05-29
die("Couldn't merge automatically: Call 'git resolve'");
}
$message .= "<= ${current_url} (threeway merge)\n";
} else {
$message .= "<= ${current_url} (automatic merge)\n";
}
}
open($fh, "+>", undef);
print $fh $message;
seek($fh, 0, 0);
$head = gitcmdinout($fh, 'git-commit-tree', $last_tree, @heads);
chomp($head);
close $fh;
head($head);
return;
}
Thomas
[-- Attachment #2: git --]
[-- Type: text/plain, Size: 25336 bytes --]
#!/usr/bin/env perl
use strict;
use warnings;
use IO::Handle;
use File::Temp qw/ tempfile tempdir /;
use File::Copy;
use Cwd;
use Getopt::Long;
STDOUT->autoflush(1);
my $hostname = gitcmdout('hostname');
chomp($hostname);
my $DIFF = undef;
my $PATCH = undef;
if (-x '/opt/csw/bin/gdiff') {
$DIFF='/opt/csw/bin/gdiff';
} else {
$DIFF='diff';
}
if (-x '/usr/bin/gpatch') {
$PATCH='/usr/bin/gpatch';
} else {
$PATCH='patch';
}
my %commands = (
"add" => \&add,
"checkout" => \&checkout,
"ci" => \&ci,
"clone" => \&clone,
"commit" => \&commit,
"diff" => \&diff,
"treediff" => \&treediff,
"dirty" => \&dirty,
"help" => \&usage,
"init-db" => \&init_db,
"log" => \&log,
"orphan" => \&orphan,
"parent" => \&parent,
"patch" => \&patch,
"pull" => \&pull,
"push" => \&_push,
"refresh" => \&refresh,
"revert" => \&revert,
"rm" => \&rm,
"setup" => \&setup,
"status" => \&status,
"undo" => \&undo,
"changed" => \&changed,
"resolve" => \&resolve,
"changes" => \&changes,
);
my %touched = ();
sub
usage
{
print STDERR <<"__EOF__";
Usage: $0 COMMAND [ARG]...
Available commands:
__EOF__
print "\t" . join("\n\t", sort(keys(%commands))) . "\n\n";
return 1;
}
sub
refresh
{
`git-update-cache --refresh`;
}
sub
process_git_diff_output
{
my $str = shift || return (());
my @in = split("\0", $str);
my @out = ();
while (@in) {
my @tmp = split(' ', shift(@in));
$tmp[0] =~ s/^://g;
push(@tmp, shift(@in));
push(@out, [@tmp]);
}
return(@out);
}
sub
dirty_files
{
refresh();
my @dirty = ();
my $str = gitcmdout('git-diff-files', '-z', '-r');
foreach (process_git_diff_output($str)) {
if ((@{$_})[1] ne '000000') {
push(@dirty, @{$_}[5]);
}
#print "<" . join("> <", @{$_}) . ">\n";
}
return @dirty;
}
sub
orphan_files
{
my @orphan = gitcmdout('git-ls-files', '--others');
chomp(@orphan);
my $regexp = '^\.';
if (-f '.git/ignore') {
my @ignore = ();
chomp (@ignore = _read_file('.git/ignore'));
$regexp = join('|', '^\.', @ignore);
}
@orphan = grep(! /$regexp/, @orphan);
return @orphan;
}
sub
orphan
{
foreach (orphan_files()) {
print $_ . "\n";
}
}
sub
dirty
{
foreach (dirty_files()) {
print $_ . "\n";
}
}
sub
changed
{
foreach my $rev (gitcmdout('git-rev-list', 'HEAD')) {
chomp($rev);
my %hash = commit_hash($rev);
my $str = gitcmdout('git-diff-tree', '--root', '-r', '-z', $rev);
foreach (process_git_diff_output($str)) {
my ($time, $rest) = split(/\s/, $hash{committer_date});
push(@{$touched{@{$_}[5]}}, $time);
}
}
}
sub
status
{
my %hash = ();
foreach (orphan_files()) {
$hash{$_} = '?';
}
foreach (dirty_files()) {
$hash{$_} = 'D';
}
my $str = gitcmdout('git-diff-cache', '-r', '--cached', '-z', 'HEAD');
foreach (process_git_diff_output($str)) {
if (@{$_}[0] eq '000000') {
$hash{@{$_}[5]} .= '+';
} elsif (@{$_}[1] eq '000000') {
$hash{@{$_}[5]} .= '-';
} else {
$hash{@{$_}[5]} .= '*';
}
}
foreach (sort(keys(%hash))) {
if ($hash{$_} eq "D") {
$hash{$_} = "D ";
}
printf("% 2s %s\n", $hash{$_}, $_);
}
}
sub
write_tree
{
chomp (my $tree = `git-write-tree`);
if ($?) {
return undef;
} else {
return $tree;
}
}
sub
retrieve_unmerged
{
my %hash = ();
foreach my $line (gitcmdout('git-ls-files', '--unmerged')) {
chomp($line);
# 100644 cde27275fad8103084d7ed2d08d246ba4ce6eb9c 1 Makefile
# 100644 d311a35e5e8a09629ea9e6051a43710c76fa8f6d 2 Makefile
# 100644 ec2b76bf90fb105b2aaf00a66f44b135046d3002 3 Makefile
if ($line =~ /^(\d{6})\s([a-z0-9]{40})\s(\d)\s(.+)$/) {
push(@{$hash{$4}}, $line);
} else {
die("Can't match: <$line>\n");
}
}
return %hash;
}
sub
process_unmerged_file
{
my $line;
my $file;
my @files = ();
while($line = shift) {
if ($line =~ /^(\d{6})\s([a-z0-9]{40})\s(\d)\s(.+)$/) {
if ($3 eq '1') { $file = "$4.GCA"; }
if ($3 eq '2') { $file = "$4.LOCAL"; }
if ($3 eq '3') { $file = "$4.REMOTE"; }
my $mode = substr($1, 2);
if (-f $file) {
die("Please get rid of $file\n");
}
print STDERR "Checking out: $file with permissions $mode\n";
system("git-cat-file blob $2 > $file");
chmod oct($mode), $file;
push(@files, $file);
}
}
if (@files == 3) {
my $filename = (@files)[0];
$filename =~ s#\.(GCA|LOCAL|REMOTE)$##;
print STDERR <<"EOF";
You got GCA, LOCAL and REMOTE, so I run merge for you and leaving the merges in
${filename} .\n
EOF
unlink($filename);
system('cp', "${filename}.LOCAL", "${filename}");
system('merge', "${filename}", "${filename}.GCA", "${filename}.REMOTE");
}
print STDERR <<"EOF";
Please resolve the conflict and add the file using the command 'git ci file'.
Droping you to a login shell now. Please exit the shell as soon as you resolved
the conflict.
EOF
system($ENV{'SHELL'}, '--login');
foreach (@files) {
unlink($_);
}
}
sub
resolve
{
if (! -f '.git/RESOLVE') {
die("Nothing to resolve");
}
if ( ! -f '.git/HEAD' && ! -l '.git/HEAD') {
die("How the hell I am supposed to resolve without a head?");
}
my $fh;
my $head = head();
my $merge_tree = undef;
my $pwd = getcwd;
chomp (my ($remote_head, $url) = _read_file('.git/RESOLVE'));
print "remote_head => $remote_head\nurl => $url\n";
my %unmerged = retrieve_unmerged();
foreach my $file (keys(%unmerged)) {
process_unmerged_file(@{$unmerged{$file}});
}
if (! defined($merge_tree = write_tree())) {
die("Still unresolved conflicts. Run git resolve again.");
}
open($fh, "+>", undef);
print $fh "Manual Merge $url => ${hostname}:${pwd}\n";
seek($fh, 0, 0);
chomp($head = gitcmdinout($fh, 'git-commit-tree', $merge_tree, '-p', $head, '-p', $remote_head));
close $fh;
head($head);
unlink('.git/RESOLVE');
checkout('-f');
print STDERR "All issues resolved manual: Commited as ${head}.\n";
print STDERR "Have a pleasant day.\n";
return;
}
sub
merge
{
my $message = undef;
my $head = undef;
my $last_tree = undef;
my $fh;
my @heads = ();
foreach my $r (@_) {
my $current_head = @{$r}[0];
my $current_url = @{$r}[1];
print "current_head => $current_head\ncurrent_url => $current_url\n";
push(@heads, '-p', ${current_head});
if (! defined($last_tree)) {
$message = "=> ${current_url}\n";
$head = $current_head;
$last_tree = $current_head;
if (@_ == 1) {
print "Setting HEAD\n";
head($head);
return;
}
print "Next round\n";
next;
}
my $merge_base = gitcmdout('git-merge-base', $head, $current_head)
|| die ("no merge-base");
chomp($merge_base);
print "head => $head\nremote => $current_head\nbase => $merge_base\n";
if ($merge_base eq $current_head) {
$message .= "<= ${current_url} (nothing to merge)\n";
if (@_ == 2) {
return;
}
next;
}
if ($merge_base eq $head) {
$message .= "<= ${current_url} (bringing head ahead)\n";
$head = ${current_head};
$last_tree = ${current_head};
if (@_ == 2) {
head($head);
return;
}
next;
}
gitcmd('git-read-tree', '-m', $merge_base, $last_tree, $current_head);
if (! defined($last_tree = write_tree())) {
system('git-merge-cache', '-o', 'git-merge-one-file-script', '-a');
if (! defined($last_tree = write_tree())) {
# FIXME: Make manual intervention possible
# --tg 23:11 05-05-29
die("Couldn't merge automatically: Call 'git resolve'");
}
$message .= "<= ${current_url} (threeway merge)\n";
} else {
$message .= "<= ${current_url} (automatic merge)\n";
}
}
open($fh, "+>", undef);
print $fh $message;
seek($fh, 0, 0);
$head = gitcmdinout($fh, 'git-commit-tree', $last_tree, @heads);
chomp($head);
close $fh;
head($head);
return;
}
sub
patch
{
my @files = ();
my ($fh, $patch);
my $file = shift || die("Need at least on argument.\n");
my $head = undef;
my $dir = undef;
if (head()
&& ( dirty_files()
|| `git-diff-cache -r --cached HEAD`)) {
print STDERR "Get rid of dirty files / uncommited deltas first.\n";
exit 1;
}
($fh, $patch) = tempfile(CLEANUP => 1);
`filterdiff -x '*/.*' $file > $patch`;
@files = `lsdiff --strip 1 $patch`;
chomp(@files);
$dir = tempdir(CLEANUP => 1);
$ENV{GIT_INDEX_FILE} = "$dir/.index";
if ($head = head()) {
gitcmd('git-read-tree', $head);
foreach my $file (@files) {
gitcmd('git-checkout-cache', '-q', "--prefix=$dir/", $file);
}
}
my $pwd = getcwd;
symlink("$pwd/.git", "$dir/.git");
chdir($dir);
# FIXME call in batch modus and check return value --tg 08:30 05-05-21
system("${PATCH} -p1 < $patch");
foreach my $file (@files) {
gitcmd('git-update-cache', '--add', '--remove', $file);
}
commit();
chdir($pwd);
delete($ENV{GIT_INDEX_FILE});
checkout('-f');
}
sub
checkout
{
my $head = head();
gitcmd('git-read-tree', '-m', $head);
# FIXME if this fails call it without -m --tg 20:25 05-05-09
if (defined($_[0]) && $_[0] eq '-f') {
gitcmd('git-checkout-cache', '-u', '-f', '-a');
} else {
gitcmd('git-checkout-cache', '-u', '-q', '-a');
}
# changed();
#
# foreach my $file (gitcmdout('git-ls-files')) {
# chomp($file);
# my $time = (sort {$b <=> $a} @{$touched{$file}})[0];
# utime $time, $time, $file;
# }
}
sub
generate_url
{
my $url = shift;
if (! defined($url)) {
if ( -f '.git/PARENT' || -l '.git/PARENT') {
chomp ($url = _read_file('.git/PARENT'));
} else {
die("$0: No URL specified and no parent found: Where to pull from?\n");
}
} else {
$url =~ s#\/$##;
if (-d $url &&
! ($url =~ /\.git$/)) {
$url .= "/.git";
}
if (-d $url &&
! ($url =~ /^\//)) {
# FIXME: use Cwd; my $pwd = getcwd; --tg 21:32 05-05-03
chomp(my $pwd = `pwd`);
$url = "${pwd}/${url}";
}
}
return $url;
}
sub
pull
{
my @references = ();
my %options;
local @ARGV = @_;
GetOptions(\%options, 'o', 'l');
@_ = @ARGV;
if (head()
&& (! defined($options{'o'}))
&& ( dirty_files()
|| `git-diff-cache -r --cached HEAD`)) {
print STDERR "Get rid of dirty files / uncommited deltas first.\n";
exit 1;
}
# FIXME SANITY CHECK. It is only possible to pull in 15 trees
# --tg 17:58 05-05-28
if (! defined($_[0])) {
unshift(@_, generate_url(shift));
}
if ( -f '.git/HEAD' || -l '.git/HEAD') {
push(@references, [head(), "${hostname}:" . getcwd()]);
}
while(defined (my $url = shift)) {
$url = generate_url($url);
my $remote_head = undef;
if (defined($options{'l'}) && -d $url) {
$ENV{'GIT_ALTERNATE_OBJECT_DIRECTORIES'} = "${url}/objects";
} else {
gitcmd('rsync', '-qa', '--ignore-existing', "$url/objects/.", ".git/objects/.");
}
gitcmd('rsync', '-qL', "$url/HEAD", '.git/REMOTE_HEAD');
chomp($remote_head = _read_file('.git/REMOTE_HEAD'));
push(@references, [$remote_head, $url]);
}
if (! defined($options{'o'})) {
merge(@references);
checkout('-f');
}
}
sub
changes
{
my %options;
local @ARGV = @_;
GetOptions(\%options, "L", "R", "d", "m", "n", "t=s", "S=s", 'root');
@_ = @ARGV;
my $git_diff_tree_options = '-s';
if (defined ($options{'d'})) {
$git_diff_tree_options = '-p';
}
if (defined ($options{'m'})) {
$git_diff_tree_options .= ' -m';
}
if (defined ($options{'S'})) {
$git_diff_tree_options .= " -S'$options{'S'}'";
}
if (defined ($options{'root'})) {
$git_diff_tree_options .= " --root";
}
if (defined ($options{'L'})) {
if (! defined ($options{'n'})) {
pull('-o', '-l', shift);
}
system("git-rev-tree HEAD '^REMOTE_HEAD' | sed -e 's/^[0-9]* //' | git-diff-tree --stdin -v $git_diff_tree_options " . join(' ', @ARGV));
} elsif (defined ($options{'R'})) {
if (! defined ($options{'n'})) {
pull('-o', '-l', shift);
}
system("git-rev-tree REMOTE_HEAD '^HEAD' | sed -e 's/^[0-9]* //' | git-diff-tree --stdin -v $git_diff_tree_options " . join(' ', @ARGV));
} elsif (defined ($options{'t'})) {
system("git-rev-tree HEAD '^$options{'t'}' | sed -e 's/^[0-9]* //' | git-diff-tree --stdin -v $git_diff_tree_options " . join (' ', @ARGV));
} else {
system("git-rev-list HEAD | git-diff-tree --stdin -v $git_diff_tree_options " . join(' ', @ARGV));
}
}
sub
_push
{
my $url = generate_url(shift);
gitcmd('rsync', '-qL', "$url/HEAD", '.git/REMOTE_HEAD');
chomp (my $remote_head = _read_file('.git/REMOTE_HEAD'));
my $head = head() || die("No local HEAD\n");
if ($head eq $remote_head) {
print STDERR "Nothing to push.\n";
return;
}
print "head => $head\nremote => $remote_head\n";
my @revlist = gitcmdout('git-rev-list', $head);
if (! grep(/^${remote_head}$/, @revlist)) {
print STDERR "Remote is ahead or unrelated: Need to pull first?\n";
exit 1;
}
gitcmd('rsync', '-qa', '--ignore-existing', ".git/objects/.", "$url/objects/.");
gitcmd('rsync', '-q', '.git/HEAD', "$url/HEAD");
}
sub
clone
{
my %options;
GetOptions(\%options, "l");
my $url = shift(@ARGV);
my $project = shift(@ARGV);
if (! defined($url) || ! defined($project)) {
die("Usage: $0 clone <url> <project>\n");
}
$url = generate_url($url);
if (defined($options{l})) {
if (-d $url) {
-d ".git" && die("$0 clone $project: Don\'t create repository in a repository.");
mkdir($project, 0755) || die("$0 clone $project: mkdir: $!");
chdir($project) || die("$0 clone $project: chdir: $!");
mkdir('.git', 0755) || die("$0 clone $project: mkdir: $!");
symlink("$url/objects", '.git/objects') || die("Can\'t symlink object database");
} else {
die("Can't symlink from network repositories\n");
}
} else {
setup($project);
}
parent($url);
pull();
}
sub
parent
{
my $parent = $_[0];
if (defined($parent)) {
if ($parent eq "-c") {
if ( -f ".git/PARENT" || -l ".git/PARENT") {
unlink(".git/PARENT") || die("can't delete .git/PARENT: $!");
}
print STDERR "$0 parent: Parent removed\n";
} else {
$parent = generate_url($parent);
_write_file('.git/PARENT', "$parent\n");
}
} else {
if ( -f ".git/PARENT" || -l ".git/PARENT") {
chomp($parent = _read_file('.git/PARENT'));
print "${parent}\n";
} else {
print STDERR "$0 parent: No parent specified\n";
}
}
}
sub
diff
{
my %options;
local @ARGV = @_;
GetOptions(\%options, 'r=s', 'f', 'c', 'v', "S=s", 'root');
@_ = @ARGV;
my $dir = tempdir(CLEANUP => 1);
refresh();
my $git_diff_tree_options = '';
if (defined ($options{'S'})) {
$git_diff_tree_options .= " -S'$options{'S'}'";
}
if (defined ($options{'root'})) {
$git_diff_tree_options .= " --root";
}
if (defined($options{'r'})) {
if($options{'r'} =~ /:/) {
my ($first, $second) = split(/:/, $options{'r'});
system("git-diff-tree -p -r $first $second $git_diff_tree_options " . join(' ', @_));
} else {
if (defined ($options{'v'})) {
$git_diff_tree_options .= ' -v';
}
system("git-diff-tree -p -r $options{'r'} $git_diff_tree_options " . join(' ', @_));
}
} elsif (defined($options{"f"})) {
system("git-diff-files -r -z $git_diff_tree_options " . join(' ', @_) . " | git-diff-helper -z");
} elsif (defined($options{"c"})) {
system("git-diff-cache --cached -r -z HEAD $git_diff_tree_options " . join(' ', @_) . " | git-diff-helper -z");
} else {
system("git-diff-cache -r -z HEAD $git_diff_tree_options " . join(' ', @_) . " | git-diff-helper -z");
}
}
sub
treediff
{
my %options;
local @ARGV = @_;
Getopt::Long::Configure("pass_through");
GetOptions(\%options, 'p=s');
Getopt::Long::Configure("no_pass_through");
@_ = @ARGV;
pull('-o', '-l', $options{'p'});
diff('-r', 'REMOTE_HEAD:HEAD', @_);
}
sub
init_db
{
gitcmd( 'init-db', @_ );
}
sub
setup
{
my $project = shift || die ("usage: $0 setup project");
-d ".git" && die ("$0 setup $project: Don\'t create repository in a repository.");
mkdir($project, 0755) || die ("$0 setup $project: mkdir: $!");
chdir($project) || die ("$0 setup $project: chdir: $!");
gitcmd('git-init-db');
}
sub
revert
{
# TODO handle revert of add/remove --tg 02:32 05-05-06
my @in = ();
my $fh;
if (! defined($_[0])) {
return;
}
if ($_[0] eq "-") {
@in = <STDIN>;
} else {
@in = @_;
}
my $head = head();
($fh, $ENV{GIT_INDEX_FILE}) = tempfile(CLEANUP => 1);
gitcmd('git-read-tree', $head);
foreach (@in) {
chomp($_);
$_ =~ s#^\.\/##;
if (/^\./) {
print STDERR "Warning: Skipping $_\n";
next;
}
if (-d $_) {
next;
}
gitcmd('git-checkout-cache', '-f', $_);
}
delete($ENV{GIT_INDEX_FILE});
foreach (@in) {
chomp($_);
$_ =~ s#^\.\/##;
if (/^\./) {
next;
}
if (! -f $_) {
next;
}
gitcmd('git-update-cache', $_);
}
}
sub
add
{
my @in = ();
if (defined($_[0]) && $_[0] eq "-") {
@in = <STDIN>;
} else {
@in = @_;
}
foreach (@in) {
chomp($_);
$_ =~ s#^\.\/##;
if (/^\./) {
print STDERR "Warning: Skipping $_\n";
next;
}
if (-d $_) {
next;
}
if (! -f $_) {
print STDERR "Warning: Skipping nonexisting file: $_\n";
next;
}
gitcmd( 'git-update-cache', '--add', '--', $_ );
}
}
sub
ci
{
my @in = ();
if (defined($_[0]) && $_[0] eq "-") {
@in = <STDIN>;
} else {
@in = @_;
}
foreach (@in) {
chomp($_);
$_ =~ s#^\.\/##;
if (/^\./) {
print STDERR "Warning: Skipping $_\n";
next;
}
if (-d $_) {
next;
}
if (! -f $_) {
print STDERR "Warning: Skipping nonexisting file: $_\n";
next;
}
gitcmd( 'git-update-cache', '--', $_ );
}
}
sub
rm
{
my @in = ();
if (defined($_[0]) && $_[0] eq "-") {
@in = <STDIN>;
} else {
@in = @_;
}
foreach (@in) {
chomp($_);
$_ =~ s#^\.\/##;
if (/^\./) {
print STDERR "Warning: Skipping $_\n";
next;
}
if (-d $_) {
next;
}
if (-f $_) {
unlink($_) || die ("$0 rm $_: $!");
}
gitcmd( 'git-update-cache', '--remove', '--' , $_ );
}
}
sub
head
{
my $head = $_[0];
if (defined($head)) {
if ($head eq "") {
if ( -f ".git/HEAD" || -l ".git/HEAD") {
unlink(".git/HEAD") || die "failed to delete .git/HEAD: $!\n";
}
} else {
chomp($head);
_write_file( '.git/HEAD', "$head\n" );
}
} else {
if (-f '.git/HEAD') {
chomp( $head = _read_file( '.git/HEAD' ) );
} else {
$head = undef;
}
}
return $head;
}
sub
print_commit
{
my $commit = shift || die("need commit");
print "commit $commit\n";
foreach (gitcmdout('git-cat-file', 'commit', $commit)) {
if (/^(author|committer)(.+>\s)(\d+)\s([+-]?\d{4})$/) {
# local $ENV{TZ} = $4;
print "$1$2" . localtime($3) . "\n";
} else {
print "$_\n";
}
}
print "\n";
}
sub
commit
{
my $head = head();
chomp (my $tree = gitcmdout('git-write-tree'));
if (dirty_files()) {
die "$0 commit: Get rid of dirty files first.\n";
}
if (defined($head)) {
my %hash = commit_hash($head);
if ($hash{tree} eq $tree) {
die "$0 commit: The commit wouldn't commit anything different.\n";
}
$head = gitcmdout('git-commit-tree', $tree, '-p', $head);
} else {
$head = gitcmdout('git-commit-tree', $tree);
}
head($head);
}
sub
log
{
my $head = head() || return ;
my $pid;
if ( ! -p STDIN && ! -p STDOUT ) {
my ( $r, $w );
pipe( $r, $w ) || die "Failed to pipe: $!";
defined( $pid = fork ) || die "Failed to fork: $!";
if ( $pid ) { # Parent
$SIG{INT} = $SIG{TERM} = $SIG{HUP} = sub {
kill 15, $pid;
exit 1;
};
close $r;
close STDOUT;
open STDOUT, '>&', $w || die "Failed to redirect STDOUT: $!";
} else { # pager child
close $w;
close STDIN;
open STDIN, '<&', $r || die "Failed to redirect STDIN: $!";
if ( $ENV{PAGER} ) {
exec( $ENV{PAGER} );
} else {
exec( 'less', '-r', '-' );
}
}
}
foreach (gitcmdout('git-rev-list', $head)) {
print_commit($_);
}
if ( $pid ) {
close STDOUT;
waitpid($pid, 0);
}
}
sub
commit_hash
{
my %hash = ();
my $id = shift || die ("$0: commit_hash: expect one argument");
my $comment = 0;
my @lines = gitcmdout('git-cat-file', 'commit', $id);
foreach (@lines) {
chomp;
if ($comment) {
push(@{$hash{comment}}, $_);
next;
}
if (/^tree\s(\w{40})$/) {
$hash{tree} = $1;
next;
}
if (/^parent\s(\w{40})$/) {
push(@{$hash{parent}}, $1);
next;
}
if (/^author\s(.*)\s<(.*)>\s(.*)$/) {
$hash{author_name} = $1;
$hash{author_eMail} = $2;
$hash{author_date} = $3;
next;
}
if (/^committer\s(.*)\s<(.*)>\s(.*)$/) {
$hash{committer_name} = $1;
$hash{committer_eMail} = $2;
$hash{committer_date} = $3;
next;
}
if (/^$/) {
$comment = 1;
}
}
return(%hash);
}
sub
first_parent
{
my $this = shift || die ("$0: parent called without object");
my %hash = commit_hash($this);
if (! defined(@{$hash{parent}}[0])) {
return "";
}
return(@{$hash{parent}}[0]);
}
sub
undo
{
my $head = head();
if (! defined($head)) {
return;
}
if ( dirty_files()
|| `git-diff-cache -r --cached HEAD`) {
print STDERR "Get rid of dirty files / uncommited deltas first.\n";
exit 1;
}
my $newhead = first_parent($head);
print "oldhead $head\nnewhead $newhead\n";
head($newhead);
checkout('-f');
}
sub
object_type
{
my $id = shift;
defined $id && $id =~ /^[A-Za-z0-9]{40}$/ ||
die "Invalid sha1 id '$id'";
my $type = gitcmdout('git-cat-file', '-t', $id );
chomp $type;
return $type;
}
sub
tree_id
{
my $id = shift;
( $id ) = grep { defined }
map { /^tree ([A-Za-z0-9]{40})$/ ? $1 : undef }
gitcmdout( 'git-cat-file', 'commit', $id ) or
die "Unable to find tree id for commit id $id";
object_type( $id ) eq 'tree' ||
die "tree id from commit is not a tree object!";
return $id;
}
sub
parent_id
{
my $id = shift;
my ( $pid ) = grep { defined }
map { /^parent ([A-Za-z0-9]{40})$/ ? $1 : undef }
gitcmdout( 'git-cat-file', 'commit', $id ) or
die "Unable to determine parent commit of commit $id";
return $pid;
}
# int
# main(int argc, char **argv)
# {
if (defined($ARGV[0]) && defined($commands{$ARGV[0]})) {
my $string = shift;
if ( ! -d ".git"
&& ! ($string eq "setup"
|| $string eq "init-db"
|| $string eq "clone")) {
die("$0 $string: Not in a git BASE directory");
}
$commands{$string}->(@ARGV);
} else {
if (defined($ARGV[0])) {
print STDERR "No such command: $ARGV[0]\n\n";
}
usage();
}
# }
{
my %gitcmd;
sub
gitcmdpath
{
my $cmd = shift;
unless ( defined $gitcmd{$cmd} ) {
local $/ = "\n";
chomp( $gitcmd{$cmd} = `which $cmd` );
return undef if $gitcmd{$cmd} eq '';
}
return $gitcmd{$cmd};
}
sub
gitcmd
{
my $cmd = shift;
gitcmdpath( $cmd ) || die "command '$cmd' not found";
my $r = system( $gitcmd{$cmd}, @_ );
die "$cmd failed: " . _gitcmderrmsg( $cmd )
if $r != 0;
return 1;
}
sub
gitcmdinout
{
my $infh = shift;
my $cmd = shift;
gitcmdpath( $cmd ) || die "command '$cmd' not found";
my ( $r, $w );
pipe( $r, $w ) || die "Failed to pipe: $!";
my $pid = fork();
die "Failed to fork: $!" unless defined $pid;
if ( $pid ) {
close $w;
local $/;
local $_ = <$r>;
close $r;
my $kid = waitpid( $pid, 0 );
die "Hmm, auto reaping in place?" if $kid == -1;
die "$cmd failed: " . _gitcmderrmsg( $cmd )
if $? & 127 || $? >> 8 != 0;
if ( wantarray ) {
return split( "\n", $_ );
} else {
return $_;
}
} else {
close $r;
close STDOUT;
close STDIN;
open STDIN, '<&', $infh || die "Failed to rediret STDIN";
open STDOUT, '>&', $w || die "Failed to redirect STDOUT";
exec( $gitcmd{$cmd}, @_ );
}
}
sub
gitcmdout
{
my $cmd = shift;
gitcmdpath( $cmd ) || die "command '$cmd' not found";
my ( $r, $w );
pipe( $r, $w ) || die "Failed to pipe: $!";
my $pid = fork();
die "Failed to fork: $!" unless defined $pid;
if ( $pid ) {
close $w;
local $/;
my $ret = <$r>;
close $r;
my $kid = waitpid( $pid, 0 );
die "Hmm, auto reaping in place?" if $kid == -1;
die "$cmd failed: " . _gitcmderrmsg( $cmd )
if $? & 127 || $? >> 8 != 0;
if (wantarray) {
return split("\n", $ret);
} else {
return $ret;
}
} else {
close $r;
close STDOUT;
open STDOUT, '>&', $w || die "Failed to redirect STDOUT";
exec( $gitcmd{$cmd}, @_ );
}
}
sub
_gitcmderrmsg
{
my $cmd = shift;
my $e;
if ( $? == -1 ) {
$e = "failed to execute $gitcmd{$cmd}: $!";
} elsif ( $? & 127 ) {
$e = sprintf( 'child die from signal %d', ( $? & 127 ) );
$e .= ' (with coredump)' if $? & 128;
} else {
$e = sprintf( 'child exit value: %d', $? >> 8 );
}
return $e;
}
}
sub
_recur_mkdir
{
my $dir = shift;
my @dir = split( /\//, $dir );
my $path = '';
while ( @dir ) {
$path .= '/' . shift @dir;
( -d $path ) || mkdir( $path ) ||
die "Failed to mkdir $path: $!";
}
}
sub
_read_file
{
my $file = shift;
my $fh;
open $fh, '<', $file || die "failed to read $file: $!\n";
if ( wantarray ) {
my @r = <$fh>;
close $fh || die "failed to close $file: $!\n";
return @r;
} else {
local $/;
my $r = <$fh>;
close $fh || die "failed to close $file: $!\n";
return $r;
}
}
sub
_write_file
{
my $file = shift;
my $fh;
open $fh, '>', $file || die "failed to write $file: $!\n";
if ( @_ ) {
print $fh join( $/, @_ );
}
close $fh || die "failed to close $file: $!\n";
return 1;
}
# vim:set noexpandtab:
next prev parent reply other threads:[~2005-05-29 23:03 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-05-27 19:24 More gitweb queries Linus Torvalds
2005-05-27 19:29 ` Thomas Glanzmann
2005-05-27 19:52 ` Junio C Hamano
2005-05-27 19:55 ` Thomas Glanzmann
2005-05-27 20:13 ` Junio C Hamano
2005-05-27 20:32 ` Thomas Glanzmann
2005-05-27 20:40 ` Junio C Hamano
2005-05-27 22:00 ` Linus Torvalds
2005-05-27 22:04 ` Thomas Glanzmann
2005-05-28 2:26 ` Junio C Hamano
2005-05-29 23:02 ` Thomas Glanzmann [this message]
2005-05-29 23:10 ` Thomas Glanzmann
2005-05-29 23:16 ` Thomas Glanzmann
2005-05-29 23:46 ` Thomas Glanzmann
2005-05-29 23:56 ` Thomas Glanzmann
2005-05-30 0:50 ` Junio C Hamano
2005-05-30 0:57 ` Junio C Hamano
2005-05-30 1:33 ` Thomas Glanzmann
2005-05-30 1:30 ` Thomas Glanzmann
2005-05-30 7:57 ` Junio C Hamano
2005-05-30 8:36 ` Thomas Glanzmann
2005-05-30 9:21 ` Thomas Glanzmann
2005-05-30 10:20 ` Junio C Hamano
2005-05-30 12:11 ` Thomas Glanzmann
2005-05-30 17:54 ` Junio C Hamano
2005-05-27 20:17 ` Linus Torvalds
2005-05-27 19:54 ` Junio C Hamano
2005-05-27 19:58 ` Thomas Glanzmann
2005-05-27 20:03 ` Linus Torvalds
2005-05-27 20:24 ` Junio C Hamano
2005-05-27 19:31 ` Thomas Glanzmann
2005-05-27 19:32 ` Junio C Hamano
2005-05-27 19:48 ` Linus Torvalds
2005-05-27 23:12 ` Benjamin Herrenschmidt
2005-05-27 23:59 ` Kay Sievers
2005-05-28 1:03 ` Daniel Serpell
2005-05-28 2:51 ` David Lang
2005-05-28 10:56 ` Kay Sievers
2005-05-28 8:42 ` Kay Sievers
2005-05-28 22:43 ` Benjamin Herrenschmidt
2005-05-30 23:03 ` Paul Mackerras
2005-05-31 2:27 ` Jeff Epler
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20050529230240.GB12290@cip.informatik.uni-erlangen.de \
--to=sithglan@stud.uni-erlangen.de \
--cc=git@vger.kernel.org \
--cc=junkio@cox.net \
--cc=torvalds@osdl.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).