* [GIT PULL] Please pull mergetool.git
@ 2007-03-14 1:15 Theodore Ts'o
2007-03-14 5:59 ` Shawn O. Pearce
2007-03-14 9:45 ` Junio C Hamano
0 siblings, 2 replies; 6+ messages in thread
From: Theodore Ts'o @ 2007-03-14 1:15 UTC (permalink / raw
To: Junio C Hamano; +Cc: git
Hi Junio,
Per our previous email conversation, I've cleaned done my final
cleanups before being ready to submit mergetool for inclusion, and
rebased it to lastest changeset the git master branch.
In order to make this easier for me to send future features and
cleanups, I've set up a repository on repo.or.cz. So please pull from:
git://repo.or.cz/git/mergetool.git
The final patch that is the repository is attached.
- Ted
From: Theodore Ts'o <tytso@mit.edu>
Date: Tue, 6 Mar 2007 00:05:16 -0500
Subject: [PATCH] Add git-mergetool to run an appropriate merge conflict resolution program
The git-mergetool program can be used to automatically run an appropriate
merge resolution program to resolve merge conflicts. It will automatically
run one of kdiff3, tkdiff, meld, xxdiff, or emacs emerge programs.
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
---
.gitignore | 1 +
Documentation/config.txt | 5 +
Documentation/git-mergetool.txt | 46 +++++
Makefile | 2 +-
git-mergetool.sh | 352 +++++++++++++++++++++++++++++++++++++++
5 files changed, 405 insertions(+), 1 deletions(-)
create mode 100644 Documentation/git-mergetool.txt
create mode 100755 git-mergetool.sh
diff --git a/.gitignore b/.gitignore
index 0eaba0a..27797d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,6 +75,7 @@ git-merge-ours
git-merge-recursive
git-merge-resolve
git-merge-stupid
+git-mergetool
git-mktag
git-mktree
git-name-rev
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 5408dd6..aaae9ac 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -453,6 +453,11 @@ merge.summary::
Whether to include summaries of merged commits in newly created
merge commit messages. False by default.
+merge.tool::
+ Controls which merge resolution program is used by
+ gitlink:git-mergetool[l]. Valid values are: "kdiff3", "tkdiff",
+ "meld", "xxdiff", "emerge"
+
merge.verbosity::
Controls the amount of output shown by the recursive merge
strategy. Level 0 outputs nothing except a final error
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
new file mode 100644
index 0000000..ae69a0e
--- /dev/null
+++ b/Documentation/git-mergetool.txt
@@ -0,0 +1,46 @@
+git-mergetool(1)
+================
+
+NAME
+----
+git-mergetool - Run merge conflict resolution tools to resolve merge conflicts
+
+SYNOPSIS
+--------
+'git-mergetool' [--tool=<tool>] [<file>]...
+
+DESCRIPTION
+-----------
+
+Use 'git mergetool' to run one of several merge utilities to resolve
+merge conflicts. It is typically run after gitlink:git-merge[1].
+
+If one or more <file> parameters are given, the merge tool program will
+be run to resolve differences on each file. If no <file> names are
+specified, 'git mergetool' will run the merge tool program on every file
+with merge conflicts.
+
+OPTIONS
+-------
+-t or --tool=<tool>::
+ Use the merge resolution program specified by <tool>.
+ Valid merge tools are:
+ kdiff3, tkdiff, meld, xxdiff, and emerge.
+
+ If a merge resolution program is not specified, 'git mergetool'
+ will use the configuration variable merge.tool. If the
+ configuration variable merge.tool is not set, 'git mergetool'
+ will pick a suitable default.
+
+Author
+------
+Written by Theodore Y Ts'o <tytso@mit.edu>
+
+Documentation
+--------------
+Documentation by Theodore Y Ts'o.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
diff --git a/Makefile b/Makefile
index 1a51f84..45fe366 100644
--- a/Makefile
+++ b/Makefile
@@ -179,7 +179,7 @@ SCRIPT_SH = \
git-clean.sh git-clone.sh git-commit.sh \
git-fetch.sh git-gc.sh \
git-ls-remote.sh \
- git-merge-one-file.sh git-parse-remote.sh \
+ git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
git-pull.sh git-rebase.sh \
git-repack.sh git-request-pull.sh git-reset.sh \
git-sh-setup.sh \
diff --git a/git-mergetool.sh b/git-mergetool.sh
new file mode 100755
index 0000000..52386a5
--- /dev/null
+++ b/git-mergetool.sh
@@ -0,0 +1,352 @@
+#!/bin/sh
+#
+# This program resolves merge conflicts in git
+#
+# Copyright (c) 2006 Theodore Y. Ts'o
+#
+# This file is licensed under the GPL v2, or a later version
+# at the discretion of Junio C Hammano.
+#
+
+USAGE='[--tool=tool] [file to merge] ...'
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+require_work_tree
+
+# Returns true if the mode reflects a symlink
+function is_symlink () {
+ test "$1" = 120000
+}
+
+function local_present () {
+ test -n "$local_mode"
+}
+
+function remote_present () {
+ test -n "$remote_mode"
+}
+
+function base_present () {
+ test -n "$base_mode"
+}
+
+cleanup_temp_files () {
+ if test "$1" = --save-backup ; then
+ mv -- "$BACKUP" "$path.orig"
+ rm -f -- "$LOCAL" "$REMOTE" "$BASE"
+ else
+ rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
+ fi
+}
+
+function describe_file () {
+ mode="$1"
+ branch="$2"
+ file="$3"
+
+ echo -n " "
+ if test -z "$mode"; then
+ echo -n "'$path' was deleted"
+ elif is_symlink "$mode" ; then
+ echo -n "'$path' is a symlink containing '"
+ cat "$file"
+ echo -n "'"
+ else
+ if base_present; then
+ echo -n "'$path' was created"
+ else
+ echo -n "'$path' was modified"
+ fi
+ fi
+ echo " in the $branch branch"
+}
+
+
+resolve_symlink_merge () {
+ while /bin/true; do
+ echo -n "Use (r)emote or (l)ocal, or (a)bort? "
+ read ans
+ case "$ans" in
+ [lL]*)
+ git-checkout-index -f --stage=2 -- "$path"
+ git-add -- "$path"
+ cleanup_temp_files --save-backup
+ return
+ ;;
+ [rR]*)
+ git-checkout-index -f --stage=3 -- "$path"
+ git-add -- "$path"
+ cleanup_temp_files --save-backup
+ return
+ ;;
+ [qQ]*)
+ exit 1
+ ;;
+ esac
+ done
+}
+
+resolve_deleted_merge () {
+ while /bin/true; do
+ echo -n "Use (m)odified or (d)eleted file, or (a)bort? "
+ read ans
+ case "$ans" in
+ [mM]*)
+ git-add -- "$path"
+ cleanup_temp_files --save-backup
+ return
+ ;;
+ [dD]*)
+ git-rm -- "$path"
+ cleanup_temp_files
+ return
+ ;;
+ [qQ]*)
+ exit 1
+ ;;
+ esac
+ done
+}
+
+merge_file () {
+ path="$1"
+
+ if test ! -f "$path" ; then
+ echo "$path: file not found"
+ exit 1
+ fi
+
+ f=`git-ls-files -u -- "$path"`
+ if test -z "$f" ; then
+ echo "$path: file does not need merging"
+ exit 1
+ fi
+
+ BACKUP="$path.BACKUP.$$"
+ LOCAL="$path.LOCAL.$$"
+ REMOTE="$path.REMOTE.$$"
+ BASE="$path.BASE.$$"
+
+ mv -- "$path" "$BACKUP"
+ cp -- "$BACKUP" "$path"
+
+ base_mode=`git ls-files -u -- "$path" | awk '{if ($3==1) print $1;}'`
+ local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'`
+ remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'`
+
+ base_present && git cat-file blob ":1:$path" > "$BASE" 2>/dev/null
+ local_present && git cat-file blob ":2:$path" > "$LOCAL" 2>/dev/null
+ remote_present && git cat-file blob ":3:$path" > "$REMOTE" 2>/dev/null
+
+ if test -z "$local_mode" -o -z "$remote_mode"; then
+ echo "Deleted merge conflict for $path:"
+ describe_file "$local_mode" "local" "$LOCAL"
+ describe_file "$remote_mode" "remote" "$REMOTE"
+ resolve_deleted_merge
+ return
+ fi
+
+ if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
+ echo "Symlink merge conflict for $path:"
+ describe_file "$local_mode" "local" "$LOCAL"
+ describe_file "$remote_mode" "remote" "$REMOTE"
+ resolve_symlink_merge
+ return
+ fi
+
+ echo "Normal merge conflict for $path:"
+ describe_file "$local_mode" "local" "$LOCAL"
+ describe_file "$remote_mode" "remote" "$REMOTE"
+ echo -n "Hit return to start merge resolution tool ($merge_tool): "
+ read ans
+
+ case "$merge_tool" in
+ kdiff3)
+ if base_present ; then
+ (kdiff3 --auto --L1 "$path (Base)" -L2 "$path (Local)" --L3 "$path (Remote)" \
+ -o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+ else
+ (kdiff3 --auto -L1 "$path (Local)" --L2 "$path (Remote)" \
+ -o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+ fi
+ status=$?
+ if test "$status" -eq 0; then
+ rm "$BACKUP"
+ fi
+ ;;
+ tkdiff)
+ if base_present ; then
+ tkdiff -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
+ else
+ tkdiff -o "$path" -- "$LOCAL" "$REMOTE"
+ fi
+ status=$?
+ if test "$status" -eq 0; then
+ mv -- "$BACKUP" "$path.orig"
+ fi
+ ;;
+ meld)
+ touch "$BACKUP"
+ meld -- "$LOCAL" "$path" "$REMOTE"
+ if test "$path" -nt "$BACKUP" ; then
+ status=0;
+ else
+ while true; do
+ echo "$path seems unchanged."
+ echo -n "Was the merge successful? [y/n] "
+ read answer < /dev/tty
+ case "$answer" in
+ y*|Y*) status=0; break ;;
+ n*|N*) status=1; break ;;
+ esac
+ done
+ fi
+ if test "$status" -eq 0; then
+ mv -- "$BACKUP" "$path.orig"
+ fi
+ ;;
+ xxdiff)
+ touch "$BACKUP"
+ if base_present ; then
+ xxdiff -X --show-merged-pane \
+ -R 'Accel.SaveAsMerged: "Ctrl-S"' \
+ -R 'Accel.Search: "Ctrl+F"' \
+ -R 'Accel.SearchForward: "Ctrl-G"' \
+ --merged-file "$path" -- "$LOCAL" "$BASE" "$REMOTE"
+ else
+ xxdiff -X --show-merged-pane \
+ -R 'Accel.SaveAsMerged: "Ctrl-S"' \
+ -R 'Accel.Search: "Ctrl+F"' \
+ -R 'Accel.SearchForward: "Ctrl-G"' \
+ --merged-file "$path" -- "$LOCAL" "$REMOTE"
+ fi
+ if test "$path" -nt "$BACKUP" ; then
+ status=0;
+ else
+ while true; do
+ echo "$path seems unchanged."
+ echo -n "Was the merge successful? [y/n] "
+ read answer < /dev/tty
+ case "$answer" in
+ y*|Y*) status=0; break ;;
+ n*|N*) status=1; break ;;
+ esac
+ done
+ fi
+ if test "$status" -eq 0; then
+ mv -- "$BACKUP" "$path.orig"
+ fi
+ ;;
+ emerge)
+ if base_present ; then
+ emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$path"
+ else
+ emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$path"
+ fi
+ status=$?
+ if test "$status" -eq 0; then
+ mv -- "$BACKUP" "$path.orig"
+ fi
+ ;;
+ esac
+ if test "$status" -ne 0; then
+ echo "merge of $path failed" 1>&2
+ mv -- "$BACKUP" "$path"
+ exit 1
+ fi
+ git add -- "$path"
+ cleanup_temp_files
+}
+
+while case $# in 0) break ;; esac
+do
+ case "$1" in
+ -t|--tool*)
+ case "$#,$1" in
+ *,*=*)
+ merge_tool=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+ ;;
+ 1,*)
+ usage ;;
+ *)
+ merge_tool="$2"
+ shift ;;
+ esac
+ ;;
+ --)
+ break
+ ;;
+ -*)
+ usage
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+
+if test -z "$merge_tool"; then
+ merge_tool=`git-config merge.tool`
+ if test $merge_tool = kdiff3 -o $merge_tool = tkdiff -o \
+ $merge_tool = xxdiff -o $merge_tool = meld ; then
+ unset merge_tool
+ fi
+fi
+
+if test -z "$merge_tool" ; then
+ if type kdiff3 >/dev/null 2>&1 && test -n "$DISPLAY"; then
+ merge_tool="kdiff3";
+ elif type tkdiff >/dev/null 2>&1 && test -n "$DISPLAY"; then
+ merge_tool=tkdiff
+ elif type xxdiff >/dev/null 2>&1 && test -n "$DISPLAY"; then
+ merge_tool=xxdiff
+ elif type meld >/dev/null 2>&1 && test -n "$DISPLAY"; then
+ merge_tool=meld
+ elif type emacs >/dev/null 2>&1; then
+ merge_tool=emerge
+ else
+ echo "No available merge resolution programs available."
+ exit 1
+ fi
+fi
+
+case "$merge_tool" in
+ kdiff3|tkdiff|meld|xxdiff)
+ if ! type "$merge_tool" > /dev/null 2>&1; then
+ echo "The merge tool $merge_tool is not available"
+ exit 1
+ fi
+ ;;
+ emerge)
+ if ! type "emacs" > /dev/null 2>&1; then
+ echo "Emacs is not available"
+ exit 1
+ fi
+ ;;
+ *)
+ echo "Unknown merge tool: $merge_tool"
+ exit 1
+ ;;
+esac
+
+if test $# -eq 0 ; then
+ files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u`
+ if test -z "$files" ; then
+ echo "No files need merging"
+ exit 0
+ fi
+ echo Merging the files: $files
+ git ls-files -u | sed -e 's/^[^ ]* //' | sort -u | while read i
+ do
+ echo ""
+ merge_file "$i" < /dev/tty > /dev/tty
+ done
+else
+ while test $# -gt 0; do
+ echo ""
+ merge_file "$1"
+ shift
+ done
+fi
+exit 0
--
1.5.0.3.272.gb515-dirty
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [GIT PULL] Please pull mergetool.git
2007-03-14 1:15 [GIT PULL] Please pull mergetool.git Theodore Ts'o
@ 2007-03-14 5:59 ` Shawn O. Pearce
2007-03-14 6:17 ` Theodore Tso
2007-03-14 7:03 ` Junio C Hamano
2007-03-14 9:45 ` Junio C Hamano
1 sibling, 2 replies; 6+ messages in thread
From: Shawn O. Pearce @ 2007-03-14 5:59 UTC (permalink / raw
To: Theodore Ts'o; +Cc: Junio C Hamano, git
Theodore Ts'o <tytso@mit.edu> wrote:
> + base_mode=`git ls-files -u -- "$path" | awk '{if ($3==1) print $1;}'`
> + local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'`
> + remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'`
> +
> + base_present && git cat-file blob ":1:$path" > "$BASE" 2>/dev/null
> + local_present && git cat-file blob ":2:$path" > "$LOCAL" 2>/dev/null
> + remote_present && git cat-file blob ":3:$path" > "$REMOTE" 2>/dev/null
Why not use `git checkout-index --stage=all "$path"` ?
E.g.:
git checkout-index --stage=all "$path" |
read base_temp local_temp remote_temp path
I'm not trying to nitpick, I'm just curious about why this particular
feature of checkout-index was not useful here.
--
Shawn.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [GIT PULL] Please pull mergetool.git
2007-03-14 5:59 ` Shawn O. Pearce
@ 2007-03-14 6:17 ` Theodore Tso
2007-03-14 7:03 ` Junio C Hamano
1 sibling, 0 replies; 6+ messages in thread
From: Theodore Tso @ 2007-03-14 6:17 UTC (permalink / raw
To: Shawn O. Pearce; +Cc: Junio C Hamano, git
On Wed, Mar 14, 2007 at 01:59:23AM -0400, Shawn O. Pearce wrote:
> Theodore Ts'o <tytso@mit.edu> wrote:
> > + base_mode=`git ls-files -u -- "$path" | awk '{if ($3==1) print $1;}'`
> > + local_mode=`git ls-files -u -- "$path" | awk '{if ($3==2) print $1;}'`
> > + remote_mode=`git ls-files -u -- "$path" | awk '{if ($3==3) print $1;}'`
> > +
> > + base_present && git cat-file blob ":1:$path" > "$BASE" 2>/dev/null
> > + local_present && git cat-file blob ":2:$path" > "$LOCAL" 2>/dev/null
> > + remote_present && git cat-file blob ":3:$path" > "$REMOTE" 2>/dev/null
>
> Why not use `git checkout-index --stage=all "$path"` ?
> E.g.:
>
> git checkout-index --stage=all "$path" |
> read base_temp local_temp remote_temp path
>
> I'm not trying to nitpick, I'm just curious about why this particular
> feature of checkout-index was not useful here.
1) I didn't know about it.
2) If I used it would I have to have renamed the files to
<path>.LOCAL, <path>.BASE, et.al, because with most of the graphical
merge tools, the filename is the only thing which gets displayed to
tell the user which file came from the local branch or the remote
branch or the base revision --- since file names such as
.merge_file_QBaxrn and .merge_file_prSEqs don't have a lot of human
meaning.....
So I don't know that it would havce saved much in the script. You
replace three invocations to git-cat-file with one invocation to
git-checkout-index plus three invocations to mv.
Regards,
- Ted
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [GIT PULL] Please pull mergetool.git
2007-03-14 5:59 ` Shawn O. Pearce
2007-03-14 6:17 ` Theodore Tso
@ 2007-03-14 7:03 ` Junio C Hamano
1 sibling, 0 replies; 6+ messages in thread
From: Junio C Hamano @ 2007-03-14 7:03 UTC (permalink / raw
To: Shawn O. Pearce; +Cc: Theodore Ts'o, git
"Shawn O. Pearce" <spearce@spearce.org> writes:
> Why not use `git checkout-index --stage=all "$path"` ?
> E.g.:
>
> git checkout-index --stage=all "$path" |
> read base_temp local_temp remote_temp path
$ echo a b c d | read a b c d; echo "<$a> <$b> <$c> <$d>"
<> <> <> <>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [GIT PULL] Please pull mergetool.git
2007-03-14 1:15 [GIT PULL] Please pull mergetool.git Theodore Ts'o
2007-03-14 5:59 ` Shawn O. Pearce
@ 2007-03-14 9:45 ` Junio C Hamano
2007-03-14 9:55 ` Alex Riesen
1 sibling, 1 reply; 6+ messages in thread
From: Junio C Hamano @ 2007-03-14 9:45 UTC (permalink / raw
To: Theodore Ts'o; +Cc: git
"Theodore Ts'o" <tytso@mit.edu> writes:
> In order to make this easier for me to send future features and
> cleanups, I've set up a repository on repo.or.cz. So please pull from:
>
> git://repo.or.cz/git/mergetool.git
>
> The final patch that is the repository is attached.
Thanks, pulled.
There should be a checklist for adding a new user-level command.
I've been reluctant to write this up, partly because some people
seem to be disturbed by seeing too many git-blah commands in
their distro's /usr/bin/ and I've been avoiding introducing new
commands (I even rejected "git-diff2", which I liked what it
did), but that is a lame excuse.
So here it is.
- An .gitignore entry to ignore the build product.
- An Makefile entry to build and clean.
- For scripts, add git-foo.sh to SCRIPT_SH (or
git-foo.perl to SCRIPT_PERL).
- For a new built-in 'git-foo' that is produced from
builtin-foo.c, add builtin-foo.o to BUILTIN_OBJS.
- For a new standalone 'git-foo' whose main is in foo.c, add
git-foo$X to PROGRAMS.
- (these are not limited to a new program) If you are adding
a library object 'foo.o', add it to LIB_OBJS; add new
headers to to LIB_H. For the latter, sorry but our
dependencies are a bit too eager to recompile.
- Asciidoc manual in Documentation/git-foo.txt to be formatted
to both HTML and man.
- An entry in Documentation/cmd-list.perl (the list at the end
of the script). This is used to link the command from the
main documentation git(7) and git.html.
The last one you missed but that is not a big deal. I'll add an
extra commit to add one entry to classify this as an ancillary
manipulator.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [GIT PULL] Please pull mergetool.git
2007-03-14 9:45 ` Junio C Hamano
@ 2007-03-14 9:55 ` Alex Riesen
0 siblings, 0 replies; 6+ messages in thread
From: Alex Riesen @ 2007-03-14 9:55 UTC (permalink / raw
To: Junio C Hamano; +Cc: Theodore Ts'o, git
On 3/14/07, Junio C Hamano <junkio@cox.net> wrote:
> So here it is.
>
> - An .gitignore entry to ignore the build product.
>
> - An Makefile entry to build and clean.
>
- If the "product" has own Makefile: QUIET support (aka "V=x")
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2007-03-14 9:56 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-03-14 1:15 [GIT PULL] Please pull mergetool.git Theodore Ts'o
2007-03-14 5:59 ` Shawn O. Pearce
2007-03-14 6:17 ` Theodore Tso
2007-03-14 7:03 ` Junio C Hamano
2007-03-14 9:45 ` Junio C Hamano
2007-03-14 9:55 ` Alex Riesen
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).