Merge branch 'master' into js/diff-ni
authorJunio C Hamano <junkio@cox.net>
Mon, 26 Feb 2007 09:20:42 +0000 (01:20 -0800)
committerJunio C Hamano <junkio@cox.net>
Mon, 26 Feb 2007 09:20:42 +0000 (01:20 -0800)
* master: (201 commits)
Documentation: link in 1.5.0.2 material to the top documentation page.
Documentation: document remote.<name>.tagopt
GIT 1.5.0.2
git-remote: support remotes with a dot in the name
Documentation: describe "-f/-t/-m" options to "git-remote add"
diff --cc: fix display of symlink conflicts during a merge.
merge-recursive: fix longstanding bug in merging symlinks
merge-index: fix longstanding bug in merging symlinks
diff --cached: give more sensible error message when HEAD is yet to be created.
Update tests to use test-chmtime
Add test-chmtime: a utility to change mtime on files
Add Release Notes to prepare for 1.5.0.2
Allow arbitrary number of arguments to git-pack-objects
rerere: do not deal with symlinks.
rerere: do not skip two conflicted paths next to each other.
Don't modify CREDITS-FILE if it hasn't changed.
diff-patch: Avoid emitting double-slashes in textual patch.
Reword git-am 3-way fallback failure message.
Limit filename for format-patch
core.legacyheaders: Use the description used in RelNotes-1.5.0
...

12 files changed:
Documentation/git-diff-files.txt
Documentation/git-diff.txt
builtin-diff-files.c
builtin-diff-index.c
builtin-diff.c
builtin-update-index.c
diff-lib.c
diff.c
diff.h
git-commit.sh
git.c
wt-status.c
index 7248b35d952c3fe97496895059af35300694eb98..b78c4c64f1b2638b8be42072a0f9a4eab1362f7f 100644 (file)
@@ -8,7 +8,7 @@ git-diff-files - Compares files in the working tree and the index
 
 SYNOPSIS
 --------
-'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc] [<common diff options>] [<path>...]
+'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc|-n|--no-index] [<common diff options>] [<path>...]
 
 DESCRIPTION
 -----------
@@ -36,6 +36,9 @@ omit diff output for unmerged entries and just show "Unmerged".
        diff, similar to the way 'diff-tree' shows a merge
        commit with these flags.
 
+\-n,\--no-index::
+       Compare the two given files / directories.
+
 -q::
        Remain silent even on nonexistent files
 
index 6a098df26b1c1639f6ac88233b5642a2be9039fa..12a531d1e9e4a7382f017ac303203e20abf47f2f 100644 (file)
@@ -23,6 +23,10 @@ tree and the index file, or the index file and the working tree.
        further add to the index but you still haven't.  You can
        stage these changes by using gitlink:git-add[1].
 
+       If exactly two paths are given, and at least one is untracked,
+       compare the two files / directories. This behavior can be
+       forced by --no-index.
+
 'git-diff' [--options] --cached [<commit>] [--] [<path>...]::
 
        This form is to view the changes you staged for the next
index 5d4a5c5828d1202b6d6b2eadffc51fc131a37dc5..e1199f80ae34a53872ca6a74ae9678053e33db14 100644 (file)
 #include "builtin.h"
 
 static const char diff_files_usage[] =
-"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
+"git-diff-files [-q] [-0/-1/2/3 |-c|--cc|-n|--no-index] [<common diff options>] [<path>...]"
 COMMON_DIFF_OPTIONS_HELP;
 
 int cmd_diff_files(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
-       int silent = 0;
+       int nongit = 0;
 
+       prefix = setup_git_directory_gently(&nongit);
        init_revisions(&rev, prefix);
        git_config(git_default_config); /* no "diff" UI options */
        rev.abbrev = 0;
 
        argc = setup_revisions(argc, argv, &rev, NULL);
-       while (1 < argc && argv[1][0] == '-') {
-               if (!strcmp(argv[1], "--base"))
-                       rev.max_count = 1;
-               else if (!strcmp(argv[1], "--ours"))
-                       rev.max_count = 2;
-               else if (!strcmp(argv[1], "--theirs"))
-                       rev.max_count = 3;
-               else if (!strcmp(argv[1], "-q"))
-                       silent = 1;
-               else
-                       usage(diff_files_usage);
-               argv++; argc--;
-       }
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_RAW;
-
-       /*
-        * Make sure there are NO revision (i.e. pending object) parameter,
-        * rev.max_count is reasonable (0 <= n <= 3),
-        * there is no other revision filtering parameters.
-        */
-       if (rev.pending.nr ||
-           rev.min_age != -1 || rev.max_age != -1)
-               usage(diff_files_usage);
-       return run_diff_files(&rev, silent);
+       return run_diff_files_cmd(&rev, argc, argv);
 }
