Code

Merge branch 'dc/format-pretty'
authorJunio C Hamano <gitster@pobox.com>
Sat, 8 Mar 2008 06:33:26 +0000 (22:33 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 8 Mar 2008 06:33:26 +0000 (22:33 -0800)
* dc/format-pretty:
  log/show/whatchanged: introduce format.pretty configuration
  specify explicit "--pretty=medium" with `git log/show/whatchanged`
  whatchanged documentation: share description of --pretty with others

Documentation/config.txt
Documentation/technical/api-run-command.txt
Makefile
contrib/completion/git-completion.bash
git-compat-util.h
git-mergetool.sh
path.c
run-command.c
t/t7610-mergetool.sh [new file with mode: 0644]

index 8a0dff9f4949662f0a7f03e55fadf6f40597cde4..683a53a0be27fda7ab90b02e34faddc20dd8b460 100644 (file)
@@ -754,8 +754,10 @@ merge.summary::
 
 merge.tool::
        Controls which merge resolution program is used by
-       linkgit:git-mergetool[1].  Valid values are: "kdiff3", "tkdiff",
-       "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff".
+       linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
+       "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
+       "opendiff".  Any other value is treated is custom merge tool
+       and there must be a corresponing mergetool.<tool>.cmd option.
 
 merge.verbosity::
        Controls the amount of output shown by the recursive merge
@@ -782,6 +784,31 @@ mergetool.<tool>.path::
        Override the path for the given tool.  This is useful in case
        your tool is not in the PATH.
 
+mergetool.<tool>.cmd::
+       Specify the command to invoke the specified merge tool.  The
+       specified command is evaluated in shell with the following
+       variables available: 'BASE' is the name of a temporary file
+       containing the common base of the files to be merged, if available;
+       'LOCAL' is the name of a temporary file containing the contents of
+       the file on the current branch; 'REMOTE' is the name of a temporary
+       file containing the contents of the file from the branch being
+       merged; 'MERGED' contains the name of the file to which the merge
+       tool should write the results of a successful merge.
+
+mergetool.<tool>.trustExitCode::
+       For a custom merge command, specify whether the exit code of
+       the merge command can be used to determine whether the merge was
+       successful.  If this is not set to true then the merge target file
+       timestamp is checked and the merge assumed to have been successful
+       if the file has been updated, otherwise the user is prompted to
+       indicate the success of the merge.
+
+mergetool.keepBackup::
+       After performing a merge, the original file with conflict markers
+       can be saved as a file with a `.orig` extension.  If this variable
+       is set to `false` then this file is not preserved.  Defaults to
+       `true` (i.e. keep the backup files).
+
 pack.window::
        The size of the window used by linkgit:git-pack-objects[1] when no
        window size is given on the command line. Defaults to 10.
index fde3b453211030b7256ccedd100970ef712338bd..c364a22c8f98f6fbb71d4059f7458ed0fa78ed43 100644 (file)
@@ -111,9 +111,10 @@ stderr as follows:
        .no_stdin, .no_stdout, .no_stderr: The respective channel is
                redirected to /dev/null.
 
-       .stdout_to_stderr: stdout of the child is redirected to the
-               parent's stderr (i.e. *not* to what .err or
-               .no_stderr specify).
+       .stdout_to_stderr: stdout of the child is redirected to its
+               stderr. This happens after stderr is itself redirected.
+               So stdout will follow stderr to wherever it is
+               redirected.
 
 To modify the environment of the sub-process, specify an array of
 string pointers (NULL terminated) in .env:
index ca5aad963ca7b5565338f49ce1705c451216c8be..6e857e62c3d02fa2bfef7b9224e24d5b6a279c9c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -478,6 +478,7 @@ ifeq ($(uname_S),FreeBSD)
        NO_MEMMEM = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
+       DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
@@ -747,6 +748,9 @@ ifdef THREADED_DELTA_SEARCH
        EXTLIBS += -lpthread
        LIB_OBJS += thread-utils.o
 endif
