git-commit --amend: allow empty commit.
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 69767b9de97f27306032d7af1aa05d0bdbf5315f..c0548eed98df0fb6c11b04a775b6b64b3bb4d417 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -178,11 +178,12 @@ static void emit_rewrite_diff(const char *name_a,
                copy_file('+', temp[1].name);
 }
 
-static void builtin_diff(const char *name_a,
+static const char *builtin_diff(const char *name_a,
                         const char *name_b,
                         struct diff_tempfile *temp,
                         const char *xfrm_msg,
-                        int complete_rewrite)
+                        int complete_rewrite,
+                        const char **args)
 {
        int i, next_at, cmd_size;
        const char *const diff_cmd = "diff -L%s -L%s";
@@ -242,19 +243,24 @@ static void builtin_diff(const char *name_a,
                }
                if (xfrm_msg && xfrm_msg[0])
                        puts(xfrm_msg);
+               /*
+                * we do not run diff between different kind
+                * of objects.
+                */
                if (strncmp(temp[0].mode, temp[1].mode, 3))
-                       /* we do not run diff between different kind
-                        * of objects.
-                        */
-                       exit(0);
+                       return NULL;
                if (complete_rewrite) {
-                       fflush(NULL);
                        emit_rewrite_diff(name_a, name_b, temp);
-                       exit(0);
+                       return NULL;
                }
        }
-       fflush(NULL);
-       execlp("/bin/sh","sh", "-c", cmd, NULL);
+
+       /* This is disgusting */
+       *args++ = "sh";
+       *args++ = "-c";
+       *args++ = cmd;
+       *args = NULL;
+       return "/bin/sh";
 }
 
 struct diff_filespec *alloc_filespec(const char *path)
@@ -311,7 +317,7 @@ static int work_tree_matches(const char *name, const unsigned char *sha1)
        ce = active_cache[pos];
        if ((lstat(name, &st) < 0) ||
            !S_ISREG(st.st_mode) || /* careful! */
-           ce_match_stat(ce, &st) ||
+           ce_match_stat(ce, &st, 0) ||
            memcmp(sha1, ce->sha1, 20))
                return 0;
        /* we return 1 only when we can stat, it is a regular file,
@@ -555,6 +561,42 @@ static void remove_tempfile(void)
 static void remove_tempfile_on_signal(int signo)
 {
        remove_tempfile();
+       signal(SIGINT, SIG_DFL);
+       raise(signo);
+}
+
+static int spawn_prog(const char *pgm, const char **arg)
+{
+       pid_t pid;
+       int status;
+
+       fflush(NULL);
+       pid = fork();
+       if (pid < 0)
+               die("unable to fork");
+       if (!pid) {
+               execvp(pgm, (char *const*) arg);
+               exit(255);
+       }
+
+       while (waitpid(pid, &status, 0) < 0) {
+               if (errno == EINTR)
+                       continue;
+               return -1;
+       }
+
+       /* Earlier we did not check the exit status because
+        * diff exits non-zero if files are different, and
+        * we are not interested in knowing that.  It was a
+        * mistake which made it harder to quit a diff-*
+        * session that uses the git-apply-patch-script as
+        * the GIT_EXTERNAL_DIFF.  A custom GIT_EXTERNAL_DIFF
+        * should also exit non-zero only when it wants to
+        * abort the entire diff-* session.
+        */
+       if (WIFEXITED(status) && !WEXITSTATUS(status))
+               return 0;
+       return -1;
 }
 
 /* An external diff command takes:
@@ -571,9 +613,9 @@ static void run_external_diff(const char *pgm,
                              const char *xfrm_msg,
                              int complete_rewrite)
 {
+       const char *spawn_arg[10];
        struct diff_tempfile *temp = diff_temp;
-       pid_t pid;
-       int status;
+       int retval;
        static int atexit_asked = 0;
        const char *othername;
 
@@ -590,59 +632,41 @@ static void run_external_diff(const char *pgm,
                signal(SIGINT, remove_tempfile_on_signal);
        }
 
-       fflush(NULL);
-       pid = fork();
-       if (pid < 0)
-               die("unable to fork");
-       if (!pid) {
-               if (pgm) {
-                       if (one && two) {
-                               const char *exec_arg[10];
-                               const char **arg = &exec_arg[0];
-                               *arg++ = pgm;
-                               *arg++ = name;
-                               *arg++ = temp[0].name;
-                               *arg++ = temp[0].hex;
-                               *arg++ = temp[0].mode;
-                               *arg++ = temp[1].name;
-                               *arg++ = temp[1].hex;
-                               *arg++ = temp[1].mode;
-                               if (other) {
-                                       *arg++ = other;
-                                       *arg++ = xfrm_msg;
-                               }
-                               *arg = NULL;
-                               execvp(pgm, (char *const*) exec_arg);
+       if (pgm) {
+               const char **arg = &spawn_arg[0];
+               if (one && two) {
+                       *arg++ = pgm;
+                       *arg++ = name;
+                       *arg++ = temp[0].name;
+                       *arg++ = temp[0].hex;
+                       *arg++ = temp[0].mode;
+                       *arg++ = temp[1].name;
+                       *arg++ = temp[1].hex;
+                       *arg++ = temp[1].mode;
+                       if (other) {
+                               *arg++ = other;
+                               *arg++ = xfrm_msg;
                        }
-                       else
-                               execlp(pgm, pgm, name, NULL);
+               } else {
+                       *arg++ = pgm;
+                       *arg++ = name;
                }
-               /*
-                * otherwise we use the built-in one.
-                */
-               if (one && two)
-                       builtin_diff(name, othername, temp, xfrm_msg,
-                                    complete_rewrite);
-               else
+               *arg = NULL;
+       } else {
+               if (one && two) {
+                       pgm = builtin_diff(name, othername, temp, xfrm_msg, complete_rewrite, spawn_arg);
+               } else
                        printf("* Unmerged path %s\n", name);
-               exit(0);
        }