index 95a3db156bda2d1e8d2ad7bdf72a9f9a821c6f51..083599d5c4c174cfab7c148428630534e4cd8174 100644 (file)
@@ -38,5 +38,9 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
        if (rev.pending.nr != 1 ||
            rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
                usage(diff_cache_usage);
+       if (read_cache() < 0) {
+               perror("read_cache");
+               return -1;
+       }
        return run_diff_index(&rev, cached);
 }
index 67f49329bf6bad1a5bf2592b1792b65421278708..28b660a780f9f2ae48acf5e8d3dc7c7aee178de2 100644 (file)
@@ -25,40 +25,6 @@ struct blobinfo {
 static const char builtin_diff_usage[] =
 "git-diff <options> <rev>{0,2} -- <path>*";
 
-static int builtin_diff_files(struct rev_info *revs,
-                             int argc, const char **argv)
-{
-       int silent = 0;
-       while (1 < argc) {
-               const char *arg = argv[1];
-               if (!strcmp(arg, "--base"))
-                       revs->max_count = 1;
-               else if (!strcmp(arg, "--ours"))
-                       revs->max_count = 2;
-               else if (!strcmp(arg, "--theirs"))
-                       revs->max_count = 3;
-               else if (!strcmp(arg, "-q"))
-                       silent = 1;
-               else
-                       usage(builtin_diff_usage);
-               argv++; argc--;
-       }
-       /*
-        * Make sure there are NO revision (i.e. pending object) parameter,
-        * specified rev.max_count is reasonable (0 <= n <= 3), and
-        * there is no other revision filtering parameter.
-        */
-       if (revs->pending.nr ||
-           revs->min_age != -1 ||
-           revs->max_age != -1 ||
-           3 < revs->max_count)
-               usage(builtin_diff_usage);
-       if (revs->max_count < 0 &&
-           (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
-               revs->combine_merges = revs->dense_combined_merges = 1;
-       return run_diff_files(revs, silent);
-}
-
 static void stuff_change(struct diff_options *opt,
                         unsigned old_mode, unsigned new_mode,
                         const unsigned char *old_sha1,
@@ -151,6 +117,10 @@ static int builtin_diff_index(struct rev_info *revs,
            revs->max_count != -1 || revs->min_age != -1 ||
            revs->max_age != -1)
                usage(builtin_diff_usage);
+       if (read_cache() < 0) {
+               perror("read_cache");
+               return -1;
+       }
        return run_diff_index(revs, cached);
 }
 
@@ -219,6 +189,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        int ents = 0, blobs = 0, paths = 0;
        const char *path = NULL;
        struct blobinfo blob[2];
+       int nongit = 0;
 
        /*
         * We could get N tree-ish in the rev.pending_objects list.
@@ -240,6 +211,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
         * Other cases are errors.
         */
 
+       prefix = setup_git_directory_gently(&nongit);
        git_config(git_diff_ui_config);
        init_revisions(&rev, prefix);
 
@@ -317,7 +289,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        if (!ents) {
                switch (blobs) {
                case 0:
-                       return builtin_diff_files(&rev, argc, argv);
+                       return run_diff_files_cmd(&rev, argc, argv);
                        break;
                case 1:
                        if (paths != 1)
index 772aaba7bbfded782cd9c0adbc4199fdac0de642..65246dad8d075de3f77454494c419000288c9ed2 100644 (file)
@@ -487,6 +487,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        int prefix_length = prefix ? strlen(prefix) : 0;
        char set_executable_bit = 0;
        unsigned int refresh_flags = 0;
+       int lock_error = 0;
        struct lock_file *lock_file;
 
        git_config(git_default_config);
@@ -494,7 +495,9 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        /* We can't free this memory, it becomes part of a linked list parsed atexit() */
        lock_file = xcalloc(1, sizeof(struct lock_file));
 
-       newfd = hold_lock_file_for_update(lock_file, get_index_file(), 1);
+       newfd = hold_lock_file_for_update(lock_file, get_index_file(), 0);
+       if (newfd < 0)
+               lock_error = errno;
 
        entries = read_cache();
        if (entries < 0)
@@ -651,6 +654,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
 
  finish:
        if (active_cache_changed) {
+               if (newfd < 0) {
+                       if (refresh_flags & REFRESH_QUIET)
+                               exit(128);
+                       die("unable to create '%s.lock': %s",
+                           get_index_file(), strerror(lock_error));
+               }
                if (write_cache(newfd, active_cache, active_nr) ||
                    close(newfd) || commit_lock_file(lock_file))
                        die("Unable to write new index file");
index 60c0fa6488eda7ae382500b70041aac4202a59c0..ae8364b42a0c67e2461fc20e731417b3203c472f 100644 (file)
 #include "diffcore.h"
 #include "revision.h"
 #include "cache-tree.h"
+#include "path-list.h"
 
 /*
  * diff-files
  */
 
+static int read_directory(const char *path, struct path_list *list)
+{
+       DIR *dir;
+       struct dirent *e;
+
+       if (!(dir = opendir(path)))
+               return error("Could not open directory %s", path);
+
+       while ((e = readdir(dir)))
+               if (strcmp(".", e->d_name) && strcmp("..", e->d_name))
+                       path_list_insert(xstrdup(e->d_name), list);
+
+       closedir(dir);
+       return 0;
+}
+
+static int queue_diff(struct diff_options *o,
+               const char *name1, const char *name2)
+{
+       struct stat st;
+       int mode1 = 0, mode2 = 0;
+
+       if (name1) {
+               if (stat(name1, &st))
+                       return error("Could not access '%s'", name1);
+               mode1 = st.st_mode;
+       }
+       if (name2) {
+               if (stat(name2, &st))
+                       return error("Could not access '%s'", name2);
+               mode2 = st.st_mode;
+       }
+
+       if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2))
+               return error("file/directory conflict: %s, %s", name1, name2);
+
+       if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
+               char buffer1[PATH_MAX], buffer2[PATH_MAX];
+               struct path_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};
+               int len1 = 0, len2 = 0, i1, i2, ret = 0;
+
+               if (name1 && read_directory(name1, &p1))
+                       return -1;
+               if (name2 && read_directory(name2, &p2)) {
+                       path_list_clear(&p1, 0);
+                       return -1;
+               }
+
+               if (name1) {
+                       len1 = strlen(name1);
+                       if (len1 > 0 && name1[len1 - 1] == '/')
+                               len1--;
+                       memcpy(buffer1, name1, len1);
+                       buffer1[len1++] = '/';
+               }
+
+               if (name2) {
+                       len2 = strlen(name2);
+                       if (len2 > 0 && name2[len2 - 1] == '/')
+                               len2--;
+                       memcpy(buffer2, name2, len2);
+                       buffer2[len2++] = '/';
+               }
+
+               for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {
+                       const char *n1, *n2;
+                       int comp;
+
+                       if (i1 == p1.nr)
+                               comp = 1;
+                       else if (i2 == p2.nr)
+                               comp = -1;
+                       else
+                               comp = strcmp(p1.items[i1].path,
+                                       p2.items[i2].path);
+
+                       if (comp > 0)
+                               n1 = NULL;
+                       else {
+                               n1 = buffer1;
+                               strncpy(buffer1 + len1, p1.items[i1++].path,
+                                               PATH_MAX - len1);
+                       }
+
+                       if (comp < 0)
+                               n2 = NULL;
+                       else {
+                               n2 = buffer2;
+                               strncpy(buffer2 + len2, p2.items[i2++].path,
+                                               PATH_MAX - len2);
+                       }
+
+                       ret = queue_diff(o, n1, n2);
+               }
+               path_list_clear(&p1, 0);
+               path_list_clear(&p2, 0);
+
+               return ret;
+       } else {
+               struct diff_filespec *d1, *d2;
+
+               if (o->reverse_diff) {
+                       unsigned tmp;
+                       const char *tmp_c;
+                       tmp = mode1; mode1 = mode2; mode2 = tmp;
+                       tmp_c = name1; name1 = name2; name2 = tmp_c;
+               }
+
+               if (!name1)
+                       name1 = "/dev/null";
+               if (!name2)
+                       name2 = "/dev/null";
+               d1 = alloc_filespec(name1);
+               d2 = alloc_filespec(name2);
+               fill_filespec(d1, null_sha1, mode1);
+               fill_filespec(d2, null_sha1, mode2);
+
+               diff_queue(&diff_queued_diff, d1, d2);
+               return 0;
+       }
+}
+
+static int is_in_index(const char *path)
+{
+       int len = strlen(path);
+       int pos = cache_name_pos(path, len);
+       char c;
+
+       if (pos < 0)
+               return 0;
+       if (strncmp(active_cache[pos]->name, path, len))
+               return 0;
+       c = active_cache[pos]->name[len];
+       return c == '\0' || c == '/';
+}
+
+static int handle_diff_files_args(struct rev_info *revs,
+               int argc, const char **argv, int *silent)
+{
+       *silent = 0;
+
+       /* revs->max_count == -2 means --no-index */
+       while (1 < argc && argv[1][0] == '-') {
+               if (!strcmp(argv[1], "--base"))
+                       revs->max_count = 1;
+               else if (!strcmp(argv[1], "--ours"))
+                       revs->max_count = 2;
+               else if (!strcmp(argv[1], "--theirs"))
+                       revs->max_count = 3;
+               else if (!strcmp(argv[1], "-n") ||
+                               !strcmp(argv[1], "--no-index"))
+                       revs->max_count = -2;
+               else if (!strcmp(argv[1], "-q"))
+                       *silent = 1;
+               else
+                       return error("invalid option: %s", argv[1]);
+               argv++; argc--;
+       }
+
+       if (revs->max_count == -1 && revs->diffopt.nr_paths == 2) {
+               /*
+                * If two files are specified, and at least one is untracked,
+                * default to no-index.
+                */
+               read_cache();
+               if (!is_in_index(revs->diffopt.paths[0]) ||
+                                       !is_in_index(revs->diffopt.paths[1]))
+                       revs->max_count = -2;
+       }
+
+       /*
+        * Make sure there are NO revision (i.e. pending object) parameter,
+        * rev.max_count is reasonable (0 <= n <= 3),
+        * there is no other revision filtering parameters.
+        */
+       if (revs->pending.nr || revs->max_count > 3 ||
+           revs->min_age != -1 || revs->max_age != -1)
+               return error("no revision allowed with diff-files");
+
+       if (revs->max_count == -1 &&
+           (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
+               revs->combine_merges = revs->dense_combined_merges = 1;
+
+       return 0;
+}
+
+int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
+{
+       int silent_on_removed;
+
+       if (handle_diff_files_args(revs, argc, argv, &silent_on_removed))
+               return -1;
+
+       if (revs->max_count == -2) {
+               if (revs->diffopt.nr_paths != 2)
+                       return error("need two files/directories with --no-index");
+               queue_diff(&revs->diffopt, revs->diffopt.paths[0],
+                               revs->diffopt.paths[1]);
+               diffcore_std(&revs->diffopt);
+               diff_flush(&revs->diffopt);
+               return 0;
+       }
+
+       if (read_cache() < 0) {
+               perror("read_cache");
+               return -1;
+       }
+       return run_diff_files(revs, silent_on_removed);
+}
+
 int run_diff_files(struct rev_info *revs, int silent_on_removed)
 {
        int entries, i;
@@ -20,11 +231,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
 
        if (diff_unmerged_stage < 0)
                diff_unmerged_stage = 2;
-       entries = read_cache();
-       if (entries < 0) {
-               perror("read_cache");
-               return -1;
-       }
+       entries = active_nr;
        for (i = 0; i < entries; i++) {
                struct stat st;
                unsigned int oldmode, newmode;
@@ -362,10 +569,6 @@ int run_diff_index(struct rev_info *revs, int cached)
        if (!revs->ignore_merges)
                match_missing = 1;
 
-       if (read_cache() < 0) {
-               perror("read_cache");
-               return -1;
-       }
        mark_merge_entries();
 
        ent = revs->pending.objects[0].item;
diff --git a/diff.c b/diff.c
index d1eae7214d863d5a87735059d509afcdf60507d7..6bd456ed42e76a41c62908e5bb0d7df2b329b032 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2455,7 +2455,8 @@ static void diff_resolve_rename_copy(void)
                                p->status = DIFF_STATUS_RENAMED;
                }
                else if (hashcmp(p->one->sha1, p->two->sha1) ||
-                        p->one->mode != p->two->mode)
+                        p->one->mode != p->two->mode ||
+                        is_null_sha1(p->one->sha1))
                        p->status = DIFF_STATUS_MODIFIED;
                else {
                        /* This is a "no-change" entry and should not
diff --git a/diff.h b/diff.h
index eece65ddccdb7bd8bf51af80af92e29b4eb76f37..b608828261c9a7bb4b60c030109d078841acb114 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -219,6 +219,7 @@ extern void diff_flush(struct diff_options*);
 extern const char *diff_unique_abbrev(const unsigned char *, int);
 
 extern int run_diff_files(struct rev_info *revs, int silent_on_removed);
+extern int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv);
 
 extern int run_diff_index(struct rev_info *revs, int cached);
 
index 476f4f18dbb76041fbbfe6243785ad4997a7e7c7..be3677c2049dca4deb35403804d5ea2d72254e7d 100755 (executable)
@@ -13,10 +13,10 @@ git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
 case "$0" in
 *status)
        status_only=t
-       unmerged_ok_if_status=--unmerged ;;
+       ;;
 *commit)
        status_only=
-       unmerged_ok_if_status= ;;
+       ;;
 esac
 
 refuse_partial () {
@@ -393,16 +393,17 @@ else
        USE_INDEX="$THIS_INDEX"
 fi
 
-GIT_INDEX_FILE="$USE_INDEX" \
-       git-update-index -q $unmerged_ok_if_status --refresh || exit
-
-################################################################
-# If the request is status, just show it and exit.
-
-case "$0" in
-*status)
+case "$status_only" in
+t)
+       # This will silently fail in a read-only repository, which is
+       # what we want.
+       GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --unmerged --refresh
        run_status
        exit $?
+       ;;
+'')
+       GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --refresh || exit
+       ;;
 esac
 
 ################################################################
diff --git a/git.c b/git.c
index 83f3d90ee35418976baea4f032c13aed0ec9e664..fa3fd11386eac14d0aed918e88d5dc449ef1b37f 100644 (file)
--- a/git.c
+++ b/git.c
@@ -237,8 +237,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "config", cmd_config },
                { "count-objects", cmd_count_objects, RUN_SETUP },
                { "describe", cmd_describe, RUN_SETUP },
-               { "diff", cmd_diff, RUN_SETUP | USE_PAGER },
-               { "diff-files", cmd_diff_files, RUN_SETUP },
+               { "diff", cmd_diff, USE_PAGER },
+               { "diff-files", cmd_diff_files },
                { "diff-index", cmd_diff_index, RUN_SETUP },
                { "diff-tree", cmd_diff_tree, RUN_SETUP },
                { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
index 035e546ed77248720813e7c42e0e2b25f5e760a3..a25632bc87867748016e32a4ba4652918c8705a3 100644 (file)
@@ -191,12 +191,18 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
                wt_status_print_trailer();
 }
 
+static void wt_read_cache(struct wt_status *s)
+{
+       discard_cache();
+       read_cache();
+}
+
 void wt_status_print_initial(struct wt_status *s)
 {
        int i;
        char buf[PATH_MAX];
 
-       read_cache();
+       wt_read_cache(s);
        if (active_nr) {
                s->commitable = 1;
                wt_status_print_cached_header(NULL);
@@ -220,6 +226,7 @@ static void wt_status_print_updated(struct wt_status *s)
        rev.diffopt.format_callback = wt_status_print_updated_cb;
        rev.diffopt.format_callback_data = s;
        rev.diffopt.detect_rename = 1;
+       wt_read_cache(s);
        run_diff_index(&rev, 1);
 }
 
@@ -231,6 +238,7 @@ static void wt_status_print_changed(struct wt_status *s)
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = wt_status_print_changed_cb;
        rev.diffopt.format_callback_data = s;
+       wt_read_cache(s);
        run_diff_files(&rev, 0);
 }
 
@@ -287,6 +295,7 @@ static void wt_status_print_verbose(struct wt_status *s)
        setup_revisions(0, NULL, &rev, s->reference);
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
        rev.diffopt.detect_rename = 1;
+       wt_read_cache(s);
        run_diff_index(&rev, 1);
 }
 
@@ -316,7 +325,6 @@ void wt_status_print(struct wt_status *s)
        }
        else {
                wt_status_print_updated(s);
-               discard_cache();
        }
 
        wt_status_print_changed(s);