From: Junio C Hamano <junkio@cox.net>
To: Linus Torvalds <torvalds@osdl.org>
Cc: git@vger.kernel.org
Subject: [PATCH 1/2] merge-trees script for Linus git
Date: Fri, 15 Apr 2005 14:48:47 -0700 [thread overview]
Message-ID: <7vfyxrhfsw.fsf_-_@assigned-by-dhcp.cox.net> (raw)
In-Reply-To: <7vmzrzhkd3.fsf@assigned-by-dhcp.cox.net> (Junio C. Hamano's message of "Fri, 15 Apr 2005 13:10:16 -0700")
Linus,
what you have in 461aef08823a18a6c69d472499ef5257f8c7f6c8 is fine
by me for the essential support for merge-trees (sorry for the
confusing name, but this is a stop-gap Q&D script until I do the real
merge-tree.c conversion).
This patch contains the merge-trees script itself and Makefile entry
for it. I have some more fixes to merge-trees in the works but that
will follow later.
I have an optional patch to add '-q' option to show-diff so that
complaints for missing files can be squelched, which I will be sending
you in a separate message.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
Makefile | 2
merge-trees | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 303 insertions(+), 1 deletion(-)
Makefile: b39b4ea37586693dd707d1d0750a9b580350ec50
--- Makefile
+++ Makefile 2005-04-15 13:32:06.000000000 -0700
@@ -14,7 +14,7 @@
PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
- check-files ls-tree merge-tree
+ check-files ls-tree merge-tree merge-trees
all: $(PROG)
--- /dev/null 2005-03-19 15:28:25.000000000 -0800
+++ merge-trees 2005-04-15 13:32:20.000000000 -0700
@@ -0,0 +1,302 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Cwd;
+use Getopt::Long;
+
+my $full_checkout = 0;
+my $partial_checkout = 0;
+my $output_directory = ',,merge~tree';
+
+GetOptions("full-checkout" => \$full_checkout,
+ "partial-checkout" => \$partial_checkout,
+ "output-directory=s" => \$output_directory)
+ or die;
+
+
+if (@ARGV != 3) {
+ die "Usage: $0 -o [output-directory] [-f] [-p] ancestor A B\n";
+}
+
+if ($full_checkout) {
+ $partial_checkout = 1;
+}
+
+################################################################
+# UI helper -- although it is encouraged to give tree ID,
+# it is OK to give commit ID.
+sub possibly_commit_to_tree {
+ my ($commit_or_tree_id) = @_;
+ my $type = read_cat_file_t($commit_or_tree_id);
+ if ($type eq 'tree') { return $commit_or_tree_id }
+ if ($type ne 'commit') {
+ die "Tree ID (or commit ID) required, given $type.";
+ }
+
+ my ($fhi);
+ open $fhi, '-|', 'cat-file', 'commit', $commit_or_tree_id
+ or die "$!: cat-file commit $commit_or_tree_id";
+ my ($tree) = <$fhi>;
+ close $fhi;
+ ($tree =~ s/^tree (.*)$/$1/)
+ or die "$tree: Linus says the first line is guaranteed to be tree.";
+ return $tree;
+}
+
+sub read_cat_file_t {
+ my ($id) = @_;
+ my ($fhi);
+ open $fhi, '-|', 'cat-file', '-t', $id
+ or die "$!: cat-file -t $id";
+ my ($t) = <$fhi>;
+ close $fhi;
+ chomp($t);
+ return $t;
+}
+
+################################################################
+# Reads diff-tree -r output and gives a hash that maps a path
+# to 4-tuple (old-mode new-mode old-oid new-oid).
+# When creating, old-* are undef. When removing, new-* are undef.
+
+sub OLD_MODE () { 0 }
+sub NEW_MODE () { 1 }
+sub OLD_OID () { 2 }
+sub NEW_OID () { 3 }
+
+sub read_diff_tree {
+ my (@tree) = @_;
+ my ($fhi);
+
+ # Regular expression piece for mode
+ my $reM = '[0-7]+';
+
+ # Regular expression piece for object ID.
+ # There is a talk about base-64 so better make it easier to modify...
+ my $reID = '[0-9a-f]{40}';
+
+ local ($_, $/);
+ $/ = "\0";
+ my %path;
+ open $fhi, '-|', 'diff-tree', '-r', @tree
+ or die "$!: diff-tree -r @tree";
+ while (<$fhi>) {
+ chomp;
+ if (/^\*($reM)->($reM)\tblob\t($reID)->($reID)\t(.*)$/so) {
+ $path{$5} = [$1, $2, $3, $4]; # modified
+ }
+ elsif (/^\+($reM)\tblob\t($reID)\t(.*)$/so) {
+ $path{$3} = [undef, $1, undef, $2]; # added
+ }
+ elsif (/^\-($reM)\tblob\t($reID)\t(.*)$/so) {
+ $path{$3} = [$1, undef, $2, undef]; # deleted
+ }
+ else {
+ die "cannot parse diff-tree output: $_";
+ }
+ }
+ close $fhi;
+ return %path;
+}
+
+################################################################
+# Read show-files output to figure out the set of files contained
+# in the tree. This is used to figure out what ancestor had.
+sub read_show_files {
+ my ($fhi);
+ local ($_, $/);
+ $/ = "\0";
+ open $fhi, '-|', 'show-files', '-z', '--cached'
+ or die "$!: show-files -z --cached";
+ my (@path) = map { chomp; $_ } <$fhi>;
+ close $fhi;
+ return @path;
+}
+
+################################################################
+# Given path and info (typically returned from read_diff_tree),
+# create the file in the working directory to match the NEW tree.
+# This does not touch dircache.
+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->[NEW_OID]
+ or die "$!: cat-file blob $info->[NEW_OID]";
+ while (<$fhi>) {
+ print $fho $_;
+ }
+ close $fhi;
+ close $fho;
+ chmod oct("0$info->[NEW_MODE]"), "$path";
+}
+
+################################################################
+# Given path and info record the file in the dircache without
+# affecting working directory.
+sub record_file {
+ my ($path, $info) = @_;
+ system ('update-cache', '--add', '--cacheinfo',
+ $info->[NEW_MODE], $info->[NEW_OID], $path);
+}
+
+################################################################
+# Merge info from two trees and leave it in path, without
+# affecting dircache.
+sub merge_tree {
+ my ($path, $infoA, $infoB) = @_;
+ checkout_file("$path~A~", $infoA);
+ checkout_file("$path~B~", $infoB);
+ system 'checkout-cache', $path;
+ rename $path, "$path~O~";
+ my ($fhi, $fho);
+ open $fhi, '-|', 'merge', '-p', "$path~A~", "$path~O~", "$path~B~";
+ open $fho, '>', $path;
+ local ($/);
+ while (<$fhi>) { print $fho $_; }
+ close $fhi;
+ close $fho;
+ # There is no reason to prefer infoA over infoB but
+ # we need to pick one.
+ chmod oct("0$infoA->[NEW_MODE]"), $path;
+}
+
+################################################################
+
+# O stands for "the original". A and B are being merged.
+my ($treeO, $treeA, $treeB) = map { possibly_commit_to_tree $_ } @ARGV;
+
+# Create a temporary directory and go there.
+system('rm', '-rf', $output_directory) == 0 &&
+system('mkdir', '-p', "$output_directory/.git") == 0 &&
+symlink(Cwd::getcwd . "/.git/objects", "$output_directory/.git/objects") &&
+chdir $output_directory &&
+system('read-tree', $treeO) == 0
+ or die "$!: Failed to set up merge working area $output_directory";
+
+# Find out edits done in each branch.
+my %treeA = read_diff_tree($treeO, $treeA);
+my %treeB = read_diff_tree($treeO, $treeB);
+
+# The list of files that was in the ancestor.
+my @ancestor_file = read_show_files();
+my %ancestor_file = map { $_ => 1 } @ancestor_file;
+
+# Report output is formated as follows:
+#
+# The first letter shows the origin of the result.
+# O - original
+# A - treeA
+# B - treeB
+# M - both treeA and treeB
+# * - treeA and treeB conflicts; needs human action.
+#
+# The second and third letter shows what each tree did.
+# . - no change
+# A - created
+# M - modified
+# D - deleted
+
+for (@ancestor_file) {
+ if (! exists $treeA{$_} && ! exists $treeB{$_}) {
+ if ($full_checkout) {
+ system 'checkout-cache', $_;
+ }
+ print STDERR "O.. $_\n"; # keep original
+ }
+}
+
+for my $set ([\%treeA, \%treeB, 'A'], [\%treeB, \%treeA, 'B']) {
+ my ($this, $other, $side) = @$set;
+ my $delete_sign = ($side eq 'A') ? 'D.' : '.D';
+ my $create_sign = ($side eq 'A') ? 'A.' : '.A';
+ my $modify_sign = ($side eq 'A') ? 'M.' : '.M';
+ while (my ($path, $info) = each %$this) {
+ # In this loop we do not deal with overlaps.
+ next if (exists $other->{$path});
+
+ if (! defined $info->[NEW_OID]) {
+ # deleted in this tree only.
+ unlink $path;
+ system 'update-cache', '--remove', $path;
+ print STDERR "${side}${delete_sign} $path\n";
+ }
+ else {
+ # modified or created in this tree only.
+ my $create_or_modify =
+ (! defined $info->[OLD_OID]) ? $create_sign : $modify_sign;
+ print STDERR "${side}${create_or_modify} $path\n";
+ if ($partial_checkout) {
+ checkout_file($path, $info);
+ system 'update-cache', '--add', $path;
+ } else {
+ record_file($path, $info);
+ }
+ }
+ }
+}
+
+my @warning = ();
+
+while (my ($path, $infoA) = each %treeA) {
+ # We need to deal only with overlaps.
+ next if (!exists $treeB{$path});
+
+ my $infoB = $treeB{$path};
+ if (! defined $infoA->[NEW_OID]) {
+ # Deleted in tree A.
+ if (! defined $infoB->[NEW_OID]) {
+ # Deleted in both trees (obvious).
+ print STDERR "MDD $path\n";
+ unlink $path;
+ system 'update-cache', '--remove', $path;
+ }
+ else {
+ # TreeA wants to remove but TreeB wants to modify it.
+ print STDERR "*DM $path\n";
+ checkout_file("$path~B~", $infoB);
+ push @warning, $path;
+ }
+ }
+ else {
+ # Modified or created in tree A
+ if (! defined $infoB->[NEW_OID]) {
+ # TreeA wants to modify but treeB wants to remove it.
+ print STDERR "*MD $path\n";
+ checkout_file("$path~A~", $infoA);
+ push @warning, $path;
+ }
+ else {
+ # Modified both in treeA and treeB.
+ # Are they modifying to the same contents?
+ if ($infoA->[NEW_OID] eq $infoB->[NEW_OID]) {
+ # No changes or just the mode.
+ # we prefer TreeA over TreeB for no particular reason.
+ print STDERR "MMM $path\n";
+ record_file($path, $infoA);
+ }
+ else {
+ # Modified in both. Needs merge.
+ print STDERR "*MM $path\n";
+ merge_tree($path, $infoA, $infoB);
+ }
+ }
+ }
+}
+
+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', '-q';
next prev parent reply other threads:[~2005-04-15 21:46 UTC|newest]
Thread overview: 130+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-04-14 0:29 Merge with git-pasky II Petr Baudis
2005-04-13 21:25 ` Christopher Li
2005-04-14 0:45 ` Petr Baudis
2005-04-13 22:00 ` Christopher Li
2005-04-14 3:51 ` Linus Torvalds
2005-04-14 1:23 ` Christopher Li
2005-04-14 5:03 ` Paul Jackson
2005-04-14 2:16 ` Christopher Li
2005-04-14 6:16 ` Paul Jackson
2005-04-14 7:05 ` Junio C Hamano
2005-04-14 8:06 ` Linus Torvalds
2005-04-14 8:39 ` Junio C Hamano
2005-04-14 9:10 ` Linus Torvalds
2005-04-14 11:14 ` Junio C Hamano
2005-04-14 12:16 ` Petr Baudis
2005-04-14 18:12 ` Junio C Hamano
2005-04-14 18:36 ` Linus Torvalds
2005-04-14 19:59 ` Junio C Hamano
2005-04-14 20:20 ` Petr Baudis
2005-04-15 0:42 ` Linus Torvalds
2005-04-15 2:33 ` Barry Silverman
2005-04-15 10:02 ` David Woodhouse
2005-04-15 15:32 ` Linus Torvalds
2005-04-15 16:01 ` David Woodhouse
2005-04-15 16:31 ` C. Scott Ananian
2005-04-15 17:11 ` Linus Torvalds
2005-04-16 15:33 ` Johannes Schindelin
2005-04-17 13:14 ` David Woodhouse
2005-04-15 19:20 ` Paul Jackson
2005-04-16 1:44 ` Simon Fowler
2005-04-16 12:19 ` David Lang
2005-04-16 15:55 ` Simon Fowler
2005-04-16 16:03 ` Petr Baudis
2005-04-16 16:26 ` Simon Fowler
2005-04-16 16:26 ` Linus Torvalds
2005-04-16 23:02 ` David Lang
2005-04-17 14:52 ` Ingo Molnar
2005-04-17 15:08 ` Brad Roberts
2005-04-17 15:18 ` Ingo Molnar
2005-04-17 15:28 ` Ingo Molnar
2005-04-17 17:34 ` Linus Torvalds
2005-04-17 22:12 ` Herbert Xu
2005-04-17 22:35 ` Linus Torvalds
2005-04-17 23:29 ` Herbert Xu
2005-04-17 23:34 ` Petr Baudis
2005-04-17 23:53 ` Kenneth Johansson
2005-04-18 0:49 ` Herbert Xu
2005-04-18 0:55 ` Petr Baudis
2005-04-17 23:50 ` Linus Torvalds
2005-04-18 4:16 ` Sanjoy Mahajan
2005-04-18 7:42 ` Ingo Molnar
2005-04-16 20:29 ` Sanjoy Mahajan
2005-04-16 20:41 ` Linus Torvalds
2005-04-15 2:21 ` [Patch] ls-tree enhancements Junio C Hamano
2005-04-15 16:13 ` Petr Baudis
2005-04-15 18:25 ` Junio C Hamano
2005-04-15 9:14 ` Merge with git-pasky II David Woodhouse
2005-04-15 9:36 ` Ingo Molnar
2005-04-15 10:05 ` David Woodhouse
2005-04-15 14:53 ` Ingo Molnar
2005-04-15 15:09 ` David Woodhouse
2005-04-15 12:03 ` Johannes Schindelin
2005-04-15 10:22 ` Theodore Ts'o
2005-04-15 14:53 ` Linus Torvalds
2005-04-15 15:29 ` David Woodhouse
2005-04-15 15:51 ` Linus Torvalds
2005-04-15 15:54 ` Paul Jackson
2005-04-15 16:30 ` C. Scott Ananian
2005-04-15 18:29 ` Paul Jackson
2005-04-14 18:51 ` Christopher Li
2005-04-14 19:35 ` Petr Baudis
2005-04-14 20:01 ` Live Merging from remote repositories Barry Silverman
2005-04-14 23:22 ` Junio C Hamano
2005-04-15 1:07 ` Question about git process model Barry Silverman
2005-04-14 20:23 ` Re: Merge with git-pasky II Erik van Konijnenburg
2005-04-14 20:24 ` Petr Baudis
2005-04-14 23:12 ` Junio C Hamano
2005-04-14 20:24 ` Christopher Li
2005-04-14 23:31 ` Petr Baudis
2005-04-14 20:30 ` Christopher Li
2005-04-14 20:37 ` Christopher Li
2005-04-14 20:50 ` Christopher Li
2005-04-15 0:58 ` Junio C Hamano
2005-04-14 22:30 ` Christopher Li
2005-04-15 7:43 ` Junio C Hamano
2005-04-15 6:28 ` Christopher Li
2005-04-15 11:11 ` Junio C Hamano
[not found] ` <7vaco0i3t9.fsf_-_@assigned-by-dhcp.cox.net>
2005-04-15 18:44 ` write-tree is pasky-0.4 Linus Torvalds
2005-04-15 18:56 ` Petr Baudis
2005-04-15 20:13 ` Linus Torvalds
2005-04-15 22:36 ` Petr Baudis
2005-04-16 0:22 ` Linus Torvalds
2005-04-16 1:13 ` Daniel Barkalow
2005-04-16 2:18 ` Linus Torvalds
2005-04-16 2:49 ` Daniel Barkalow
2005-04-16 3:13 ` Linus Torvalds
2005-04-16 3:56 ` Daniel Barkalow
2005-04-16 6:59 ` Paul Jackson
2005-04-16 15:34 ` Re: Re: " Petr Baudis
2005-04-15 20:10 ` Junio C Hamano
2005-04-15 20:58 ` C. Scott Ananian
2005-04-15 21:22 ` Petr Baudis
2005-04-15 23:16 ` Junio C Hamano
2005-04-15 21:48 ` Junio C Hamano [this message]
2005-04-15 21:54 ` [PATCH 2/2] merge-trees script for Linus git Junio C Hamano
2005-04-15 23:33 ` [PATCH 3/2] " Junio C Hamano
2005-04-16 1:02 ` Linus Torvalds
2005-04-16 4:10 ` Junio C Hamano
2005-04-16 5:02 ` Linus Torvalds
2005-04-16 6:26 ` Linus Torvalds
2005-04-16 8:12 ` Junio C Hamano
2005-04-16 9:27 ` [PATCH] Byteorder fix for read-tree, new -m semantics version Junio C Hamano
2005-04-16 10:35 ` [PATCH 1/2] Add --stage to show-files for new stage dircache Junio C Hamano
2005-04-16 10:42 ` [PATCH 2/2] " Junio C Hamano
2005-04-16 14:03 ` Issues with higher-order stages in dircache Junio C Hamano
2005-04-17 5:11 ` Junio C Hamano
2005-04-17 5:31 ` Linus Torvalds
2005-04-17 6:01 ` Junio C Hamano
2005-04-17 10:00 ` Summary of "read-tree -m O A B" mechanism Junio C Hamano
2005-04-16 15:28 ` [PATCH 3/2] merge-trees script for Linus git Linus Torvalds
2005-04-16 16:36 ` Linus Torvalds
2005-04-16 17:14 ` Junio C Hamano
2005-04-15 19:54 ` Re: Merge with git-pasky II Petr Baudis
2005-04-15 10:22 ` Junio C Hamano
2005-04-15 20:40 ` Petr Baudis
2005-04-15 22:41 ` Junio C Hamano
2005-04-15 19:57 ` Junio C Hamano
2005-04-15 20:45 ` Linus Torvalds
2005-04-14 0:30 ` Petr Baudis
2005-04-14 22:11 ` git merge Petr Baudis
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
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=7vfyxrhfsw.fsf_-_@assigned-by-dhcp.cox.net \
--to=junkio@cox.net \
--cc=git@vger.kernel.org \
--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.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
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 read-only IMAP folder(s) and NNTP newsgroup(s).