-       if (waitpid(pid, &status, 0) < 0 ||
-           !WIFEXITED(status) || WEXITSTATUS(status)) {
-               /* Earlier we did not check the exit status because
-                * diff exits non-zero if files are different, and
-                * we are not interested in knowing that.  It was a
-                * mistake which made it harder to quit a diff-*
-                * session that uses the git-apply-patch-script as
-                * the GIT_EXTERNAL_DIFF.  A custom GIT_EXTERNAL_DIFF
-                * should also exit non-zero only when it wants to
-                * abort the entire diff-* session.
-                */
-               remove_tempfile();
+
+       retval = 0;
+       if (pgm)
+               retval = spawn_prog(pgm, spawn_arg);
+       remove_tempfile();
+       if (retval) {
                fprintf(stderr, "external diff died, stopping at %s.\n", name);
                exit(1);
        }
-       remove_tempfile();
 }
 
 static void diff_fill_sha1_info(struct diff_filespec *one)
@@ -847,8 +871,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->find_copies_harder = 1;
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
-       else if (!strncmp(arg, "--abbrev=", 9))
+       else if (!strncmp(arg, "--abbrev=", 9)) {
                options->abbrev = strtoul(arg + 9, NULL, 10);
+               if (options->abbrev < MINIMUM_ABBREV)
+                       options->abbrev = MINIMUM_ABBREV;
+               else if (40 < options->abbrev)
+                       options->abbrev = 40;
+       }
        else
                return 0;
        return 1;
@@ -956,7 +985,7 @@ void diff_free_filepair(struct diff_filepair *p)
 }
 
 /* This is different from find_unique_abbrev() in that
- * it needs to deal with 0{40} SHA1.
+ * it stuffs the result with dots for alignment.
  */
 const char *diff_unique_abbrev(const unsigned char *sha1, int len)
 {
@@ -966,16 +995,8 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len)
                return sha1_to_hex(sha1);
 
        abbrev = find_unique_abbrev(sha1, len);
-       if (!abbrev) {
-               if (!memcmp(sha1, null_sha1, 20)) {
-                       char *buf = sha1_to_hex(null_sha1);
-                       if (len < 37)
-                               strcpy(buf + len, "...");
-                       return buf;
-               }
-               else 
-                       return sha1_to_hex(sha1);
-       }
+       if (!abbrev)
+               return sha1_to_hex(sha1);
        abblen = strlen(abbrev);
        if (abblen < 37) {
                static char hex[41];