+ifdef DIR_HAS_BSD_GROUP_SEMANTICS
+       COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
+endif
 
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
index 8f70e1efc11553ce2360fab8693e2749a3fc36b4..49e6df09654f061d674f18ffcd6347f82ea644fa 100755 (executable)
@@ -506,7 +506,33 @@ _git_bisect ()
 
 _git_branch ()
 {
-       __gitcomp "$(__git_refs)"
+       local i c=1 only_local_ref="n" has_r="n"
+
+       while [ $c -lt $COMP_CWORD ]; do
+               i="${COMP_WORDS[c]}"
+               case "$i" in
+               -d|-m)  only_local_ref="y" ;;
+               -r)     has_r="y" ;;
+               esac
+               c=$((++c))
+       done
+
+       case "${COMP_WORDS[COMP_CWORD]}" in
+       --*=*)  COMPREPLY=() ;;
+       --*)
+               __gitcomp "
+                       --color --no-color --verbose --abbrev= --no-abbrev
+                       --track --no-track
+                       "
+               ;;
+       *)
+               if [ $only_local_ref = "y" -a $has_r = "n" ]; then
+                       __gitcomp "$(__git_heads)"
+               else
+                       __gitcomp "$(__git_refs)"
+               fi
+               ;;
+       esac
 }
 
 _git_bundle ()
