Here is a diff to update the git-merge.perl script I showed you
earlier today ;-). It contains the following updates against
your HEAD (bb95843a5a0f397270819462812735ee29796fb4).
* git-merge.perl command we talked about on the git list. I've
covered the changed-to-the-same case etc. I still haven't done
anything about file-vs-directory case yet.
It does warn when it needed to run merge to automerge and let
merge give a warning message about conflicts if any. In
modify/remove cases, modified in one but removed in the other
files are left in either $path~A~ or $path~B~ in the merge
temporary directory, and the script issues a warning at the
end.
* show-files and ls-tree updates to add -z flag to NUL terminate records;
this is needed for git-merge.perl to work.
* show-diff updates to add -r flag to squelch diffs for files not in
the working directory. This is mainly useful when verifying the
result of an automated merge.
* update-cache updates to add "--cacheinfo mode sha1" flag to register
a file that is not in the current working directory. Needed for
minimum-checkout merging by git-merge.perl.
Signed-off-by: Junio C Hamano <[EMAIL PROTECTED]>
---
git-merge.perl | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ls-tree.c | 9 +-
show-diff.c | 11 +-
show-files.c | 12 ++
update-cache.c | 25 +++++
5 files changed, 296 insertions(+), 8 deletions(-)
diff -x .git -Nru ,,1/git-merge.perl ,,2/git-merge.perl
--- ,,1/git-merge.perl 1969-12-31 16:00:00.000000000 -0800
+++ ,,2/git-merge.perl 2005-04-14 04:00:14.000000000 -0700
@@ -0,0 +1,247 @@
+#!/usr/bin/perl -w
+
+use Getopt::Long;
+
+my $full_checkout = 0;
+my $oneside_checkout = 0;
+GetOptions("full" => \$full_checkout,
+ "oneside" => \$oneside_checkout)
+ or die;
+
+if ($full_checkout) {
+ $oneside_checkout = 1;
+}
+
+sub read_rev_tree {
+ my (@head) = @_;
+ my ($fhi);
+ open $fhi, '-|', 'rev-tree', '--edges', @head
+ or die "$!: rev-tree --edges @head";
+ my %common;
+ while (<$fhi>) {
+ chomp;
+ (undef, undef, my @common) = split(/ /, $_);
+ for (@common) {
+ if (s/^([a-f0-f]{40}):3$/$1/) {
+ $common{$_}++;
+ }
+ }
+ }
+ close $fhi;
+
+ my @common = (map { $_->[1] }
+ sort { $b->[0] <=> $a->[0] }
+ map { [ $common{$_} => $_ ] }
+ keys %common);
+
+ return $common[0];
+}
+
+sub read_commit_tree {
+ my ($commit) = @_;
+ my ($fhi);
+ open $fhi, '-|', 'cat-file', 'commit', $commit
+ or die "$!: cat-file commit $commit";
+ my $tree = <$fhi>;
+ close $fhi;
+ $tree =~ s/^tree //;
+ return $tree;
+}
+
+# Reads diff-tree -r output and gives a hash that maps a path
+# to 3-tuple (old-mode new-mode new-sha).
+# When creating, old-mode is undef. When removing, new-* are undef.
+sub read_diff_tree {
+ my (@tree) = @_;
+ my ($fhi);
+ local ($_, $/);
+ $/ = "\0";
+ my %path;
+ open $fhi, '-|', 'diff-tree', '-r', @tree
+ or die "$!: diff-tree -r @tree";
+ while (<$fhi>) {
+ chomp;
+ if (/^\*([0-7]+)->([0-7]+)\tblob\t[0-9a-f]+->([0-9a-f]{40})\t(.*)$/s) {
+ $path{$4} = [$1, $2, $3];
+ }
+ elsif (/^\+([0-7]+)\tblob\t([0-9a-f]{40})\t(.*)$/s) {
+ $path{$3} = [undef, $1, $2];
+ }
+ elsif (/^\-([0-7]+)\tblob\t[0-9a-f]{40}\t(.*)$/s) {
+ $path{$2} = [$1, undef, undef];
+ }
+ else {
+ die "cannot parse diff-tree output: $_";
+ }
+ }
+ close $fhi;
+ return %path;
+}
+
+sub read_show_files {
+ my ($fhi);
+ local ($_, $/);
+ $/ = "\0";
+ open $fhi, '-|', 'show-files', '-z'
+ or die "$!: show-files -z";
+ my (@path) = map { chomp; $_ } <$fhi>;
+ close $fhi;
+ return @path;
+}
+
+sub checkout_file {
+ my ($path, $info) = @_;
+ my (@elt) = split(/\//, $path);
+ my $j = '';
+ my $tail = pop @elt;
+ my ($fhi, $fho);
+ for (@elt) {
+ mkdir "$j$_";
+ $j = "$j$_/";
+ }
+ open $fho, '>', "$path";
+ open $fhi, '-|', 'cat-file', 'blob', $info->[2]
+ or die "$!: cat-file blob $info->[2]";
+ while (<$fhi>) {
+ print $fho $_;
+ }
+ close $fhi;
+ close $fho;
+ chmod oct("0$info->[1]"), "$path";
+}
+
+sub record_file {
+ my ($path, $info) = @_;
+ system ('update-cache', '--add', '--cacheinfo',
+ $info->[1], $info->[2], $path);
+}
+
+sub merge_tree {
+ my ($path, $info0, $info1) = @_;
+ checkout_file(',,merge-0', $info0);
+ checkout_file(',,merge-1', $info1);
+ system 'checkout-cache', $path;
+ my ($fhi, $fho);
+ open $fhi, '-|', 'merge', '-p', ',,merge-0', $path, ',,merge-1';
+ open $fho, '>', "$path+";
+ local ($/);
+ while (<$fhi>) { print $fho $_; }
+ close $fhi;
+ close $fho;
+ unlink ',,merge-0', ',,merge-1';
+ rename "$path+", $path;
+ # There is no reason to prefer info0 over info1 but
+ # we need to pick one.
+ chmod oct("0$info0->[1]"), "$path";
+}
+
+# Find common ancestor of two trees.
+my $common = read_rev_tree(@ARGV);
+print "Common ancestor: $common\n";
+
+# Create a temporary directory and go there.
+system 'rm', '-rf', ',,merge-temp';
+for ((',,merge-temp', '.git')) { mkdir $_; chdir $_; }
+symlink "../../.git/objects", "objects";
+chdir '..';
+
+my $ancestor_tree = read_commit_tree($common);
+system 'read-tree', $ancestor_tree;
+
+my %tree0 = read_diff_tree($ancestor_tree, read_commit_tree($ARGV[0]));
+my %tree1 = read_diff_tree($ancestor_tree, read_commit_tree($ARGV[1]));
+
+my @ancestor_file = read_show_files();
+my %ancestor_file = map { $_ => 1 } @ancestor_file;
+
+for (@ancestor_file) {
+ if (! exists $tree0{$_} && ! exists $tree1{$_}) {
+ if ($full_checkout) {
+ system 'checkout-cache', $_;
+ }
+ print STDERR "O - $_\n";
+ }
+}
+
+for my $set ([\%tree0, \%tree1, 'A'], [\%tree1, \%tree0, 'B']) {
+ my ($treeA, $treeB, $side) = @$set;
+ while (my ($path, $info) = each %$treeA) {
+ # In this loop we do not deal with overlaps.
+ next if (exists $treeB->{$path});
+
+ if (! defined $info->[1]) {
+ # deleted in this tree only.
+ unlink $path;
+ system 'update-cache', '--remove', $path;
+ print STDERR "$side D $path\n";
+ }
+ else {
+ # modified or created in this tree only.
+ print STDERR "$side M $path\n";
+ if ($oneside_checkout) {
+ checkout_file($path, $info);
+ system 'update-cache', '--add', "$path";
+ } else {
+ record_file($path, $info);
+ }
+ }
+ }
+}
+
+my @warning = ();
+
+while (my ($path, $info0) = each %tree0) {
+ # We need to deal only with overlaps.
+ next if (!exists $tree1{$path});
+
+ my $info1 = $tree1{$path};
+ if (! defined $info0->[1]) {
+ # deleted in this tree.
+ if (! defined $info1->[1]) {
+ # deleted in both trees. Obvious.
+ print STDERR "*DD $path\n";
+ unlink $path;
+ system 'update-cache', '--remove', $path;
+ }
+ else {
+ # oops. tree0 wants to remove but tree1 wants to modify it.
+ print STDERR "*DM $path\n";
+ checkout_file("$path~B~", $info1);
+ push @warning, $path;
+ }
+ }
+ else {
+ # modified or created in tree0
+ if (! defined $info1->[1]) {
+ # oops. tree0 wants to modify but tree1 wants to remove it.
+ print STDERR "*MD $path\n";
+ checkout_file("$path~A~", $info0);
+ push @warning, $path;
+ }
+ else {
+ # modified both in tree0 and tree1
+ # are they modifying to the same contents?
+ if ($info0->[2] eq $info1->[2]) {
+ # just mode changes (or no changes)
+ # we prefer tree0 over tree1 for no particular reason.
+ print STDERR "*MM $path\n";
+ record_file($path, $info0);
+ }
+ else {
+ # modified in both. Needs merge.
+ print STDERR "MRG $path\n";
+ merge_tree($path, $info0, $info1);
+ }
+ }
+ }
+}
+
+if (@warning) {
+ print "\nThere are some files that were deleted in one branch and\n"
+ . "modified in another. Please examine them carefully:\n";
+ for (@warning) {
+ print "$_\n";
+ }
+}
+
+# system 'show-diff';
diff -x .git -Nru ,,1/ls-tree.c ,,2/ls-tree.c
--- ,,1/ls-tree.c 2005-04-14 03:47:18.000000000 -0700
+++ ,,2/ls-tree.c 2005-04-14 04:00:14.000000000 -0700
@@ -5,6 +5,8 @@
*/
#include "cache.h"
+int line_termination = '\n';
+
static int list(unsigned char *sha1)
{
void *buffer;
@@ -31,7 +33,8 @@
* It seems not worth it to read each file just to get this
* and the file size. -- [EMAIL PROTECTED] */
type = S_ISDIR(mode) ? "tree" : "blob";
- printf("%03o\t%s\t%s\t%s\n", mode, type, sha1_to_hex(sha1),
path);
+ printf("%03o\t%s\t%s\t%s%c", mode, type, sha1_to_hex(sha1),
+ path, line_termination);
}
return 0;
}
@@ -40,6 +43,10 @@
{
unsigned char sha1[20];
+ if (argc == 3 && !strcmp(argv[1], "-z")) {
+ line_termination = 0;
+ argc--; argv++;
+ }
if (argc != 2)
usage("ls-tree <key>");
if (get_sha1_hex(argv[1], sha1) < 0)
diff -x .git -Nru ,,1/show-diff.c ,,2/show-diff.c
--- ,,1/show-diff.c 2005-04-14 03:47:18.000000000 -0700
+++ ,,2/show-diff.c 2005-04-14 04:00:14.000000000 -0700
@@ -58,15 +58,20 @@
int main(int argc, char **argv)
{
int silent = 0;
+ int silent_on_nonexisting_files = 0;
int entries = read_cache();
int i;
while (argc-- > 1) {
if (!strcmp(argv[1], "-s")) {
- silent = 1;
+ silent_on_nonexisting_files = silent = 1;
continue;
}
- usage("show-diff [-s]");
+ if (!strcmp(argv[1], "-r")) {
+ silent_on_nonexisting_files = 1;
+ continue;
+ }
+ usage("show-diff [-s] [-r]");
}
if (entries < 0) {
@@ -83,7 +88,7 @@
if (stat(ce->name, &st) < 0) {
printf("%s: %s\n", ce->name, strerror(errno));
- if (errno == ENOENT && !silent)
+ if (errno == ENOENT && !silent_on_nonexisting_files)
show_diff_empty(ce);
continue;
}
diff -x .git -Nru ,,1/show-files.c ,,2/show-files.c
--- ,,1/show-files.c 2005-04-14 03:47:18.000000000 -0700
+++ ,,2/show-files.c 2005-04-14 04:00:14.000000000 -0700
@@ -14,6 +14,7 @@
static int show_cached = 0;
static int show_others = 0;
static int show_ignored = 0;
+static int line_terminator = '\n';
static const char **dir;
static int nr_dir;
@@ -105,12 +106,12 @@
}
if (show_others) {
for (i = 0; i < nr_dir; i++)
- printf("%s\n", dir[i]);
+ printf("%s%c", dir[i], line_terminator);
}
if (show_cached) {
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
- printf("%s\n", ce->name);
+ printf("%s%c", ce->name, line_terminator);
}
}
if (show_deleted) {
@@ -119,7 +120,7 @@
struct stat st;
if (!stat(ce->name, &st))
continue;
- printf("%s\n", ce->name);
+ printf("%s%c", ce->name, line_terminator);
}
}
if (show_ignored) {
@@ -134,6 +135,11 @@
for (i = 1; i < argc; i++) {
char *arg = argv[i];
+ if (!strcmp(arg, "-z")) {
+ line_terminator = 0;
+ continue;
+ }
+
if (!strcmp(arg, "--cached")) {
show_cached = 1;
continue;
diff -x .git -Nru ,,1/update-cache.c ,,2/update-cache.c
--- ,,1/update-cache.c 2005-04-14 03:47:18.000000000 -0700
+++ ,,2/update-cache.c 2005-04-14 04:00:14.000000000 -0700
@@ -250,6 +250,8 @@
{
int i, newfd, entries;
int allow_options = 1;
+ const char *sha1_force = NULL;
+ const char *mode_force = NULL;
newfd = open(".git/index.lock", O_RDWR | O_CREAT | O_EXCL, 0600);
if (newfd < 0)
@@ -282,14 +284,35 @@
refresh_cache();
continue;
}
+ if (!strcmp(path, "--cacheinfo")) {
+ mode_force = argv[++i];
+ sha1_force = argv[++i];
+ continue;
+ }
die("unknown option %s", path);
}
if (!verify_path(path)) {
fprintf(stderr, "Ignoring path %s\n", argv[i]);
continue;
}
- if (add_file_to_cache(path))
+ if (sha1_force && mode_force) {
+ struct cache_entry *ce;
+ int namelen = strlen(path);
+ int mode;
+ int size = cache_entry_size(namelen);
+ sscanf(mode_force, "%o", &mode);
+ ce = malloc(size);
+ memset(ce, 0, size);
+ memcpy(ce->name, path, namelen);
+ ce->namelen = namelen;
+ ce->st_mode = mode;
+ get_sha1_hex(sha1_force, ce->sha1);
+
+ add_cache_entry(ce, 1);
+ }
+ else if (add_file_to_cache(path))
die("Unable to add %s to database", path);
+ mode_force = sha1_force = NULL;
}
if (write_cache(newfd, active_cache, active_nr) ||
rename(".git/index.lock", ".git/index"))
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html