index 2a40703c851d7011723588dd7643dc034db49c31..591244351ec4e410ea3b7f891d0de2f33a5aff7a 100644 (file)
@@ -437,4 +437,10 @@ void git_qsort(void *base, size_t nmemb, size_t size,
 #define qsort git_qsort
 #endif
 
+#ifndef DIR_HAS_BSD_GROUP_SEMANTICS
+# define FORCE_DIR_SET_GID S_ISGID
+#else
+# define FORCE_DIR_SET_GID 0
+#endif
+
 #endif
index cbbb707959cc64427f7bbd7bfefb0a8f0f263596..5c86f69229042c16704d11ce404e43297cc7b72c 100755 (executable)
@@ -34,7 +34,7 @@ base_present () {
 
 cleanup_temp_files () {
     if test "$1" = --save-backup ; then
-       mv -- "$BACKUP" "$path.orig"
+       mv -- "$BACKUP" "$MERGED.orig"
        rm -f -- "$LOCAL" "$REMOTE" "$BASE"
     else
        rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
@@ -67,14 +67,14 @@ resolve_symlink_merge () {
        read ans
        case "$ans" in
            [lL]*)
-               git checkout-index -f --stage=2 -- "$path"
-               git add -- "$path"
+               git checkout-index -f --stage=2 -- "$MERGED"
+               git add -- "$MERGED"
                cleanup_temp_files --save-backup
                return
                ;;
            [rR]*)
-               git checkout-index -f --stage=3 -- "$path"
-               git add -- "$path"
+               git checkout-index -f --stage=3 -- "$MERGED"
+               git add -- "$MERGED"
                cleanup_temp_files --save-backup
                return
                ;;
@@ -95,12 +95,12 @@ resolve_deleted_merge () {
        read ans
        case "$ans" in
            [mMcC]*)
-               git add -- "$path"
+               git add -- "$MERGED"
                cleanup_temp_files --save-backup
                return
                ;;
            [dD]*)
-               git rm -- "$path" > /dev/null
+               git rm -- "$MERGED" > /dev/null
                cleanup_temp_files
                return
                ;;
@@ -112,11 +112,11 @@ resolve_deleted_merge () {
 }
 
 check_unchanged () {
-    if test "$path" -nt "$BACKUP" ; then
+    if test "$MERGED" -nt "$BACKUP" ; then
        status=0;
     else
        while true; do
-           echo "$path seems unchanged."
+           echo "$MERGED seems unchanged."
            printf "Was the merge successful? [y/n] "
            read answer < /dev/tty
            case "$answer" in
@@ -127,50 +127,38 @@ check_unchanged () {
     fi
 }
 
-save_backup () {
-    if test "$status" -eq 0; then
-       mv -- "$BACKUP" "$path.orig"
-    fi
-}
-
-remove_backup () {
-    if test "$status" -eq 0; then
-       rm "$BACKUP"
-    fi
-}
-
 merge_file () {
-    path="$1"
+    MERGED="$1"
 
-    f=`git ls-files -u -- "$path"`
+    f=`git ls-files -u -- "$MERGED"`
     if test -z "$f" ; then
-       if test ! -f "$path" ; then
-           echo "$path: file not found"
+       if test ! -f "$MERGED" ; then
+           echo "$MERGED: file not found"
        else
-           echo "$path: file does not need merging"
+           echo "$MERGED: file does not need merging"
        fi
        exit 1
     fi
 
-    ext="$$$(expr "$path" : '.*\(\.[^/]*\)$')"
-    BACKUP="$path.BACKUP.$ext"
-    LOCAL="$path.LOCAL.$ext"
-    REMOTE="$path.REMOTE.$ext"
-    BASE="$path.BASE.$ext"
+    ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
+    BACKUP="$MERGED.BACKUP.$ext"
+    LOCAL="$MERGED.LOCAL.$ext"
+    REMOTE="$MERGED.REMOTE.$ext"
+    BASE="$MERGED.BASE.$ext"
 
-    mv -- "$path" "$BACKUP"
-    cp -- "$BACKUP" "$path"
+    mv -- "$MERGED" "$BACKUP"
+    cp -- "$BACKUP" "$MERGED"
 
-    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_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}'`
+    local_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}'`
+    remote_mode=`git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}'`
 
-    base_present   && git cat-file blob ":1:$prefix$path" >"$BASE" 2>/dev/null
-    local_present  && git cat-file blob ":2:$prefix$path" >"$LOCAL" 2>/dev/null
-    remote_present && git cat-file blob ":3:$prefix$path" >"$REMOTE" 2>/dev/null
+    base_present   && git cat-file blob ":1:$prefix$MERGED" >"$BASE" 2>/dev/null
+    local_present  && git cat-file blob ":2:$prefix$MERGED" >"$LOCAL" 2>/dev/null
+    remote_present && git cat-file blob ":3:$prefix$MERGED" >"$REMOTE" 2>/dev/null
 
     if test -z "$local_mode" -o -z "$remote_mode"; then
-       echo "Deleted merge conflict for '$path':"
+       echo "Deleted merge conflict for '$MERGED':"
        describe_file "$local_mode" "local" "$LOCAL"
        describe_file "$remote_mode" "remote" "$REMOTE"
        resolve_deleted_merge
@@ -178,14 +166,14 @@ merge_file () {
     fi
 
     if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
-       echo "Symbolic link merge conflict for '$path':"
+       echo "Symbolic link merge conflict for '$MERGED':"
        describe_file "$local_mode" "local" "$LOCAL"
        describe_file "$remote_mode" "remote" "$REMOTE"
        resolve_symlink_merge
        return
     fi
 
-    echo "Normal merge conflict for '$path':"
+    echo "Normal merge conflict for '$MERGED':"
     describe_file "$local_mode" "local" "$LOCAL"
     describe_file "$remote_mode" "remote" "$REMOTE"
     printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
@@ -194,36 +182,32 @@ merge_file () {
     case "$merge_tool" in
        kdiff3)
            if base_present ; then
-               ("$merge_tool_path" --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \
-                   -o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+               ("$merge_tool_path" --auto --L1 "$MERGED (Base)" --L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" \
+                   -o "$MERGED" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1)
            else
-               ("$merge_tool_path" --auto --L1 "$path (Local)" --L2 "$path (Remote)" \
-                   -o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
+               ("$merge_tool_path" --auto --L1 "$MERGED (Local)" --L2 "$MERGED (Remote)" \
+                   -o "$MERGED" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
            fi
            status=$?
-           remove_backup
            ;;
        tkdiff)
            if base_present ; then
-               "$merge_tool_path" -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE"
+               "$merge_tool_path" -a "$BASE" -o "$MERGED" -- "$LOCAL" "$REMOTE"
            else
-               "$merge_tool_path" -o "$path" -- "$LOCAL" "$REMOTE"
+               "$merge_tool_path" -o "$MERGED" -- "$LOCAL" "$REMOTE"
            fi
            status=$?
-           save_backup
            ;;
        meld|vimdiff)
            touch "$BACKUP"
-           "$merge_tool_path" -- "$LOCAL" "$path" "$REMOTE"
+           "$merge_tool_path" -- "$LOCAL" "$MERGED" "$REMOTE"
            check_unchanged
-           save_backup
            ;;
        gvimdiff)
-               touch "$BACKUP"
-               "$merge_tool_path" -f -- "$LOCAL" "$path" "$REMOTE"
-               check_unchanged
-               save_backup
-               ;;
+           touch "$BACKUP"
+           "$merge_tool_path" -f -- "$LOCAL" "$MERGED" "$REMOTE"
+           check_unchanged
+           ;;
        xxdiff)
            touch "$BACKUP"
            if base_present ; then
@@ -231,53 +215,68 @@ merge_file () {
                    -R 'Accel.SaveAsMerged: "Ctrl-S"' \
                    -R 'Accel.Search: "Ctrl+F"' \
                    -R 'Accel.SearchForward: "Ctrl-G"' \
-                   --merged-file "$path" -- "$LOCAL" "$BASE" "$REMOTE"
+                   --merged-file "$MERGED" -- "$LOCAL" "$BASE" "$REMOTE"
            else
                "$merge_tool_path" -X --show-merged-pane \
                    -R 'Accel.SaveAsMerged: "Ctrl-S"' \
                    -R 'Accel.Search: "Ctrl+F"' \
                    -R 'Accel.SearchForward: "Ctrl-G"' \
-                   --merged-file "$path" -- "$LOCAL" "$REMOTE"
+                   --merged-file "$MERGED" -- "$LOCAL" "$REMOTE"
            fi
            check_unchanged
-           save_backup
            ;;
        opendiff)
            touch "$BACKUP"
            if base_present; then
-               "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat
+               "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED" | cat
            else
-               "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$path" | cat
+               "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED" | cat
            fi
            check_unchanged
-           save_backup
            ;;
        ecmerge)
            touch "$BACKUP"
            if base_present; then
-               "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$path"
+               "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$MERGED"
            else
-               "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$path"
+               "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$MERGED"
            fi
            check_unchanged
-           save_backup
            ;;
        emerge)
            if base_present ; then
-               "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")"
+               "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$MERGED")"
            else
-               "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")"
+               "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$MERGED")"
            fi
            status=$?
-           save_backup
+           ;;
+       *)
+           if test -n "$merge_tool_cmd"; then
+               if test "$merge_tool_trust_exit_code" = "false"; then
+                   touch "$BACKUP"
+                   ( eval $merge_tool_cmd )
+                   check_unchanged
+               else
+                   ( eval $merge_tool_cmd )
+                   status=$?
+               fi
+           fi
            ;;
     esac
     if test "$status" -ne 0; then
-       echo "merge of $path failed" 1>&2
-       mv -- "$BACKUP" "$path"
+       echo "merge of $MERGED failed" 1>&2
+       mv -- "$BACKUP" "$MERGED"
        exit 1
     fi
-    git add -- "$path"
+
+    if test "$merge_keep_backup" = "true"; then
+       mv -- "$BACKUP" "$MERGED.orig"
+    else
+       rm -- "$BACKUP"
+    fi
+
+    git add -- "$MERGED"
     cleanup_temp_files
 }
 
@@ -309,12 +308,20 @@ do
     shift
 done
 
+valid_custom_tool()
+{
+    merge_tool_cmd="$(git config mergetool.$1.cmd)"
+    test -n "$merge_tool_cmd"
+}
+
 valid_tool() {
        case "$1" in
                kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
                        ;; # happy
                *)
-                       return 1
+                       if ! valid_custom_tool "$1"; then
+                               return 1
+                       fi
                        ;;
        esac
 }
@@ -380,10 +387,16 @@ else
 
     init_merge_tool_path "$merge_tool"
 
-    if ! type "$merge_tool_path" > /dev/null 2>&1; then
+    merge_keep_backup="$(git config --bool merge.keepBackup || echo true)"
+
+    if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
         echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
         exit 1
     fi
+
+    if ! test -z "$merge_tool_cmd"; then
+        merge_tool_trust_exit_code="$(git config --bool mergetool.$merge_tool.trustExitCode || echo false)"
+    fi
 fi
 
 
diff --git a/path.c b/path.c
index af2716199b92e920df9fe2021035d319e8cafbc6..f4ed979997b6a968ba1987a199beae39035d5da2 100644 (file)
--- a/path.c
+++ b/path.c
@@ -283,7 +283,7 @@ int adjust_shared_perm(const char *path)
                            ? (S_IXGRP|S_IXOTH)
                            : 0));
        if (S_ISDIR(mode))
-               mode |= S_ISGID;
+               mode |= FORCE_DIR_SET_GID;
        if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
                return -2;
        return 0;
index 743757c36ec0e5667fd8af28800ac095f1581adb..44100a749b09439121073e32e7a3143b54185d31 100644 (file)
@@ -91,6 +91,13 @@ int start_command(struct child_process *cmd)
                        close(cmd->in);
                }
 
+               if (cmd->no_stderr)
+                       dup_devnull(2);
+               else if (need_err) {
+                       dup2(fderr[1], 2);
+                       close_pair(fderr);
+               }
+
                if (cmd->no_stdout)
                        dup_devnull(1);
                else if (cmd->stdout_to_stderr)
@@ -103,13 +110,6 @@ int start_command(struct child_process *cmd)
                        close(cmd->out);
                }
 
-               if (cmd->no_stderr)
-                       dup_devnull(2);
-               else if (need_err) {
-                       dup2(fderr[1], 2);
-                       close_pair(fderr);
-               }
-
                if (cmd->dir && chdir(cmd->dir))
                        die("exec %s: cd to %s failed (%s)", cmd->argv[0],
                            cmd->dir, strerror(errno));
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
new file mode 100644 (file)
index 0000000..6b0483f
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Charles Bailey
+#
+
+test_description='git-mergetool
+
+Testing basic merge tool invocation'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+    echo master >file1 &&
+    git add file1 &&
+    git commit -m "added file1" &&
+    git checkout -b branch1 master &&
+    echo branch1 change >file1 &&
+    echo branch1 newfile >file2 &&
+    git add file1 file2 &&
+    git commit -m "branch1 changes" &&
+    git checkout -b branch2 master &&
+    echo branch2 change >file1 &&
+    echo branch2 newfile >file2 &&
+    git add file1 file2 &&
+    git commit -m "branch2 changes" &&
+    git checkout master &&
+    echo master updated >file1 &&
+    echo master new >file2 &&
+    git add file1 file2 &&
+    git commit -m "master updates"
+'
+
+test_expect_success 'custom mergetool' '
+    git config merge.tool mytool &&
+    git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+    git config mergetool.mytool.trustExitCode true &&
+       git checkout branch1 &&
+    ! git merge master >/dev/null 2>&1 &&
+    ( yes "" | git mergetool file1>/dev/null 2>&1 ) &&
+    ( yes "" | git mergetool file2>/dev/null 2>&1 ) &&
+    test "$(cat file1)" = "master updated" &&
+    test "$(cat file2)" = "master new" &&
+       git commit -m "branch1 resolved with mergetool"
+'
+
+test_done