Merge branch 'gb/gitweb-pathinfo'
authorJunio C Hamano <gitster@pobox.com>
Mon, 3 Nov 2008 00:36:25 +0000 (16:36 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 3 Nov 2008 00:36:25 +0000 (16:36 -0800)
* gb/gitweb-pathinfo:
gitweb: generate parent..current URLs
gitweb: parse parent..current syntax from PATH_INFO
gitweb: use_pathinfo filenames start with /
gitweb: generate project/action/hash URLs
gitweb: parse project/action/hash_base:filename PATH_INFO

86 files changed:
Documentation/RelNotes-1.6.0.3.txt
Documentation/RelNotes-1.6.0.4.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.1.txt
Documentation/SubmittingPatches
Documentation/asciidoc.conf
Documentation/config.txt
Documentation/git-daemon.txt
Documentation/gitattributes.txt
Documentation/user-manual.txt
Makefile
archive.c
branch.c
builtin-archive.c
builtin-commit.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-ls-files.c
builtin-pack-objects.c
builtin-receive-pack.c
builtin-remote.c
builtin-revert.c
builtin-rm.c
builtin-send-pack.c
bundle.c
cache.h
compat/cygwin.c
connect.c
contrib/completion/git-completion.bash
daemon.c
diff.c
diffcore-rename.c
diffcore.h
git-add--interactive.perl
git-gui/Makefile
git-gui/git-gui--askpass [new file with mode: 0755]
git-gui/git-gui.sh
git-gui/lib/blame.tcl
git-gui/lib/choose_repository.tcl
git-gui/lib/diff.tcl
git-gui/lib/index.tcl
git-gui/lib/merge.tcl
git-gui/lib/mergetool.tcl
git-gui/lib/remote.tcl
git-gui/lib/remote_add.tcl [new file with mode: 0644]
git-gui/lib/remote_branch_delete.tcl
git-gui/lib/search.tcl [new file with mode: 0644]
git-gui/lib/spellcheck.tcl
git-gui/lib/sshkey.tcl [new file with mode: 0644]
git-gui/lib/transport.tcl
git-gui/po/de.po
git-pull.sh
git-send-email.perl
git-submodule.sh
git-svn.perl
http-push.c
index-pack.c
lockfile.c
pack-refs.c
path.c
perl/Git.pm
read-cache.c
refs.c
remote.c
remote.h
rerere.c
sha1_file.c
t/t1005-read-tree-reset.sh
t/t3600-rm.sh
t/t4012-diff-binary.sh
t/t5000-tar-tree.sh
t/t5302-pack-index.sh
t/t5400-send-pack.sh
t/t5405-send-pack-rewind.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5520-pull.sh
t/t7201-co.sh
t/t7502-status.sh
t/t9300-fast-import.sh
test-chmtime.c
transport.c
unpack-trees.c
userdiff.c [new file with mode: 0644]
userdiff.h [new file with mode: 0644]
walker.c
wt-status.c
index c0f037d6dbe4cfa98ea97a9b28d115e34ccfde75..ae0577836ae8b3ea73f8565acc42964d4144dd26 100644 (file)
@@ -27,9 +27,6 @@ Fixes since v1.6.0.2
 * "git diff" hunk header patterns with multiple elements separated by LF
   were not used correctly.
 
-* "git gc" when ejecting otherwise unreachable objects from packfiles into
-  loose form leaked memory.
-
 * Hunk headers in "git diff" default to using extended regular
   expressions, fixing some of the internal patterns on non-GNU
   platforms.
@@ -37,9 +34,15 @@ Fixes since v1.6.0.2
 * New config "diff.*.xfuncname" exposes extended regular expressions
   for user specified hunk header patterns.
 
+* "git gc" when ejecting otherwise unreachable objects from packfiles into
+  loose form leaked memory.
+
 * "git index-pack" was recently broken and mishandled objects added by
   thin-pack completion processing under memory pressure.
 
+* "git index-pack" was recently broken and misbehaved when run from inside
+  .git/objects/pack/ directory.
+
 * "git stash apply sash@{1}" was fixed to error out.  Prior versions
   would have applied stash@{0} incorrectly.
 
@@ -112,9 +115,3 @@ Fixes since v1.6.0.2
   ("git fetch") is still however supported.
 
 Many other documentation updates.
-
---
-exec >/var/tmp/1
-O=v1.6.0.2-110-gf07c3c5
-echo O=$(git describe maint)
-git shortlog --no-merges $O..maint
diff --git a/Documentation/RelNotes-1.6.0.4.txt b/Documentation/RelNotes-1.6.0.4.txt
new file mode 100644 (file)
index 0000000..4a4530b
--- /dev/null
@@ -0,0 +1,29 @@
+GIT v1.6.0.4 Release Notes
+==========================
+
+Fixes since v1.6.0.3
+--------------------
+
+* 'git-add -p' said "No changes" when only binary files were changed.
+
+* git-archive did not work correctly in bare repositories.
+
+* when we refuse to detect renames because there are too many new or
+  deleted files, we did not say how many there are.
+
+* 'git-push --mirror' tried and failed to push the stash; there is no
+  point in sending it to begin with.
+
+* 'git-send-email' had a small fd leak while scanning directory.
+
+* git-svn used deprecated 'git-foo' form of subcommand invocaition.
+
+* Plugged small memleaks here and there.
+
+* Also contains many documentation updates.
+
+--
+exec >/var/tmp/1
+O=v1.6.0.3-22-gc2163c6
+echo O=$(git describe maint)
+git shortlog --no-merges $O..maint
index afd1150eaa4fc17600dd948f8d7dc4ec266aa162..f5a1311a19ae911bd60a0b6f5c1e5a2e917f82f5 100644 (file)
@@ -62,6 +62,8 @@ on.
 * "git bisect" is careful about a user mistake and suggests testing of
   merge base first when good is not a strict ancestor of bad.
 
+* "git check-attr --stdin" can check attributes for multiple paths.
+
 * "git checkout --track origin/hack" used to be a syntax error.  It now
   DWIMs to create a corresponding local branch "hack", i.e. acts as if you
   said "git checkout --track -b hack origin/hack".
@@ -74,6 +76,8 @@ on.
 
 * "git cherry-pick" can also utilize rerere for conflict resolution.
 
+* "git clone" learned to be verbose with -v
+
 * "git commit --author=$name" can look up author name from existing
   commits.
 
@@ -85,6 +89,12 @@ on.
 
 * "git daemon" learned --max-connections=<count> option.
 
+* "git daemon" exports REMOTE_ADDR to record client address, so that
+  spawned programs can act differently on it.
+
+* "git describe --tags" favours closer lightweight tags than farther
+  annotated tags now.
+
 * "git diff" learned to mimic --suppress-blank-empty from GNU diff via a
   configuration option.
 
@@ -99,9 +109,16 @@ on.
 
 * "git diff" hunk header pattern for ObjC has been added.
 
+* a "textconv" filter that makes binary files textual form for human
+   consumption can be specified as an attribute for paths; "git diff"
+   learnt to make use of it.
+
 * "git for-each-ref" learned "refname:short" token that gives an
   unambiguously abbreviated refname.
 
+* Auto-numbering of the subject lines is the default for "git
+  format-patch" now.
+
 * "git grep" learned to accept -z similar to GNU grep.
 
 * "git help" learned to use GIT_MAN_VIEWER environment variable before
@@ -127,9 +144,18 @@ on.
 * "git merge -s $strategy" can use a custom built strategy if you have a
   command "git-merge-$strategy" on your $PATH.
 
+* "git rebase" honours pre-rebase hook; use --no-verify to bypass it.
+
+* "git rebase -p" uses interactive rebase machinery now to preserve the merges.
+
 * "git reflog expire branch" can be used in place of "git reflog expire
   refs/heads/branch".
 
+* "git remote show $remote" lists remote branches one-per-line now.
+
+* when giving up resolving a conflicted merge, "git reset --hard" failed
+  to remove new paths from the working tree. [cherry-pick to 'maint'?]
+
 * "git submodule foreach" subcommand allows you to iterate over checked
   out submodules.
 
@@ -179,6 +205,6 @@ release, unless otherwise noted.
 
 --
 exec >/var/tmp/1
-O=v1.6.0.2-553-g58e0fa5
+O=v1.6.0.3-574-gaebd173
 echo O=$(git describe master)
 git shortlog --no-merges $O..master ^maint
index a1e9100f9e3ccb8466ec603e154cb230dc2cb33b..f0295c60f5aceb975575903327228776d6b2bb9e 100644 (file)
@@ -456,3 +456,30 @@ This should help you to submit patches inline using KMail.
 
 5) Back in the compose window: add whatever other text you wish to the
 message, complete the addressing and subject fields, and press send.
+
+
+Gmail
+-----
+
+Submitting properly formatted patches via Gmail is simple now that
+IMAP support is available. First, edit your ~/.gitconfig to specify your
+account settings:
+
+[imap]
+       folder = "[Gmail]/Drafts"
+       host = imaps://imap.gmail.com
+       user = user@gmail.com
+       pass = p4ssw0rd
+       port = 993
+       sslverify = false
+
+Next, ensure that your Gmail settings are correct. In "Settings" the
+"Use Unicode (UTF-8) encoding for outgoing messages" should be checked.
+
+Once your commits are ready to send to the mailing list, run the following
+command to send the patch emails to your Gmail Drafts folder.
+
+       $ git format-patch -M --stdout origin/master | git imap-send
+
+Go to your Gmail account, open the Drafts folder, find the patch email, fill
+in the To: and CC: fields and send away!
index 40d43b78ee9d6c3827bcf631c1f41f54d0e3dfbc..2da867d2f8dd1e5272d33571062bda5f169cd278 100644 (file)
@@ -40,6 +40,26 @@ endif::doctype-manpage[]
 </literallayout>
 {title#}</example>
 endif::docbook-xsl-172[]
+
+ifdef::docbook-xsl-172[]
+ifdef::doctype-manpage[]
+# The following two small workarounds insert a simple paragraph after screen
+[listingblock]
+<example><title>{title}</title>
+<screen>
+|
+</screen><simpara></simpara>
+{title#}</example>
+
+[verseblock]
+<formalpara{id? id="{id}"}><title>{title}</title><para>
+{title%}<literallayout{id? id="{id}"}>
+{title#}<literallayout>
+|
+</literallayout><simpara></simpara>
+{title#}</para></formalpara>
+endif::doctype-manpage[]
+endif::docbook-xsl-172[]
 endif::backend-docbook[]
 
 ifdef::doctype-manpage[]
index 29369d051b1d8248b85ae177151a4ebaa79bce3b..965ed746da0901d84e0fed3f08de2a0d470ca4e4 100644 (file)
@@ -1188,6 +1188,10 @@ receive.unpackLimit::
        especially on slow filesystems.  If not set, the value of
        `transfer.unpackLimit` is used instead.
 
+receive.denyDeletes::
+       If set to true, git-receive-pack will deny a ref update that deletes
+       the ref. Use this to prevent such a ref deletion via a push.
+
 receive.denyNonFastForwards::
        If set to true, git-receive-pack will deny a ref update which is
        not a fast forward. Use this to prevent such an update via a push,
index b08a08cd95b6da192a008c58d7973769dfe3fc8c..f1a570a874ff0ccc2c2ffb6271ac76529506c4cc 100644 (file)
@@ -270,6 +270,15 @@ selectively enable/disable services per repository::
 ----------------------------------------------------------------
 
 
+ENVIRONMENT
+-----------
+'git-daemon' will set REMOTE_ADDR to the IP address of the client
+that connected to it, if the IP address is available. REMOTE_ADDR will
+be available in the environment of hooks called when
+services are performed.
+
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki
index 26945593cb1739bb6a6e1e2acc2cd78caab3a102..eb648418eedba4f131aec1f18ccba3bd62f53dc2 100644 (file)
@@ -163,8 +163,8 @@ few exceptions.  Even though...
 `ident`
 ^^^^^^^
 
-When the attribute `ident` is set to a path, git replaces
-`$Id$` in the blob object with `$Id:`, followed by
+When the attribute `ident` is set for a path, git replaces
+`$Id$` in the blob object with `$Id:`, followed by the
 40-character hexadecimal blob object name, followed by a dollar
 sign `$` upon checkout.  Any byte sequence that begins with
 `$Id:` and ends with `$` in the worktree file is replaced
@@ -213,6 +213,9 @@ with `crlf`, and then `ident` and fed to `filter`.
 Generating diff text
 ~~~~~~~~~~~~~~~~~~~~
 
+`diff`
+^^^^^^
+
 The attribute `diff` affects if 'git-diff' generates textual
 patch for the path or just says `Binary files differ`.  It also
 can affect what line is shown on the hunk header `@@ -k,l +n,m @@`
@@ -331,6 +334,9 @@ patterns are available:
 Performing a three-way merge
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+`merge`
+^^^^^^^
+
 The attribute `merge` affects how three versions of a file is
 merged when a file-level merge is necessary during `git merge`,
 and other programs such as `git revert` and `git cherry-pick`.
index 08d1310bf5fc5590ada1ee5b2af77d361ff4d874..645d752c5c26d8724e8ded0abe1f207bf3ff6854 100644 (file)
@@ -4356,7 +4356,9 @@ $ git remote show example # get details
 * remote example
   URL: git://example.com/project.git
   Tracked remote branches
-    master next ...
+    master
+    next
+    ...
 $ git fetch example            # update branches from example
 $ git branch -r                        # list all remote branches
 -----------------------------------------------
index 308dc70b5de118083a14bcb2665372a40ae6522c..40309e1537aef1475992de110b6c37144a94ad67 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -389,6 +389,7 @@ LIB_H += transport.h
 LIB_H += tree.h
 LIB_H += tree-walk.h
 LIB_H += unpack-trees.h
+LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += wt-status.h
 
@@ -485,6 +486,7 @@ LIB_OBJS += tree-diff.o
 LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
 LIB_OBJS += unpack-trees.o
+LIB_OBJS += userdiff.o
 LIB_OBJS += usage.o
 LIB_OBJS += utf8.o
 LIB_OBJS += walker.o
@@ -638,8 +640,6 @@ ifeq ($(uname_S),Darwin)
        endif
        NO_STRLCPY = YesPlease
        NO_MEMMEM = YesPlease
-       COMPAT_CFLAGS += -Icompat/regex
-       COMPAT_OBJS += compat/regex/regex.o
 endif
 ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
@@ -690,8 +690,6 @@ ifeq ($(uname_S),FreeBSD)
        BASIC_LDFLAGS += -L/usr/local/lib
        DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
        THREADED_DELTA_SEARCH = YesPlease
-       COMPAT_CFLAGS += -Icompat/regex
-       COMPAT_OBJS += compat/regex/regex.o
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
@@ -718,8 +716,6 @@ ifeq ($(uname_S),AIX)
        INTERNAL_QSORT = UnfortunatelyYes
        NEEDS_LIBICONV=YesPlease
        BASIC_CFLAGS += -D_LARGE_FILES
-       COMPAT_CFLAGS += -Icompat/regex
-       COMPAT_OBJS += compat/regex/regex.o
 endif
 ifeq ($(uname_S),GNU)
        # GNU/Hurd
index 849eed553e9d6455c021664135a01b322eef9a31..9ac455d889b72deba8c949da1d9efe2be3a50244 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -336,5 +336,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
        parse_treeish_arg(argv, &args, prefix);
        parse_pathspec_arg(argv + 1, &args);
 
+       git_config(git_default_config, NULL);
+
        return ar->write_archive(&args);
 }
index 205b89dc697eb4475355ab5b2a6528e404901c0c..b1ac837f3d30c826ddbe29492b906bf2d0de0a1a 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -129,7 +129,9 @@ void create_branch(const char *head,
                        die("Cannot setup tracking information; starting point is not a branch.");
                break;
        case 1:
-               /* Unique completion -- good */
+               /* Unique completion -- good, only if it is a real ref */
+               if (track == BRANCH_TRACK_EXPLICIT && !strcmp(real_ref, "HEAD"))
+                       die("Cannot setup tracking information; starting point is not a branch.");
                break;
        default:
                die("Ambiguous object name: '%s'.", start_name);
index 432ce2acc6bb687b35f3e9663f3058ebef2354e7..5ceec433fd590e8bf6a51700ea69c37f9af30fa7 100644 (file)
@@ -111,8 +111,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
 {
        const char *remote = NULL;
 
-       git_config(git_default_config, NULL);
-
        remote = extract_remote_arg(&argc, argv);
        if (remote)
                return run_remote_archiver(remote, argc, argv);
index 33b659edce478fb0d3d4bb723e4f54c13db0efb1..93ca49635731942c72cb52b27b1a6ba53becd1ca 100644 (file)
@@ -320,7 +320,9 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
                die("unable to write new_index file");
 
        fd = hold_lock_file_for_update(&false_lock,
-                                      git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), 1);
+                                      git_path("next-index-%"PRIuMAX,
+                                               (uintmax_t) getpid()),
+                                      LOCK_DIE_ON_ERROR);
 
        create_base_index();
        add_remove_files(&partial);
index fa3c936493cc0b139edf3e4e8154569f453afc02..372bfa20a2eac978f7511f5d8d9296be789b527f 100644 (file)
@@ -813,7 +813,8 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                          )
                        die("shallow file was changed during fetch");
 
-               fd = hold_lock_file_for_update(&lock, shallow, 1);
+               fd = hold_lock_file_for_update(&lock, shallow,
+                                              LOCK_DIE_ON_ERROR);
                if (!write_shallow_commits(fd, 0)) {
                        unlink(shallow);
                        rollback_lock_file(&lock);
index ee93d3a93da0267caa485fa552c29d779aecfdf7..f151cfa2fd028dd37ad0cc3b6b35b2017417fffa 100644 (file)
@@ -521,8 +521,8 @@ static void find_non_local_tags(struct transport *transport,
                     will_fetch(head, ref->old_sha1))) {
                        string_list_insert(ref_name, &new_refs);
 
-                       rm = alloc_ref_from_str(ref_name);
-                       rm->peer_ref = alloc_ref_from_str(ref_name);
+                       rm = alloc_ref(ref_name);
+                       rm->peer_ref = alloc_ref(ref_name);
                        hashcpy(rm->old_sha1, ref_sha1);
 
                        **tail = rm;
@@ -534,6 +534,19 @@ static void find_non_local_tags(struct transport *transport,
        string_list_clear(&new_refs, 0);
 }
 
+static void check_not_current_branch(struct ref *ref_map)
+{
+       struct branch *current_branch = branch_get(NULL);
+
+       if (is_bare_repository() || !current_branch)
+               return;
+
+       for (; ref_map; ref_map = ref_map->next)
+               if (ref_map->peer_ref && !strcmp(current_branch->refname,
+                                       ref_map->peer_ref->name))
+                       die("Refusing to fetch into current branch");
+}
+
 static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
 {
@@ -558,6 +571,8 @@ static int do_fetch(struct transport *transport,
        }
 
        ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
+       if (!update_head_ok)
+               check_not_current_branch(ref_map);
 
        for (rm = ref_map; rm; rm = rm->next) {
                if (rm->peer_ref)
index 068f424696864f3db1c1b78ef25a92a197ff5474..b48327db950e5d8a54045f4956fa92b7879227bc 100644 (file)
@@ -91,39 +91,10 @@ static void show_other_files(struct dir_struct *dir)
 {
        int i;
 
-
-       /*
-        * Skip matching and unmerged entries for the paths,
-        * since we want just "others".
-        *
-        * (Matching entries are normally pruned during
-        * the directory tree walk, but will show up for
-        * gitlinks because we don't necessarily have
-        * dir->show_other_directories set to suppress
-        * them).
-        */
        for (i = 0; i < dir->nr; i++) {
                struct dir_entry *ent = dir->entries[i];
-               int len, pos;
-               struct cache_entry *ce;
-
-               /*
-                * Remove the '/' at the end that directory
-                * walking adds for directory entries.
-                */
-               len = ent->len;
-               if (len && ent->name[len-1] == '/')
-                       len--;
-               pos = cache_name_pos(ent->name, len);
-               if (0 <= pos)
-                       continue;       /* exact match */
-               pos = -pos - 1;
-               if (pos < active_nr) {
-                       ce = active_cache[pos];
-                       if (ce_namelen(ce) == len &&
-                           !memcmp(ce->name, ent->name, len))
-                               continue; /* Yup, this one exists unmerged */
-               }
+               if (!cache_name_is_other(ent->name, ent->len))
+                       continue;
                show_dir_entry(tag_other, ent);
        }
 }
index 59c30d1caa37416041177ff4aaf01b67f4e8add4..15b80db5a1a43ae2ae44375c2cb27978c2e70a71 100644 (file)
@@ -1375,7 +1375,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
        array = xcalloc(window, sizeof(struct unpacked));
 
        for (;;) {
-               struct object_entry *entry = *list++;
+               struct object_entry *entry;
                struct unpacked *n = array + idx;
                int j, max_depth, best_base = -1;
 
@@ -1384,6 +1384,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
                        progress_unlock();
                        break;
                }
+               entry = *list++;
                (*list_size)--;
                if (!entry->preferred_base) {
                        (*processed)++;
index 45e3cd90fd476cdb0a32e5de27739b18e060e031..2c0225c89a44a28150806608ea3956c44e27ef6f 100644 (file)
@@ -11,6 +11,7 @@
 
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
+static int deny_deletes = 0;
 static int deny_non_fast_forwards = 0;
 static int receive_fsck_objects;
 static int receive_unpack_limit = -1;
@@ -23,6 +24,11 @@ static int capabilities_sent;
 
 static int receive_pack_config(const char *var, const char *value, void *cb)
 {
+       if (strcmp(var, "receive.denydeletes") == 0) {
+               deny_deletes = git_config_bool(var, value);
+               return 0;
+       }
+
        if (strcmp(var, "receive.denynonfastforwards") == 0) {
                deny_non_fast_forwards = git_config_bool(var, value);
                return 0;
@@ -185,6 +191,12 @@ static const char *update(struct command *cmd)
                      "but I can't find it!", sha1_to_hex(new_sha1));
                return "bad pack";
        }
+       if (deny_deletes && is_null_sha1(new_sha1) &&
+           !is_null_sha1(old_sha1) &&
+           !prefixcmp(name, "refs/heads/")) {
+               error("denying ref deletion for %s", name);
+               return "deletion prohibited";
+       }
        if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
            !is_null_sha1(old_sha1) &&
            !prefixcmp(name, "refs/heads/")) {
@@ -466,12 +478,17 @@ static int delete_only(struct command *cmd)
 
 static int add_refs_from_alternate(struct alternate_object_database *e, void *unused)
 {
-       char *other = xstrdup(make_absolute_path(e->base));
-       size_t len = strlen(other);
+       char *other;
+       size_t len;
        struct remote *remote;
        struct transport *transport;
        const struct ref *extra;
 
+       e->name[-1] = '\0';
+       other = xstrdup(make_absolute_path(e->base));
+       e->name[-1] = '/';
+       len = strlen(other);
+
        while (other[len-1] == '/')
                other[--len] = '\0';
        if (len < 8 || memcmp(other + len - 8, "/objects", 8))
index 6b3325dfa9e0aa52d806d1c28692a05cf549abe4..df2be068b69b6d926d4980e6e9eada05d92643e1 100644 (file)
@@ -412,10 +412,9 @@ static void show_list(const char *title, struct string_list *list,
                return;
 
        printf(title, list->nr > 1 ? "es" : "", extra_arg);
-       printf("\n    ");
-       for (i = 0; i < list->nr; i++)
-               printf("%s%s", i ? " " : "", list->items[i].string);
        printf("\n");
+       for (i = 0; i < list->nr; i++)
+               printf("    %s\n", list->items[i].string);
 }
 
 static int get_remote_ref_states(const char *name,
@@ -511,17 +510,17 @@ static int show(int argc, const char **argv)
                show_list("  Tracked remote branch%s", &states.tracked, "");
 
                if (states.remote->push_refspec_nr) {
-                       printf("  Local branch%s pushed with 'git push'\n   ",
+                       printf("  Local branch%s pushed with 'git push'\n",
                                states.remote->push_refspec_nr > 1 ?
                                        "es" : "");
                        for (i = 0; i < states.remote->push_refspec_nr; i++) {
                                struct refspec *spec = states.remote->push + i;
-                               printf(" %s%s%s%s", spec->force ? "+" : "",
+                               printf("    %s%s%s%s\n",
+                                      spec->force ? "+" : "",
                                       abbrev_branch(spec->src),
                                       spec->dst ? ":" : "",
                                       spec->dst ? abbrev_branch(spec->dst) : "");
                        }
-                       printf("\n");
                }
 
                /* NEEDSWORK: free remote */
index 84865397405d96aa4fdbfca801e8ecd47e3dea36..7483a7a63bf09a7a123e442e9b933b9d24f2b1a6 100644 (file)
@@ -251,7 +251,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        int i, index_fd, clean;
        char *oneline, *reencoded_message = NULL;
        const char *message, *encoding;
-       const char *defmsg = xstrdup(git_path("MERGE_MSG"));
+       char *defmsg = xstrdup(git_path("MERGE_MSG"));
        struct merge_options o;
        struct tree *result, *next_tree, *base_tree, *head_tree;
        static struct lock_file index_lock;
@@ -329,7 +329,8 @@ static int revert_or_cherry_pick(int argc, const char **argv)
         * reverse of it if we are revert.
         */
 
-       msg_fd = hold_lock_file_for_update(&msg_file, defmsg, 1);
+       msg_fd = hold_lock_file_for_update(&msg_file, defmsg,
+                                          LOCK_DIE_ON_ERROR);
 
        encoding = get_encoding(message);
        if (!encoding)
@@ -431,6 +432,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                return execv_git_cmd(args);
        }
        free(reencoded_message);
+       free(defmsg);
 
        return 0;
 }
index e06640cf8d3418cbbe177b8fdcdccd19e0f3379f..b7126e3e25d4f52ae8c4a120d524a24c11129785 100644 (file)
@@ -79,7 +79,8 @@ static int check_local_mod(unsigned char *head, int index_only)
                     || hashcmp(ce->sha1, sha1))
                        staged_changes = 1;
 
-               if (local_changes && staged_changes)
+               if (local_changes && staged_changes &&
+                   !(index_only && is_empty_blob_sha1(ce->sha1)))
                        errs = error("'%s' has staged content different "
                                     "from both the file and the HEAD\n"
                                     "(use -f to force removal)", name);
index 910db92b62eb6dd91a4002b2643fef4a76ec8f83..d68ce2d0e3451127c61658ae7df3053f5eae6366 100644 (file)
@@ -140,7 +140,13 @@ static struct ref *remote_refs, **remote_tail;
 static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct ref *ref;
-       int len = strlen(refname) + 1;
+       int len;
+
+       /* we already know it starts with refs/ to get here */
+       if (check_ref_format(refname + 5))
+               return 0;
+
+       len = strlen(refname) + 1;
        ref = xcalloc(1, sizeof(*ref) + len);
        hashcpy(ref->new_sha1, sha1);
        memcpy(ref->name, refname, len);
index 00b2aabefca49b634f49143523ee31556baa7777..7d17a1fde16204859849aaf28945739aaa685f91 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -186,7 +186,8 @@ int create_bundle(struct bundle_header *header, const char *path,
        if (bundle_to_stdout)
                bundle_fd = 1;
        else
-               bundle_fd = hold_lock_file_for_update(&lock, path, 1);
+               bundle_fd = hold_lock_file_for_update(&lock, path,
+                                                     LOCK_DIE_ON_ERROR);
 
        /* write signature */
        write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
diff --git a/cache.h b/cache.h
index 991544cf0bd7e84c5db5bd6486e5922a53ec136f..b0edbf9b9f68a98a0052ecb949f20aafb3125e56 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -277,6 +277,7 @@ static inline void remove_name_hash(struct cache_entry *ce)
 #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
 #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
 #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
+#define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
 #endif
 
 enum object_type {
@@ -393,6 +394,7 @@ extern int add_to_index(struct index_state *, const char *path, struct stat *, i
 extern int add_file_to_index(struct index_state *, const char *path, int flags);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
 extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
+extern int index_name_is_other(const struct index_state *, const char *, int);
 
 /* do stat comparison even if CE_VALID is true */
 #define CE_MATCH_IGNORE_VALID          01
@@ -421,6 +423,8 @@ struct lock_file {
        char on_list;
        char filename[PATH_MAX];
 };
+#define LOCK_DIE_ON_ERROR 1
+#define LOCK_NODEREF 2
 extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
 extern int commit_lock_file(struct lock_file *);
@@ -515,6 +519,7 @@ static inline void hashclr(unsigned char *hash)
 {
        memset(hash, 0, 20);
 }
+extern int is_empty_blob_sha1(const unsigned char *sha1);
 
 int git_mkstemp(char *path, size_t n, const char *template);
 
index f1967532bae87cbe3627b065083cc56ca0b64550..ebac1483929c798905e6558e0013e3de3d6abeb2 100644 (file)
@@ -91,26 +91,32 @@ static int cygwin_stat(const char *path, struct stat *buf)
  * functions should be used. The choice is determined by core.ignorecygwinfstricks.
  * Reading this option is not always possible immediately as git_dir may be
  * not be set yet. So until it is set, use cygwin lstat/stat functions.
- * However, if the trust_executable_bit is set, we must use the Cygwin posix
+ * However, if core.filemode is set, we must use the Cygwin posix
  * stat/lstat as the Windows stat fuctions do not determine posix filemode.
+ *
+ * Note that git_cygwin_config() does NOT call git_default_config() and this
+ * is deliberate.  Many commands read from config to establish initial
+ * values in variables and later tweak them from elsewhere (e.g. command line).
+ * init_stat() is called lazily on demand, typically much late in the program,
+ * and calling git_default_config() from here would break such variables.
  */
 static int native_stat = 1;
-extern int trust_executable_bit;
+static int core_filemode;
 
 static int git_cygwin_config(const char *var, const char *value, void *cb)
 {
-       if (!strcmp(var, "core.ignorecygwinfstricks")) {
+       if (!strcmp(var, "core.ignorecygwinfstricks"))
                native_stat = git_config_bool(var, value);
-               return 0;
-       }
-       return git_default_config(var, value, cb);
+       else if (!strcmp(var, "core.filemode"))
+               core_filemode = git_config_bool(var, value);
+       return 0;
 }
 
 static int init_stat(void)
 {
        if (have_git_dir()) {
                git_config(git_cygwin_config, NULL);
-               if (!trust_executable_bit && native_stat) {
+               if (!core_filemode && native_stat) {
                        cygwin_stat_fn = cygwin_stat;
                        cygwin_lstat_fn = cygwin_lstat;
                } else {
index 67d2cd86a89a92f8f75baa7ae5e820ac3150f3b4..584e04c217da4ea8943e33c77fea56ce64547ed1 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -70,6 +70,9 @@ struct ref **get_remote_heads(int in, struct ref **list,
                if (buffer[len-1] == '\n')
                        buffer[--len] = 0;
 
+               if (len > 4 && !prefixcmp(buffer, "ERR "))
+                       die("remote error: %s", buffer + 4);
+
                if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
                        die("protocol error: expected sha/ref, got '%s'", buffer);
                name = buffer + 41;
@@ -90,9 +93,8 @@ struct ref **get_remote_heads(int in, struct ref **list,
                        continue;
                if (nr_match && !path_match(name, nr_match, match))
                        continue;
-               ref = alloc_ref(name_len + 1);
+               ref = alloc_ref(buffer + 41);
                hashcpy(ref->old_sha1, old_sha1);
-               memcpy(ref->name, buffer + 41, name_len + 1);
                *list = ref;
                list = &ref->next;
        }
index eebe73409bebb8e8c2bb448f1612117cabf8d2ad..de193ba7c1caf69367410d763f3541555a64746f 100755 (executable)
@@ -1398,6 +1398,8 @@ _git_shortlog ()
 
 _git_show ()
 {
+       __git_has_doubledash && return
+
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --pretty=*)
index 3e5582d28921af22357f2d6068ef55f847bd016a..b9ba44c582b3b6cfac0f150f7f8901d60b48bbb3 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -537,6 +537,10 @@ static int execute(struct sockaddr *addr)
 #endif
                }
                loginfo("Connection from %s:%d", addrbuf, port);
+               setenv("REMOTE_ADDR", addrbuf, 1);
+       }
+       else {
+               unsetenv("REMOTE_ADDR");
        }
 
        alarm(init_timeout ? init_timeout : timeout);
diff --git a/diff.c b/diff.c
index 1c6be897b2c95fc481c02834e4fe022b6bd405ae..e368fef14fdccf63fc3525349841ae93aea93046 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -11,6 +11,7 @@
 #include "attr.h"
 #include "run-command.h"
 #include "utf8.h"
+#include "userdiff.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -37,6 +38,9 @@ static char diff_colors[][COLOR_MAXLEN] = {
        "\033[41m",     /* WHITESPACE (red background) */
 };
 
+static void diff_filespec_load_driver(struct diff_filespec *one);
+static char *run_textconv(const char *, struct diff_filespec *, size_t *);
+
 static int parse_diff_color_slot(const char *var, int ofs)
 {
        if (!strcasecmp(var+ofs, "plain"))
@@ -56,80 +60,6 @@ static int parse_diff_color_slot(const char *var, int ofs)
        die("bad config variable '%s'", var);
 }
 
-static struct ll_diff_driver {
-       const char *name;
-       struct ll_diff_driver *next;
-       const char *cmd;
-} *user_diff, **user_diff_tail;
-
-/*
- * Currently there is only "diff.<drivername>.command" variable;
- * because there are "diff.color.<slot>" variables, we are parsing
- * this in a bit convoluted way to allow low level diff driver
- * called "color".
- */
-static int parse_lldiff_command(const char *var, const char *ep, const char *value)
-{
-       const char *name;
-       int namelen;
-       struct ll_diff_driver *drv;
-
-       name = var + 5;
-       namelen = ep - name;
-       for (drv = user_diff; drv; drv = drv->next)
-               if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
-                       break;
-       if (!drv) {
-               drv = xcalloc(1, sizeof(struct ll_diff_driver));
-               drv->name = xmemdupz(name, namelen);
-               if (!user_diff_tail)
-                       user_diff_tail = &user_diff;
-               *user_diff_tail = drv;
-               user_diff_tail = &(drv->next);
-       }
-
-       return git_config_string(&(drv->cmd), var, value);
-}
-
-/*
- * 'diff.<what>.funcname' attribute can be specified in the configuration
- * to define a customized regexp to find the beginning of a function to
- * be used for hunk header lines of "diff -p" style output.
- */
-struct funcname_pattern_entry {
-       char *name;
-       char *pattern;
-       int cflags;
-};
-static struct funcname_pattern_list {
-       struct funcname_pattern_list *next;
-       struct funcname_pattern_entry e;
-} *funcname_pattern_list;
-
-static int parse_funcname_pattern(const char *var, const char *ep, const char *value, int cflags)
-{
-       const char *name;
-       int namelen;
-       struct funcname_pattern_list *pp;
-
-       name = var + 5; /* "diff." */
-       namelen = ep - name;
-
-       for (pp = funcname_pattern_list; pp; pp = pp->next)
-               if (!strncmp(pp->e.name, name, namelen) && !pp->e.name[namelen])
-                       break;
-       if (!pp) {
-               pp = xcalloc(1, sizeof(*pp));
-               pp->e.name = xmemdupz(name, namelen);
-               pp->next = funcname_pattern_list;
-               funcname_pattern_list = pp;
-       }
-       free(pp->e.pattern);
-       pp->e.pattern = xstrdup(value);
-       pp->e.cflags = cflags;
-       return 0;
-}
-
 /*
  * These are to give UI layer defaults.
  * The core-level commands such as git-diff-files should
@@ -162,11 +92,11 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
        }
        if (!strcmp(var, "diff.external"))
                return git_config_string(&external_diff_cmd_cfg, var, value);
-       if (!prefixcmp(var, "diff.")) {
-               const char *ep = strrchr(var, '.');
 
-               if (ep != var + 4 && !strcmp(ep, ".command"))
-                       return parse_lldiff_command(var, ep, value);
+       switch (userdiff_config_porcelain(var, value)) {
+               case 0: break;
+               case -1: return -1;
+               default: return 0;
        }
 
        return git_diff_basic_config(var, value, cb);
@@ -193,21 +123,10 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!prefixcmp(var, "diff.")) {
-               const char *ep = strrchr(var, '.');
-               if (ep != var + 4) {
-                       if (!strcmp(ep, ".funcname")) {
-                               if (!value)
-                                       return config_error_nonbool(var);
-                               return parse_funcname_pattern(var, ep, value,
-                                       0);
-                       } else if (!strcmp(ep, ".xfuncname")) {
-                               if (!value)
-                                       return config_error_nonbool(var);
-                               return parse_funcname_pattern(var, ep, value,
-                                       REG_EXTENDED);
-                       }
-               }
+       switch (userdiff_config_basic(var, value)) {
+               case 0: break;
+               case -1: return -1;
+               default: return 0;
        }
 
        return git_color_default_config(var, value, cb);
@@ -374,8 +293,19 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
        }
        else if (diff_populate_filespec(one, 0))
                return -1;
-       mf->ptr = one->data;
-       mf->size = one->size;
+
+       diff_filespec_load_driver(one);
+       if (one->driver->textconv) {
+               size_t size;
+               mf->ptr = run_textconv(one->driver->textconv, one, &size);
+               if (!mf->ptr)
+                       return -1;
+               mf->size = size;
+       }
+       else {
+               mf->ptr = one->data;
+               mf->size = one->size;
+       }
        return 0;
 }
 
@@ -1352,136 +1282,37 @@ static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)
        emit_binary_diff_body(file, two, one);
 }
 
-static void setup_diff_attr_check(struct git_attr_check *check)
+void diff_filespec_load_driver(struct diff_filespec *one)
 {
-       static struct git_attr *attr_diff;
-
-       if (!attr_diff) {
-               attr_diff = git_attr("diff", 4);
-       }
-       check[0].attr = attr_diff;
-}
-
-static void diff_filespec_check_attr(struct diff_filespec *one)
-{
-       struct git_attr_check attr_diff_check;
-       int check_from_data = 0;
-
-       if (one->checked_attr)
-               return;
-
-       setup_diff_attr_check(&attr_diff_check);
-       one->is_binary = 0;
-       one->funcname_pattern_ident = NULL;
-
-       if (!git_checkattr(one->path, 1, &attr_diff_check)) {
-               const char *value;
-
-               /* binaryness */
-               value = attr_diff_check.value;
-               if (ATTR_TRUE(value))
-                       ;
-               else if (ATTR_FALSE(value))
-                       one->is_binary = 1;
-               else
-                       check_from_data = 1;
-
-               /* funcname pattern ident */
-               if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value))
-                       ;
-               else
-                       one->funcname_pattern_ident = value;
-       }
-
-       if (check_from_data) {
-               if (!one->data && DIFF_FILE_VALID(one))
-                       diff_populate_filespec(one, 0);
-
-               if (one->data)
-                       one->is_binary = buffer_is_binary(one->data, one->size);
-       }
+       if (!one->driver)
+               one->driver = userdiff_find_by_path(one->path);
+       if (!one->driver)
+               one->driver = userdiff_find_by_name("default");
 }
 
 int diff_filespec_is_binary(struct diff_filespec *one)
 {
-       diff_filespec_check_attr(one);
+       if (one->is_binary == -1) {
+               diff_filespec_load_driver(one);
+               if (one->driver->binary != -1)
+                       one->is_binary = one->driver->binary;
+               else {
+                       if (!one->data && DIFF_FILE_VALID(one))
+                               diff_populate_filespec(one, 0);
+                       if (one->data)
+                               one->is_binary = buffer_is_binary(one->data,
+                                               one->size);
+                       if (one->is_binary == -1)
+                               one->is_binary = 0;
+               }
+       }
        return one->is_binary;
 }
 
-static const struct funcname_pattern_entry *funcname_pattern(const char *ident)
-{
-       struct funcname_pattern_list *pp;
-
-       for (pp = funcname_pattern_list; pp; pp = pp->next)
-               if (!strcmp(ident, pp->e.name))
-                       return &pp->e;
-       return NULL;
-}
-
-static const struct funcname_pattern_entry builtin_funcname_pattern[] = {
-       { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
-         REG_EXTENDED },
-       { "html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$", REG_EXTENDED },
-       { "java",
-         "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
-         "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
-         REG_EXTENDED },
-       { "objc",
-         /* Negate C statements that can look like functions */
-         "!^[ \t]*(do|for|if|else|return|switch|while)\n"
-         /* Objective-C methods */
-         "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
-         /* C functions */
-         "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
-         /* Objective-C class/protocol definitions */
-         "^(@(implementation|interface|protocol)[ \t].*)$",
-         REG_EXTENDED },
-       { "pascal",
-         "^((procedure|function|constructor|destructor|interface|"
-               "implementation|initialization|finalization)[ \t]*.*)$"
-         "\n"
-         "^(.*=[ \t]*(class|record).*)$",
-         REG_EXTENDED },
-       { "php", "^[\t ]*((function|class).*)", REG_EXTENDED },
-       { "python", "^[ \t]*((class|def)[ \t].*)$", REG_EXTENDED },
-       { "ruby", "^[ \t]*((class|module|def)[ \t].*)$",
-         REG_EXTENDED },
-       { "tex",
-         "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
-         REG_EXTENDED },
-};
-
-static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one)
+static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one)
 {
-       const char *ident;
-       const struct funcname_pattern_entry *pe;
-       int i;
-
-       diff_filespec_check_attr(one);
-       ident = one->funcname_pattern_ident;
-
-       if (!ident)
-               /*
-                * If the config file has "funcname.default" defined, that
-                * regexp is used; otherwise NULL is returned and xemit uses
-                * the built-in default.
-                */
-               return funcname_pattern("default");
-
-       /* Look up custom "funcname.$ident" regexp from config. */
-       pe = funcname_pattern(ident);
-       if (pe)
-               return pe;
-
-       /*
-        * And define built-in fallback patterns here.  Note that
-        * these can be overridden by the user's config settings.
-        */
-       for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++)
-               if (!strcmp(ident, builtin_funcname_pattern[i].name))
-                       return &builtin_funcname_pattern[i];
-
-       return NULL;
+       diff_filespec_load_driver(one);
+       return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
 }
 
 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
@@ -1579,7 +1410,7 @@ static void builtin_diff(const char *name_a,
                xdemitconf_t xecfg;
                xdemitcb_t ecb;
                struct emit_callback ecbdata;
-               const struct funcname_pattern_entry *pe;
+               const struct userdiff_funcname *pe;
 
                pe = diff_funcname_pattern(one);
                if (!pe)
@@ -1733,6 +1564,7 @@ struct diff_filespec *alloc_filespec(const char *path)
        spec->path = (char *)(spec + 1);
        memcpy(spec->path, path, namelen+1);
        spec->count = 1;
+       spec->is_binary = -1;
        return spec;
 }
 
@@ -2117,29 +1949,6 @@ static void run_external_diff(const char *pgm,
        }
 }
 
-static const char *external_diff_attr(const char *name)
-{
-       struct git_attr_check attr_diff_check;
-
-       if (!name)
-               return NULL;
-
-       setup_diff_attr_check(&attr_diff_check);
-       if (!git_checkattr(name, 1, &attr_diff_check)) {
-               const char *value = attr_diff_check.value;
-               if (!ATTR_TRUE(value) &&
-                   !ATTR_FALSE(value) &&
-                   !ATTR_UNSET(value)) {
-                       struct ll_diff_driver *drv;
-
-                       for (drv = user_diff; drv; drv = drv->next)
-                               if (!strcmp(drv->name, value))
-                                       return drv->cmd;
-               }
-       }
-       return NULL;
-}
-
 static void run_diff_cmd(const char *pgm,
                         const char *name,
                         const char *other,
@@ -2153,9 +1962,9 @@ static void run_diff_cmd(const char *pgm,
        if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
                pgm = NULL;
        else {
-               const char *cmd = external_diff_attr(attr_path);
-               if (cmd)
-                       pgm = cmd;
+               struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
+               if (drv && drv->external)
+                       pgm = drv->external;
        }
 
        if (pgm) {
@@ -3578,3 +3387,34 @@ void diff_unmerge(struct diff_options *options,
        fill_filespec(one, sha1, mode);
        diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
 }
+
+static char *run_textconv(const char *pgm, struct diff_filespec *spec,
+               size_t *outsize)
+{
+       struct diff_tempfile temp;
+       const char *argv[3];
+       const char **arg = argv;
+       struct child_process child;
+       struct strbuf buf = STRBUF_INIT;
+
+       prepare_temp_file(spec->path, &temp, spec);
+       *arg++ = pgm;
+       *arg++ = temp.name;
+       *arg = NULL;
+
+       memset(&child, 0, sizeof(child));
+       child.argv = argv;
+       child.out = -1;
+       if (start_command(&child) != 0 ||
+           strbuf_read(&buf, child.out, 0) < 0 ||
+           finish_command(&child) != 0) {
+               if (temp.name == temp.tmp_path)
+                       unlink(temp.name);
+               error("error running textconv command '%s'", pgm);
+               return NULL;
+       }
+       if (temp.name == temp.tmp_path)
+               unlink(temp.name);
+
+       return strbuf_detach(&buf, outsize);
+}
index 1b2ebb40014d820fe4fb679509ab694d453be7b4..168a95b541c2d6a4679115ebc9f30b1016645b19 100644 (file)
@@ -493,7 +493,7 @@ void diffcore_rename(struct diff_options *options)
        if ((num_create > rename_limit && num_src > rename_limit) ||
            (num_create * num_src > rename_limit * rename_limit)) {
                if (options->warn_on_too_large_rename)
-                       warning("too many files, skipping inexact rename detection");
+                       warning("too many files (created: %d deleted: %d), skipping inexact rename detection", num_create, num_src);
                goto cleanup;
        }
 
index 8ae35785fd4439f466620ab9186838e7cb20fa36..713cca785c4b986b5f667086ad43c324f248929c 100644 (file)
@@ -22,6 +22,8 @@
 
 #define MINIMUM_BREAK_SIZE     400 /* do not break a file smaller than this */
 
+struct userdiff_driver;
+
 struct diff_filespec {
        unsigned char sha1[20];
        char *path;
@@ -40,8 +42,10 @@ struct diff_filespec {
 #define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
        unsigned should_free : 1; /* data should be free()'ed */
        unsigned should_munmap : 1; /* data should be munmap()'ed */
-       unsigned checked_attr : 1;
-       unsigned is_binary : 1; /* data should be considered "binary" */
+
+       struct userdiff_driver *driver;
+       /* data should be considered "binary"; -1 means "don't know yet" */
+       int is_binary;
 };
 
 extern struct diff_filespec *alloc_filespec(const char *);
index da768ee7acc22e6480f0a067e109239561d43927..b0223c3419301132032fb67519a275e57707df22 100755 (executable)
@@ -811,11 +811,16 @@ sub help_patch_cmd {
 }
 
 sub patch_update_cmd {
-       my @mods = grep { !($_->{BINARY}) } list_modified('file-only');
+       my @all_mods = list_modified('file-only');
+       my @mods = grep { !($_->{BINARY}) } @all_mods;
        my @them;
 
        if (!@mods) {
-               print STDERR "No changes.\n";
+               if (@all_mods) {
+                       print STDERR "Only binary files changed.\n";
+               } else {
+                       print STDERR "No changes.\n";
+               }
                return 0;
        }
        if ($patch_mode) {
index 55765c8a3aa6b3702b230e8efe3c2ab47a6e73e5..3ad8a21b30128ce21b6b6c28094d9af5e5866faf 100644 (file)
@@ -285,6 +285,7 @@ all:: $(GITGUI_MAIN) lib/tclIndex $(ALL_MSGFILES)
 install: all
        $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1)
        $(QUIET)$(INSTALL_X0)git-gui $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
+       $(QUIET)$(INSTALL_X0)git-gui--askpass $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(INSTALL_L0)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L1)'$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' $(INSTALL_L2)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L3) &&) true
 ifdef GITGUI_WINDOWS_WRAPPER
        $(QUIET)$(INSTALL_R0)git-gui.tcl $(INSTALL_R1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
@@ -302,6 +303,7 @@ endif
 uninstall:
        $(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui $(REMOVE_F1)
+       $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askpass $(REMOVE_F1)
        $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/$p $(REMOVE_F1) &&) true
 ifdef GITGUI_WINDOWS_WRAPPER
        $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui.tcl $(REMOVE_F1)
diff --git a/git-gui/git-gui--askpass b/git-gui/git-gui--askpass
new file mode 100755 (executable)
index 0000000..12e117e
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+# Tcl ignores the next line -*- tcl -*- \
+exec wish "$0" -- "$@"
+
+# This is a trivial implementation of an SSH_ASKPASS handler.
+# Git-gui uses this script if none are already configured.
+
+set answer {}
+set yesno  0
+set rc     255
+
+if {$argc < 1} {
+       set prompt "Enter your OpenSSH passphrase:"
+} else {
+       set prompt [join $argv " "]
+       if {[regexp -nocase {\(yes\/no\)\?\s*$} $prompt]} {
+               set yesno 1
+       }
+}
+
+message .m -text $prompt -justify center -aspect 4000
+pack .m -side top -fill x -padx 20 -pady 20 -expand 1
+
+entry .e -textvariable answer -width 50
+pack .e -side top -fill x -padx 10 -pady 10
+
+if {!$yesno} {
+       .e configure -show "*"
+}
+
+frame .b
+button .b.ok     -text OK     -command finish
+button .b.cancel -text Cancel -command {destroy .}
+
+pack .b.ok -side left -expand 1
+pack .b.cancel -side right -expand 1
+pack .b -side bottom -fill x -padx 10 -pady 10
+
+bind . <Visibility> {focus -force .e}
+bind . <Key-Return> finish
+bind . <Key-Escape> {destroy .}
+bind . <Destroy>    {exit $rc}
+
+proc finish {} {
+       if {$::yesno} {
+               if {$::answer ne "yes" && $::answer ne "no"} {
+                       tk_messageBox -icon error -title "Error" -type ok \
+                               -message "Only 'yes' or 'no' input allowed."
+                       return
+               }
+       }
+
+       set ::rc 0
+       puts $::answer
+       destroy .
+}
+
+wm title . "OpenSSH"
+tk::PlaceWindow .
index 4085e8fea59f2b819812f20185df89379d851c0b..12b496bec9233a86da18a45073d964bd559f059c 100755 (executable)
@@ -591,6 +591,12 @@ bind . <Visibility> {
 
 if {[is_Windows]} {
        wm iconbitmap . -default $oguilib/git-gui.ico
+       set ::tk::AlwaysShowSelection 1
+
+       # Spoof an X11 display for SSH
+       if {![info exists env(DISPLAY)]} {
+               set env(DISPLAY) :9999
+       }
 }
 
 ######################################################################
@@ -995,6 +1001,7 @@ citool {
 ##
 ## repository setup
 
+set picked 0
 if {[catch {
                set _gitdir $env(GIT_DIR)
                set _prefix {}
@@ -1006,6 +1013,7 @@ if {[catch {
        load_config 1
        apply_config
        choose_repository::pick
+       set picked 1
 }
 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
        catch {set _gitdir [exec cygpath --windows $_gitdir]}
@@ -1065,6 +1073,15 @@ set selected_commit_type new
 set nullid "0000000000000000000000000000000000000000"
 set nullid2 "0000000000000000000000000000000000000001"
 
+set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
+
+######################################################################
+
+# Suggest our implementation of askpass, if none is set
+if {![info exists env(SSH_ASKPASS)]} {
+       set env(SSH_ASKPASS) [gitexec git-gui--askpass]
+}
+
 ######################################################################
 ##
 ## task management
@@ -1869,6 +1886,19 @@ proc do_gitk {revs} {
        }
 }
 
+proc do_explore {} {
+       set explorer {}
+       if {[is_Cygwin] || [is_Windows]} {
+               set explorer "explorer.exe"
+       } elseif {[is_MacOSX]} {
+               set explorer "open"
+       } else {
+               # freedesktop.org-conforming system is our best shot
+               set explorer "xdg-open"
+       }
+       eval exec $explorer [file dirname [gitdir]] &
+}
+
 set is_quitting 0
 set ret_code    1
 
@@ -2090,7 +2120,9 @@ proc toggle_or_diff {w x y} {
        if {$col == 0 && $y > 1} {
                # Conflicts need special handling
                if {[string first {U} $state] >= 0} {
-                       merge_stage_workdir $path $w $lno
+                       # $w must always be $ui_workdir, but...
+                       if {$w ne $ui_workdir} { set lno {} }
+                       merge_stage_workdir $path $lno
                        return
                }
 
@@ -2218,6 +2250,11 @@ if {[is_enabled transport]} {
 #
 menu .mbar.repository
 
+.mbar.repository add command \
+       -label [mc "Explore Working Copy"] \
+       -command {do_explore}
+.mbar.repository add separator
+
 .mbar.repository add command \
        -label [mc "Browse Current Branch's Files"] \
        -command {browser::new $current_branch}
@@ -2413,7 +2450,7 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
 
        .mbar.commit add separator
 
-       if {![is_enabled nocommit]} {
+       if {![is_enabled nocommitmsg]} {
                .mbar.commit add command -label [mc "Sign Off"] \
                        -command do_signoff \
                        -accelerator $M1T-S
@@ -2446,12 +2483,16 @@ if {[is_enabled branch]} {
 if {[is_enabled transport]} {
        menu .mbar.remote
 
+       .mbar.remote add command \
+               -label [mc "Add..."] \
+               -command remote_add::dialog \
+               -accelerator $M1T-A
        .mbar.remote add command \
                -label [mc "Push..."] \
                -command do_push_anywhere \
                -accelerator $M1T-P
        .mbar.remote add command \
-               -label [mc "Delete..."] \
+               -label [mc "Delete Branch..."] \
                -command remote_branch_delete::dialog
 }
 
@@ -2487,8 +2528,7 @@ if {![is_MacOSX]} {
                -command do_about
 }
 
-set browser {}
-catch {set browser $repo_config(instaweb.browser)}
+
 set doc_path [file dirname [gitexec]]
 set doc_path [file join $doc_path Documentation index.html]
 
@@ -2496,34 +2536,23 @@ if {[is_Cygwin]} {
        set doc_path [exec cygpath --mixed $doc_path]
 }
 
-if {$browser eq {}} {
-       if {[is_MacOSX]} {
-               set browser open
-       } elseif {[is_Cygwin]} {
-               set program_files [file dirname [exec cygpath --windir]]
-               set program_files [file join $program_files {Program Files}]
-               set firefox [file join $program_files {Mozilla Firefox} firefox.exe]
-               set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE]
-               if {[file exists $firefox]} {
-                       set browser $firefox
-               } elseif {[file exists $ie]} {
-                       set browser $ie
-               }
-               unset program_files firefox ie
-       }
-}
-
 if {[file isfile $doc_path]} {
        set doc_url "file:$doc_path"
 } else {
        set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
 }
 
-if {$browser ne {}} {
-       .mbar.help add command -label [mc "Online Documentation"] \
-               -command [list exec $browser $doc_url &]
+proc start_browser {url} {
+       git "web--browse" $url
 }
-unset browser doc_path doc_url
+
+.mbar.help add command -label [mc "Online Documentation"] \
+       -command [list start_browser $doc_url]
+
+.mbar.help add command -label [mc "Show SSH Key"] \
+       -command do_ssh_key
+
+unset doc_path doc_url
 
 # -- Standard bindings
 #
@@ -2743,7 +2772,7 @@ pack .vpane.lower.commarea.buttons.incall -side top -fill x
 lappend disable_on_lock \
        {.vpane.lower.commarea.buttons.incall conf -state}
 
-if {![is_enabled nocommit]} {
+if {![is_enabled nocommitmsg]} {
        button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
                -command do_signoff
        pack .vpane.lower.commarea.buttons.signoff -side top -fill x
@@ -3261,8 +3290,7 @@ if {[is_enabled transport]} {
        load_all_remotes
 
        set n [.mbar.remote index end]
-       populate_push_menu
-       populate_fetch_menu
+       populate_remotes_menu
        set n [expr {[.mbar.remote index end] - $n}]
        if {$n > 0} {
                if {[.mbar.remote type 0] eq "tearoff"} { incr n }
@@ -3369,3 +3397,6 @@ if {[is_enabled multicommit]} {
 if {[is_enabled retcode]} {
        bind . <Destroy> {+terminate_me %W}
 }
+if {$picked && [is_config_true gui.autoexplore]} {
+       do_explore
+}
index eb61374d2db79a1199d455dfdda85e2d063bf1f6..765d08c004b68254713e2524b53d6df0bdc0c007 100644 (file)
@@ -21,9 +21,11 @@ field w_amov     ; # text column: annotations + move tracking
 field w_asim     ; # text column: annotations (simple computation)
 field w_file     ; # text column: actual file data
 field w_cviewer  ; # pane showing commit message
+field finder     ; # find mini-dialog frame
 field status     ; # status mega-widget instance
 field old_height ; # last known height of $w.file_pane
 
+
 # Tk UI colors
 #
 variable active_color #c0edc5
@@ -59,7 +61,7 @@ field tooltip_timer     {} ; # Current timer event for our tooltip
 field tooltip_commit    {} ; # Commit(s) in tooltip
 
 constructor new {i_commit i_path i_jump} {
-       global cursor_ptr
+       global cursor_ptr M1B M1T have_tk85
        variable active_color
        variable group_colors
 
@@ -69,6 +71,8 @@ constructor new {i_commit i_path i_jump} {
        make_toplevel top w
        wm title $top [append "[appname] ([reponame]): " [mc "File Viewer"]]
 
+       set font_w [font measure font_diff "0"]
+
        frame $w.header -background gold
        label $w.header.commit_l \
                -text [mc "Commit:"] \
@@ -114,9 +118,9 @@ constructor new {i_commit i_path i_jump} {
        pack $w_path -fill x -side right
        pack $w.header.path_l -side right
 
-       panedwindow $w.file_pane -orient vertical
-       frame $w.file_pane.out
-       frame $w.file_pane.cm
+       panedwindow $w.file_pane -orient vertical -borderwidth 0 -sashwidth 3
+       frame $w.file_pane.out -relief flat -borderwidth 1
+       frame $w.file_pane.cm -relief sunken -borderwidth 1
        $w.file_pane add $w.file_pane.out \
                -sticky nsew \
                -minsize 100 \
@@ -197,6 +201,11 @@ constructor new {i_commit i_path i_jump} {
                -width 80 \
                -xscrollcommand [list $w.file_pane.out.sbx set] \
                -font font_diff
+       if {$have_tk85} {
+               $w_file configure -inactiveselectbackground darkblue
+       }
+       $w_file tag conf found \
+               -background yellow
 
        set w_columns [list $w_amov $w_asim $w_line $w_file]
 
@@ -217,6 +226,11 @@ constructor new {i_commit i_path i_jump} {
                -weight 1
        grid rowconfigure $w.file_pane.out 0 -weight 1
 
+       set finder [::searchbar::new \
+               $w.file_pane.out.ff $w_file \
+               -column [expr {[llength $w_columns] - 1}] \
+               ]
+
        set w_cviewer $w.file_pane.cm.t
        text $w_cviewer \
                -background white \
@@ -257,6 +271,10 @@ constructor new {i_commit i_path i_jump} {
                -label [mc "Copy Commit"] \
                -command [cb _copycommit]
        $w.ctxm add separator
+       $w.ctxm add command \
+               -label [mc "Find Text..."] \
+               -accelerator F7 \
+               -command [list searchbar::show $finder]
        menu $w.ctxm.enc
        build_encoding_menu $w.ctxm.enc [cb _setencoding]
        $w.ctxm add cascade \
@@ -278,9 +296,15 @@ constructor new {i_commit i_path i_jump} {
                        $i tag conf color$g -background [lindex $group_colors $g]
                }
 
+               if {$i eq $w_file} {
+                       $w_file tag raise found
+               }
+               $i tag raise sel
+
                $i conf -cursor $cursor_ptr
-               $i conf -yscrollcommand [list many2scrollbar \
-                       $w_columns yview $w.file_pane.out.sby]
+               $i conf -yscrollcommand \
+                       "[list ::searchbar::scrolled $finder]
+                        [list many2scrollbar $w_columns yview $w.file_pane.out.sby]"
                bind $i <Button-1> "
                        [cb _hide_tooltip]
                        [cb _click $i @%x,%y]
@@ -317,6 +341,11 @@ constructor new {i_commit i_path i_jump} {
        bind $w_cviewer <Tab>       "[list focus $w_file];break"
        bind $w_cviewer <Button-1> [list focus $w_cviewer]
        bind $w_file    <Visibility> [list focus $w_file]
+       bind $top       <F7>         [list searchbar::show $finder]
+       bind $top       <Escape>     [list searchbar::hide $finder]
+       bind $top       <F3>         [list searchbar::find_next $finder]
+       bind $top       <Shift-F3>   [list searchbar::find_prev $finder]
+       catch { bind $top <Shift-Key-XF86_Switch_VT_3> [list searchbar::find_prev $finder] }
 
        grid configure $w.header -sticky ew
        grid configure $w.file_pane -sticky nsew
@@ -328,9 +357,14 @@ constructor new {i_commit i_path i_jump} {
 
        set req_w [winfo reqwidth  $top]
        set req_h [winfo reqheight $top]
-       set scr_h [expr {[winfo screenheight $top] - 100}]
-       if {$req_w < 600} {set req_w 600}
+       set scr_w [expr {[winfo screenwidth $top] - 40}]
+       set scr_h [expr {[winfo screenheight $top] - 120}]
+       set opt_w [expr {$font_w * (80 + 5*3 + 3)}]
+       if {$req_w < $opt_w} {set req_w $opt_w}
+       if {$req_w > $scr_w} {set req_w $scr_w}
+       set opt_h [expr {$req_w*4/3}]
        if {$req_h < $scr_h} {set req_h $scr_h}
+       if {$req_h > $opt_h} {set req_h $opt_h}
        set g "${req_w}x${req_h}"
        wm geometry $top $g
        update
@@ -338,16 +372,23 @@ constructor new {i_commit i_path i_jump} {
        set old_height [winfo height $w.file_pane]
        $w.file_pane sash place 0 \
                [lindex [$w.file_pane sash coord 0] 0] \
-               [expr {int($old_height * 0.70)}]
+               [expr {int($old_height * 0.80)}]
        bind $w.file_pane <Configure> \
        "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}"
 
        wm protocol $top WM_DELETE_WINDOW "destroy $top"
-       bind $top <Destroy> [cb _kill]
+       bind $top <Destroy> [cb _handle_destroy %W]
 
        _load $this $i_jump
 }
 
+method _handle_destroy {win} {
+       if {$win eq $w} {
+               _kill $this
+               delete_this
+       }
+}
+
 method _kill {} {
        if {$current_fd ne {}} {
                kill_file_process $current_fd
@@ -866,6 +907,10 @@ method _showcommit {cur_w lno} {
                foreach i $w_columns {
                        $i tag conf g$cmit -background $active_color
                        $i tag raise g$cmit
+                       if {$i eq $w_file} {
+                               $w_file tag raise found
+                       }
+                       $i tag raise sel
                }
 
                set author_name {}
index 318078615862adab052d0d0f637f82176a0a785a..909131689ea837cb67bd587a6acc8b5eb3127c4e 100644 (file)
@@ -381,7 +381,8 @@ method _do_new {} {
        label $w_body.where.l -text [mc "Directory:"]
        entry $w_body.where.t \
                -textvariable @local_path \
-               -font font_diff \
+               -borderwidth 1 \
+               -relief sunken \
                -width 50
        button $w_body.where.b \
                -text [mc "Browse"] \
@@ -463,20 +464,22 @@ method _do_clone {} {
        frame $w_body.args
        pack $args -fill both
 
-       label $args.origin_l -text [mc "URL:"]
+       label $args.origin_l -text [mc "Source Location:"]
        entry $args.origin_t \
                -textvariable @origin_url \
-               -font font_diff \
+               -borderwidth 1 \
+               -relief sunken \
                -width 50
        button $args.origin_b \
                -text [mc "Browse"] \
                -command [cb _open_origin]
        grid $args.origin_l $args.origin_t $args.origin_b -sticky ew
 
-       label $args.where_l -text [mc "Directory:"]
+       label $args.where_l -text [mc "Target Directory:"]
        entry $args.where_t \
                -textvariable @local_path \
-               -font font_diff \
+               -borderwidth 1 \
+               -relief sunken \
                -width 50
        button $args.where_b \
                -text [mc "Browse"] \
@@ -979,7 +982,8 @@ method _do_open {} {
        label $w_body.where.l -text [mc "Repository:"]
        entry $w_body.where.t \
                -textvariable @local_path \
-               -font font_diff \
+               -borderwidth 1 \
+               -relief sunken \
                -width 50
        button $w_body.where.b \
                -text [mc "Browse"] \
index abe502d97104cb62bed308b0f5025c9d014834fb..94ee38cccc1ee9a43f8dd1309353609b283d8af5 100644 (file)
@@ -117,22 +117,22 @@ proc show_unmerged_diff {cont_info} {
        if {$merge_stages(2) eq {}} {
                set is_conflict_diff 1
                lappend current_diff_queue \
-                       [list "LOCAL: deleted\nREMOTE:\n" d======= \
+                       [list [mc "LOCAL: deleted\nREMOTE:\n"] d======= \
                            [list ":1:$current_diff_path" ":3:$current_diff_path"]]
        } elseif {$merge_stages(3) eq {}} {
                set is_conflict_diff 1
                lappend current_diff_queue \
-                       [list "REMOTE: deleted\nLOCAL:\n" d======= \
+                       [list [mc "REMOTE: deleted\nLOCAL:\n"] d======= \
                            [list ":1:$current_diff_path" ":2:$current_diff_path"]]
        } elseif {[lindex $merge_stages(1) 0] eq {120000}
                || [lindex $merge_stages(2) 0] eq {120000}
                || [lindex $merge_stages(3) 0] eq {120000}} {
                set is_conflict_diff 1
                lappend current_diff_queue \
-                       [list "LOCAL:\n" d======= \
+                       [list [mc "LOCAL:\n"] d======= \
                            [list ":1:$current_diff_path" ":2:$current_diff_path"]]
                lappend current_diff_queue \
-                       [list "REMOTE:\n" d======= \
+                       [list [mc "REMOTE:\n"] d======= \
                            [list ":1:$current_diff_path" ":3:$current_diff_path"]]
        } else {
                start_show_diff $cont_info
@@ -164,7 +164,7 @@ proc show_other_diff {path w m cont_info} {
        # - Git won't give us the diff, there's nothing to compare to!
        #
        if {$m eq {_O}} {
-               set max_sz [expr {128 * 1024}]
+               set max_sz 100000
                set type unknown
                if {[catch {
                                set type [file type $path]
@@ -218,17 +218,17 @@ proc show_other_diff {path w m cont_info} {
                                d_@
                } else {
                        if {$sz > $max_sz} {
-                               $ui_diff insert end \
-"* Untracked file is $sz bytes.
-* Showing only first $max_sz bytes.
-" d_@
+                               $ui_diff insert end [mc \
+"* Untracked file is %d bytes.
+* Showing only first %d bytes.
+" $sz $max_sz] d_@
                        }
                        $ui_diff insert end $content
                        if {$sz > $max_sz} {
-                               $ui_diff insert end "
-* Untracked file clipped here by [appname].
+                               $ui_diff insert end [mc "
+* Untracked file clipped here by %s.
 * To see the entire file, use an external editor.
-" d_@
+" [appname]] d_@
                        }
                }
                $ui_diff conf -state disabled
@@ -377,7 +377,6 @@ proc read_diff {fd cont_info} {
                        {+} {
                                if {[regexp {^\+([<>]{7} |={7})} $line _g op]} {
                                        set is_conflict_diff 1
-                                       set line [string replace $line 0 0 { }]
                                        set tags d$op
                                } else {
                                        set tags d_+
index b045219a1cca6bf46218bf211c0c88b8d699e144..d33896a0ce26dd34c8024e21c71123e62832b8c0 100644 (file)
@@ -298,11 +298,18 @@ proc add_helper {txt paths} {
        set after {}
        foreach path $paths {
                switch -glob -- [lindex $file_states($path) 0] {
+               _U -
+               U? {
+                       if {$path eq $current_diff_path} {
+                               unlock_index
+                               merge_stage_workdir $path
+                               return
+                       }
+               }
                _O -
                ?M -
                ?D -
-               ?T -
-               U? {
+               ?T {
                        lappend pathList $path
                        if {$path eq $current_diff_path} {
                                set after {reshow_diff;}
index 5c01875b051305b5b40a17ac7a2245f69081f41b..283e4915e928df0c188d8ed2ec49e49eeed3b519 100644 (file)
@@ -40,6 +40,7 @@ The rescan will be automatically started now.
                _O {
                        continue; # and pray it works!
                }
+               _U -
                U? {
                        error_popup [mc "You are in the middle of a conflicted merge.
 
index 6ab5701d937b9a719570907e745609b60d1d56a8..eb2b4b56a4db4c20727432c7a71d5192d580ce9e 100644 (file)
@@ -23,13 +23,14 @@ This operation can be undone only by restarting the merge." \
        }
 }
 
-proc merge_stage_workdir {path w lno} {
+proc merge_stage_workdir {path {lno {}}} {
        global current_diff_path diff_active
+       global current_diff_side ui_workdir
 
        if {$diff_active} return
 
-       if {$path ne $current_diff_path} {
-               show_diff $path $w $lno {} [list do_merge_stage_workdir $path]
+       if {$path ne $current_diff_path || $ui_workdir ne $current_diff_side} {
+               show_diff $path $ui_workdir $lno {} [list do_merge_stage_workdir $path]
        } else {
                do_merge_stage_workdir $path
        }
@@ -375,14 +376,6 @@ proc merge_tool_finish {fd} {
                }
        }
 
-       # Check the modification time of the target file
-       if {!$failed && [file mtime $mtool_target] eq $mtool_mtime} {
-               if {[ask_popup [mc "File %s unchanged, still accept as resolved?" \
-                               [short_path $mtool_target]]] ne {yes}} {
-                       set failed 1
-               }
-       }
-
        # Finish
        if {$failed} {
                file rename -force -- $backup $mtool_target
@@ -395,6 +388,6 @@ proc merge_tool_finish {fd} {
 
                delete_temp_files $mtool_tmpfiles
 
-               merge_add_resolution $mtool_target
+               reshow_diff
        }
 }
index 0e86ddac0981fbb575a7dd5294ddaed29f7c3917..b92b429cf766d525402047175ed0a69af015c682 100644 (file)
@@ -132,91 +132,145 @@ proc load_all_remotes {} {
        set all_remotes [lsort -unique $all_remotes]
 }
 
-proc populate_fetch_menu {} {
-       global all_remotes repo_config
-
+proc add_fetch_entry {r} {
+       global repo_config
        set remote_m .mbar.remote
        set fetch_m $remote_m.fetch
        set prune_m $remote_m.prune
-
-       foreach r $all_remotes {
-               set enable 0
-               if {![catch {set a $repo_config(remote.$r.url)}]} {
-                       if {![catch {set a $repo_config(remote.$r.fetch)}]} {
-                               set enable 1
-                       }
-               } else {
-                       catch {
-                               set fd [open [gitdir remotes $r] r]
-                               while {[gets $fd n] >= 0} {
-                                       if {[regexp {^Pull:[ \t]*([^:]+):} $n]} {
-                                               set enable 1
-                                               break
-                                       }
+       set remove_m $remote_m.remove
+       set enable 0
+       if {![catch {set a $repo_config(remote.$r.url)}]} {
+               if {![catch {set a $repo_config(remote.$r.fetch)}]} {
+                       set enable 1
+               }
+       } else {
+               catch {
+                       set fd [open [gitdir remotes $r] r]
+                       while {[gets $fd n] >= 0} {
+                               if {[regexp {^Pull:[ \t]*([^:]+):} $n]} {
+                                       set enable 1
+                                       break
                                }
-                               close $fd
                        }
+                       close $fd
                }
+       }
 
-               if {$enable} {
-                       if {![winfo exists $fetch_m]} {
-                               menu $prune_m
-                               $remote_m insert 0 cascade \
-                                       -label [mc "Prune from"] \
-                                       -menu $prune_m
-
-                               menu $fetch_m
-                               $remote_m insert 0 cascade \
-                                       -label [mc "Fetch from"] \
-                                       -menu $fetch_m
-                       }
+       if {$enable} {
+               if {![winfo exists $fetch_m]} {
+                       menu $remove_m
+                       $remote_m insert 0 cascade \
+                               -label [mc "Remove Remote"] \
+                               -menu $remove_m
+
+                       menu $prune_m
+                       $remote_m insert 0 cascade \
+                               -label [mc "Prune from"] \
+                               -menu $prune_m
 
-                       $fetch_m add command \
-                               -label $r \
-                               -command [list fetch_from $r]
-                       $prune_m add command \
-                               -label $r \
-                               -command [list prune_from $r]
+                       menu $fetch_m
+                       $remote_m insert 0 cascade \
+                               -label [mc "Fetch from"] \
+                               -menu $fetch_m
                }
+
+               $fetch_m add command \
+                       -label $r \
+                       -command [list fetch_from $r]
+               $prune_m add command \
+                       -label $r \
+                       -command [list prune_from $r]
+               $remove_m add command \
+                       -label $r \
+                       -command [list remove_remote $r]
        }
 }
 
-proc populate_push_menu {} {
-       global all_remotes repo_config
-
+proc add_push_entry {r} {
+       global repo_config
        set remote_m .mbar.remote
        set push_m $remote_m.push
-
-       foreach r $all_remotes {
-               set enable 0
-               if {![catch {set a $repo_config(remote.$r.url)}]} {
-                       if {![catch {set a $repo_config(remote.$r.push)}]} {
-                               set enable 1
-                       }
-               } else {
-                       catch {
-                               set fd [open [gitdir remotes $r] r]
-                               while {[gets $fd n] >= 0} {
-                                       if {[regexp {^Push:[ \t]*([^:]+):} $n]} {
-                                               set enable 1
-                                               break
-                                       }
+       set enable 0
+       if {![catch {set a $repo_config(remote.$r.url)}]} {
+               if {![catch {set a $repo_config(remote.$r.push)}]} {
+                       set enable 1
+               }
+       } else {
+               catch {
+                       set fd [open [gitdir remotes $r] r]
+                       while {[gets $fd n] >= 0} {
+                               if {[regexp {^Push:[ \t]*([^:]+):} $n]} {
+                                       set enable 1
+                                       break
                                }
-                               close $fd
                        }
+                       close $fd
                }
+       }
 
-               if {$enable} {
-                       if {![winfo exists $push_m]} {
-                               menu $push_m
-                               $remote_m insert 0 cascade \
-                                       -label [mc "Push to"] \
-                                       -menu $push_m
-                       }
-
-                       $push_m add command \
-                               -label $r \
-                               -command [list push_to $r]
+       if {$enable} {
+               if {![winfo exists $push_m]} {
+                       menu $push_m
+                       $remote_m insert 0 cascade \
+                               -label [mc "Push to"] \
+                               -menu $push_m
                }
+
+               $push_m add command \
+                       -label $r \
+                       -command [list push_to $r]
+       }
+}
+
+proc populate_remotes_menu {} {
+       global all_remotes
+
+       foreach r $all_remotes {
+               add_fetch_entry $r
+               add_push_entry $r
+       }
+}
+
+proc add_single_remote {name location} {
+       global all_remotes repo_config
+       lappend all_remotes $name
+
+       git remote add $name $location
+
+       # XXX: Better re-read the config so that we will never get out
+       # of sync with git remote implementation?
+       set repo_config(remote.$name.url) $location
+       set repo_config(remote.$name.fetch) "+refs/heads/*:refs/remotes/$name/*"
+
+       add_fetch_entry $name
+       add_push_entry $name
+}
+
+proc delete_from_menu {menu name} {
+       if {[winfo exists $menu]} {
+               $menu delete $name
        }
 }
+
+proc remove_remote {name} {
+       global all_remotes repo_config
+
+       git remote rm $name
+
+       catch {
+               # Missing values are ok
+               unset repo_config(remote.$name.url)
+               unset repo_config(remote.$name.fetch)
+               unset repo_config(remote.$name.push)
+       }
+
+       set i [lsearch -exact all_remotes $name]
+       lreplace all_remotes $i $i
+
+       set remote_m .mbar.remote
+       delete_from_menu $remote_m.fetch $name
+       delete_from_menu $remote_m.prune $name
+       delete_from_menu $remote_m.remove $name
+       # Not all remotes are in the push menu
+       catch { delete_from_menu $remote_m.push $name }
+}
diff --git a/git-gui/lib/remote_add.tcl b/git-gui/lib/remote_add.tcl
new file mode 100644 (file)
index 0000000..fb29422
--- /dev/null
@@ -0,0 +1,191 @@
+# git-gui remote adding support
+# Copyright (C) 2008 Petr Baudis
+
+class remote_add {
+
+field w              ; # widget path
+field w_name         ; # new remote name widget
+field w_loc          ; # new remote location widget
+
+field name         {}; # name of the remote the user has chosen
+field location     {}; # location of the remote the user has chosen
+
+field opt_action fetch; # action to do after registering the remote locally
+
+constructor dialog {} {
+       global repo_config
+
+       make_toplevel top w
+       wm title $top [append "[appname] ([reponame]): " [mc "Add Remote"]]
+       if {$top ne {.}} {
+               wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+       }
+
+       label $w.header -text [mc "Add New Remote"] -font font_uibold
+       pack $w.header -side top -fill x
+
+       frame $w.buttons
+       button $w.buttons.create -text [mc Add] \
+               -default active \
+               -command [cb _add]
+       pack $w.buttons.create -side right
+       button $w.buttons.cancel -text [mc Cancel] \
+               -command [list destroy $w]
+       pack $w.buttons.cancel -side right -padx 5
+       pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+       labelframe $w.desc -text [mc "Remote Details"]
+
+       label $w.desc.name_l -text [mc "Name:"]
+       set w_name $w.desc.name_t
+       entry $w_name \
+               -borderwidth 1 \
+               -relief sunken \
+               -width 40 \
+               -textvariable @name \
+               -validate key \
+               -validatecommand [cb _validate_name %d %S]
+       grid $w.desc.name_l $w_name -sticky we -padx {0 5}
+
+       label $w.desc.loc_l -text [mc "Location:"]
+       set w_loc $w.desc.loc_t
+       entry $w_loc \
+               -borderwidth 1 \
+               -relief sunken \
+               -width 40 \
+               -textvariable @location
+       grid $w.desc.loc_l $w_loc -sticky we -padx {0 5}
+
+       grid columnconfigure $w.desc 1 -weight 1
+       pack $w.desc -anchor nw -fill x -pady 5 -padx 5
+
+       labelframe $w.action -text [mc "Further Action"]
+
+       radiobutton $w.action.fetch \
+               -text [mc "Fetch Immediately"] \
+               -value fetch \
+               -variable @opt_action
+       pack $w.action.fetch -anchor nw
+
+       radiobutton $w.action.push \
+               -text [mc "Initialize Remote Repository and Push"] \
+               -value push \
+               -variable @opt_action
+       pack $w.action.push -anchor nw
+
+       radiobutton $w.action.none \
+               -text [mc "Do Nothing Else Now"] \
+               -value none \
+               -variable @opt_action
+       pack $w.action.none -anchor nw
+
+       grid columnconfigure $w.action 1 -weight 1
+       pack $w.action -anchor nw -fill x -pady 5 -padx 5
+
+       bind $w <Visibility> [cb _visible]
+       bind $w <Key-Escape> [list destroy $w]
+       bind $w <Key-Return> [cb _add]\;break
+       tkwait window $w
+}
+
+method _add {} {
+       global repo_config env
+       global M1B
+
+       if {$name eq {}} {
+               tk_messageBox \
+                       -icon error \
+                       -type ok \
+                       -title [wm title $w] \
+                       -parent $w \
+                       -message [mc "Please supply a remote name."]
+               focus $w_name
+               return
+       }
+
+       # XXX: We abuse check-ref-format here, but
+       # that should be ok.
+       if {[catch {git check-ref-format "remotes/$name"}]} {
+               tk_messageBox \
+                       -icon error \
+                       -type ok \
+                       -title [wm title $w] \
+                       -parent $w \
+                       -message [mc "'%s' is not an acceptable remote name." $name]
+               focus $w_name
+               return
+       }
+
+       if {[catch {add_single_remote $name $location}]} {
+               tk_messageBox \
+                       -icon error \
+                       -type ok \
+                       -title [wm title $w] \
+                       -parent $w \
+                       -message [mc "Failed to add remote '%s' of location '%s'." $name $location]
+               focus $w_name
+               return
+       }
+
+       switch -- $opt_action {
+       fetch {
+               set c [console::new \
+                       [mc "fetch %s" $name] \
+                       [mc "Fetching the %s" $name]]
+               console::exec $c [list git fetch $name]
+       }
+       push {
+               set cmds [list]
+
+               # Parse the location
+               if { [regexp {(?:git\+)?ssh://([^/]+)(/.+)} $location xx host path]
+                    || [regexp {([^:][^:]+):(.+)} $location xx host path]} {
+                       set ssh ssh
+                       if {[info exists env(GIT_SSH)]} {
+                               set ssh $env(GIT_SSH)
+                       }
+                       lappend cmds [list exec $ssh $host mkdir -p $location && git --git-dir=$path init --bare]
+               } elseif { ! [regexp {://} $location xx] } {
+                       lappend cmds [list exec mkdir -p $location]
+                       lappend cmds [list exec git --git-dir=$location init --bare]
+               } else {
+                       tk_messageBox \
+                               -icon error \
+                               -type ok \
+                               -title [wm title $w] \
+                               -parent $w \
+                               -message [mc "Do not know how to initialize repository at location '%s'." $location]
+                       destroy $w
+                       return
+               }
+
+               set c [console::new \
+                       [mc "push %s" $name] \
+                       [mc "Setting up the %s (at %s)" $name $location]]
+
+               lappend cmds [list exec git push -v --all $name]
+               console::chain $c $cmds
+       }
+       none {
+       }
+       }
+
+       destroy $w
+}
+
+method _validate_name {d S} {
+       if {$d == 1} {
+               if {[regexp {[~^:?*\[\0- ]} $S]} {
+                       return 0
+               }
+       }
+       return 1
+}
+
+method _visible {} {
+       grab $w
+       $w_name icursor end
+       focus $w_name
+}
+
+}
index c7b81486984d46a9dca59867c406a8e247d76313..89eb0f70f289e48e2b875e2cd49eb026a266ca0e 100644 (file)
@@ -26,12 +26,12 @@ constructor dialog {} {
        global all_remotes M1B
 
        make_toplevel top w
-       wm title $top [append "[appname] ([reponame]): " [mc "Delete Remote Branch"]]
+       wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch Remotely"]]
        if {$top ne {.}} {
                wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
        }
 
-       label $w.header -text [mc "Delete Remote Branch"] -font font_uibold
+       label $w.header -text [mc "Delete Branch Remotely"] -font font_uibold
        pack $w.header -side top -fill x
 
        frame $w.buttons
@@ -63,7 +63,7 @@ constructor dialog {} {
                set urltype url
        }
        radiobutton $w.dest.url_r \
-               -text [mc "Arbitrary URL:"] \
+               -text [mc "Arbitrary Location:"] \
                -value url \
                -variable @urltype
        entry $w.dest.url_t \
diff --git a/git-gui/lib/search.tcl b/git-gui/lib/search.tcl
new file mode 100644 (file)
index 0000000..d292f20
--- /dev/null
@@ -0,0 +1,190 @@
+# incremental search panel
+# based on code from gitk, Copyright (C) Paul Mackerras
+
+class searchbar {
+
+field w
+field ctext
+
+field searchstring   {}
+field casesensitive  1
+field searchdirn     -forwards
+
+field smarktop
+field smarkbot
+
+constructor new {i_w i_text args} {
+       set w      $i_w
+       set ctext  $i_text
+
+       frame  $w
+       label  $w.l       -text [mc Find:]
+       button $w.bn      -text [mc Next] -command [cb find_next]
+       button $w.bp      -text [mc Prev] -command [cb find_prev]
+       checkbutton $w.cs -text [mc Case-Sensitive] \
+               -variable ${__this}::casesensitive -command [cb _incrsearch]
+       entry  $w.ent -textvariable ${__this}::searchstring -background lightgreen
+       pack   $w.l   -side left
+       pack   $w.cs  -side right
+       pack   $w.bp  -side right
+       pack   $w.bn  -side right
+       pack   $w.ent -side left -expand 1 -fill x
+
+       eval grid conf $w -sticky we $args
+       grid remove $w
+
+       trace add variable searchstring write [cb _incrsearch_cb]
+       
+       bind $w <Destroy> [cb delete_this]
+       return $this
+}
+
+method show {} {
+       if {![winfo ismapped $w]} {
+               grid $w
+       }
+       focus -force $w.ent
+}
+
+method hide {} {
+       if {[winfo ismapped $w]} {
+               focus $ctext
+               grid remove $w
+       }
+}
+
+method _get_new_anchor {} {
+       # use start of selection if it is visible,
+       # or the bounds of the visible area
+       set top    [$ctext index @0,0]
+       set bottom [$ctext index @0,[winfo height $ctext]]
+       set sel    [$ctext tag ranges sel]
+       if {$sel ne {}} {
+               set spos [lindex $sel 0]
+               if {[lindex $spos 0] >= [lindex $top 0] &&
+                   [lindex $spos 0] <= [lindex $bottom 0]} {
+                       return $spos
+               }
+       }
+       if {$searchdirn eq "-forwards"} {
+               return $top
+       } else {
+               return $bottom
+       }
+}
+
+method _get_wrap_anchor {dir} {
+       if {$dir eq "-forwards"} {
+               return 1.0
+       } else {
+               return end
+       }
+}
+
+method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} {
+       set cmd [list $ctext search]
+       if {$mlenvar ne {}} {
+               upvar $mlenvar mlen
+               lappend cmd -count mlen
+       }
+       if {!$casesensitive} {
+               lappend cmd -nocase
+       }
+       if {$dir eq {}} {
+               set dir $searchdirn
+       }
+       lappend cmd $dir -- $searchstring
+       if {$endbound ne {}} {
+               set here [eval $cmd [list $start] [list $endbound]]
+       } else {
+               set here [eval $cmd [list $start]]
+               if {$here eq {}} {
+                       set here [eval $cmd [_get_wrap_anchor $this $dir]]
+               }
+       }
+       return $here
+}
+
+method _incrsearch_cb {name ix op} {
+       after idle [cb _incrsearch]
+}
+
+method _incrsearch {} {
+       $ctext tag remove found 1.0 end
+       if {[catch {$ctext index anchor}]} {
+               $ctext mark set anchor [_get_new_anchor $this]
+       }
+       if {$searchstring ne {}} {
+               set here [_do_search $this anchor mlen]
+               if {$here ne {}} {
+                       $ctext see $here
+                       $ctext tag remove sel 1.0 end
+                       $ctext tag add sel $here "$here + $mlen c"
+                       $w.ent configure -background lightgreen
+                       _set_marks $this 1
+               } else {
+                       $w.ent configure -background lightpink
+               }
+       }
+}
+
+method find_prev {} {
+       find_next $this -backwards
+}
+
+method find_next {{dir -forwards}} {
+       focus $w.ent
+       $w.ent icursor end
+       set searchdirn $dir
+       $ctext mark unset anchor
+       if {$searchstring ne {}} {
+               set start [_get_new_anchor $this]
+               if {$dir eq "-forwards"} {
+                       set start "$start + 1c"
+               }
+               set match [_do_search $this $start mlen]
+               $ctext tag remove sel 1.0 end
+               if {$match ne {}} {
+                       $ctext see $match
+                       $ctext tag add sel $match "$match + $mlen c"
+               }
+       }
+}
+
+method _mark_range {first last} {
+       set mend $first.0
+       while {1} {
+               set match [_do_search $this $mend mlen -forwards $last.end]
+               if {$match eq {}} break
+               set mend "$match + $mlen c"
+               $ctext tag add found $match $mend
+       }
+}
+
+method _set_marks {doall} {
+       set topline [lindex [split [$ctext index @0,0] .] 0]
+       set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
+       if {$doall || $botline < $smarktop || $topline > $smarkbot} {
+               # no overlap with previous
+               _mark_range $this $topline $botline
+               set smarktop $topline
+               set smarkbot $botline
+       } else {
+               if {$topline < $smarktop} {
+                       _mark_range $this $topline [expr {$smarktop-1}]
+                       set smarktop $topline
+               }
+               if {$botline > $smarkbot} {
+                       _mark_range $this [expr {$smarkbot+1}] $botline
+                       set smarkbot $botline
+               }
+       }
+}
+
+method scrolled {} {
+       if {$searchstring ne {}} {
+               after idle [cb _set_marks 0]
+       }
+}
+
+}
\ No newline at end of file
index 78f344f08f34842c134b6b0e22b6eb8c49b1dbbd..e6120303e940ee6c849bf706415c1b450e68b57f 100644 (file)
@@ -80,7 +80,7 @@ method _connect {pipe_fd} {
                error_popup [strcat [mc "Unrecognized spell checker"] ":\n\n$s_version"]
                return
        }
-       set s_version [string range $s_version 5 end]
+       set s_version [string range [string trim $s_version] 5 end]
        regexp \
                {International Ispell Version .* \(but really (Aspell .*?)\)$} \
                $s_version _junk s_version
@@ -314,6 +314,7 @@ method _run {} {
 method _read {} {
        while {[gets $s_fd line] >= 0} {
                set lineno [lindex $s_pending 0 0]
+               set line [string trim $line]
 
                if {$s_clear} {
                        $w_text tag remove misspelled "$lineno.0" "$lineno.end"
diff --git a/git-gui/lib/sshkey.tcl b/git-gui/lib/sshkey.tcl
new file mode 100644 (file)
index 0000000..82a1a80
--- /dev/null
@@ -0,0 +1,126 @@
+# git-gui about git-gui dialog
+# Copyright (C) 2006, 2007 Shawn Pearce
+
+proc find_ssh_key {} {
+       foreach name {~/.ssh/id_dsa.pub ~/.ssh/id_rsa.pub ~/.ssh/identity.pub} {
+               if {[file exists $name]} {
+                       set fh    [open $name r]
+                       set cont  [read $fh]
+                       close $fh
+                       return [list $name $cont]
+               }
+       }
+
+       return {}
+}
+
+proc do_ssh_key {} {
+       global sshkey_title have_tk85 sshkey_fd
+
+       set w .sshkey_dialog
+       if {[winfo exists $w]} {
+               raise $w
+               return
+       }
+
+       toplevel $w
+       wm transient $w .
+
+       set finfo [find_ssh_key]
+       if {$finfo eq {}} {
+               set sshkey_title [mc "No keys found."]
+               set gen_state   normal
+       } else {
+               set sshkey_title [mc "Found a public key in: %s" [lindex $finfo 0]]
+               set gen_state   disabled
+       }
+
+       frame $w.header -relief flat
+       label $w.header.lbl -textvariable sshkey_title -anchor w
+       button $w.header.gen -text [mc "Generate Key"] \
+               -command [list make_ssh_key $w] -state $gen_state
+       pack $w.header.lbl -side left -expand 1 -fill x
+       pack $w.header.gen -side right
+       pack $w.header -fill x -pady 5 -padx 5
+
+       text $w.contents -width 60 -height 10 -wrap char -relief sunken
+       pack $w.contents -fill both -expand 1
+       if {$have_tk85} {
+               $w.contents configure -inactiveselectbackground darkblue
+       }
+
+       frame $w.buttons
+       button $w.buttons.close -text [mc Close] \
+               -default active -command [list destroy $w]
+       pack $w.buttons.close -side right
+       button $w.buttons.copy -text [mc "Copy To Clipboard"] \
+               -command [list tk_textCopy $w.contents]
+       pack $w.buttons.copy -side left
+       pack $w.buttons -side bottom -fill x -pady 5 -padx 5
+
+       if {$finfo ne {}} {
+               $w.contents insert end [lindex $finfo 1] sel
+       }
+       $w.contents configure -state disabled
+
+       bind $w <Visibility> "grab $w; focus $w.buttons.close"
+       bind $w <Key-Escape> "destroy $w"
+       bind $w <Key-Return> "destroy $w"
+       bind $w <Destroy> kill_sshkey
+       wm title $w [mc "Your OpenSSH Public Key"]
+       tk::PlaceWindow $w widget .
+       tkwait window $w
+}
+
+proc make_ssh_key {w} {
+       global sshkey_title sshkey_output sshkey_fd
+
+       set sshkey_title [mc "Generating..."]
+       $w.header.gen configure -state disabled
+
+       set cmdline [list sh -c {echo | ssh-keygen -q -t rsa -f ~/.ssh/id_rsa 2>&1}]
+
+       if {[catch { set sshkey_fd [_open_stdout_stderr $cmdline] } err]} {
+               error_popup [mc "Could not start ssh-keygen:\n\n%s" $err]
+               return
+       }
+
+       set sshkey_output {}
+       fconfigure $sshkey_fd -blocking 0
+       fileevent $sshkey_fd readable [list read_sshkey_output $sshkey_fd $w]
+}
+
+proc kill_sshkey {} {
+       global sshkey_fd
+       if {![info exists sshkey_fd]} return
+       catch { kill_file_process $sshkey_fd }
+       catch { close $sshkey_fd }
+}
+
+proc read_sshkey_output {fd w} {
+       global sshkey_fd sshkey_output sshkey_title
+
+       set sshkey_output "$sshkey_output[read $fd]"
+       if {![eof $fd]} return
+
+       fconfigure $fd -blocking 1
+       unset sshkey_fd
+
+       $w.contents configure -state normal
+       if {[catch {close $fd} err]} {
+               set sshkey_title [mc "Generation failed."]
+               $w.contents insert end $err
+               $w.contents insert end "\n"
+               $w.contents insert end $sshkey_output
+       } else {
+               set finfo [find_ssh_key]
+               if {$finfo eq {}} {
+                       set sshkey_title [mc "Generation succeded, but no keys found."]
+                       $w.contents insert end $sshkey_output
+               } else {
+                       set sshkey_title [mc "Your key is in: %s" [lindex $finfo 0]]
+                       $w.contents insert end [lindex $finfo 1] sel
+               }
+       }
+       $w.contents configure -state disable
+}
index 8e6a9d0a6010cf5efee069a2d488124bcbdb54cd..e419d7810ae404e41a0a85080d16f4576d737d8a 100644 (file)
@@ -135,7 +135,7 @@ proc do_push_anywhere {} {
                set push_urltype url
        }
        radiobutton $w.dest.url_r \
-               -text [mc "Arbitrary URL:"] \
+               -text [mc "Arbitrary Location:"] \
                -value url \
                -variable push_urltype
        entry $w.dest.url_t \
index 793cca1e798640400453e74e2a4b64863f76a540..5c04812b1ab67c29fb68a4c3d7d4e97bb6483984 100644 (file)
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-09-13 10:20+0200\n"
-"PO-Revision-Date: 2008-09-13 10:24+0200\n"
+"POT-Creation-Date: 2008-10-25 13:32+0200\n"
+"PO-Revision-Date: 2008-10-25 22:47+0200\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
@@ -86,7 +86,17 @@ msgstr "Dateistatus aktualisieren..."
 msgid "Scanning for modified files ..."
 msgstr "Nach geänderten Dateien suchen..."
 
-#: git-gui.sh:1324 lib/browser.tcl:246
+#: git-gui.sh:1325
+#, fuzzy
+msgid "Calling prepare-commit-msg hook..."
+msgstr "Aufrufen der Vor-Eintragen-Kontrolle..."
+
+#: git-gui.sh:1342
+#, fuzzy
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr "Eintragen abgelehnt durch Vor-Eintragen-Kontrolle (»pre-commit hook«)."
+
+#: git-gui.sh:1502 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Bereit."
 
@@ -170,7 +180,11 @@ msgstr "Zusammenführen"
 msgid "Remote"
 msgstr "Andere Archive"
 
-#: git-gui.sh:1879
+#: git-gui.sh:2242
+msgid "Explore Working Copy"
+msgstr "Arbeitskopie im Dateimanager"
+
+#: git-gui.sh:2247
 msgid "Browse Current Branch's Files"
 msgstr "Aktuellen Zweig durchblättern"
 
@@ -267,7 +281,15 @@ msgstr "Löschen..."
 msgid "Reset..."
 msgstr "Zurücksetzen..."
 
-#: git-gui.sh:2002 git-gui.sh:2389
+#: git-gui.sh:2372
+msgid "Done"
+msgstr "Fertig"
+
+#: git-gui.sh:2374
+msgid "Commit@@verb"
+msgstr "Eintragen"
+
+#: git-gui.sh:2383 git-gui.sh:2786
 msgid "New Commit"
 msgstr "Neue Version"
 
@@ -307,11 +329,7 @@ msgstr "Mehr Zeilen anzeigen"
 msgid "Sign Off"
 msgstr "Abzeichnen"
 
-#: git-gui.sh:2053 git-gui.sh:2372
-msgid "Commit@@verb"
-msgstr "Eintragen"
-
-#: git-gui.sh:2064
+#: git-gui.sh:2458
 msgid "Local Merge..."
 msgstr "Lokales Zusammenführen..."
 
@@ -319,11 +337,19 @@ msgstr "Lokales Zusammenführen..."
 msgid "Abort Merge..."
 msgstr "Zusammenführen abbrechen..."
 
-#: git-gui.sh:2081
+#: git-gui.sh:2475
+msgid "Add..."
+msgstr "Hinzufügen..."
+
+#: git-gui.sh:2479
 msgid "Push..."
 msgstr "Versenden..."
 
-#: git-gui.sh:2197 git-gui.sh:2219 lib/about.tcl:14
+#: git-gui.sh:2483
+msgid "Delete Branch..."
+msgstr "Zweig löschen..."
+
+#: git-gui.sh:2493 git-gui.sh:2515 lib/about.tcl:14
 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
@@ -416,7 +442,11 @@ msgstr "Schriftgröße verkleinern"
 msgid "Increase Font Size"
 msgstr "Schriftgröße vergrößern"
 
-#: git-gui.sh:2870
+#: git-gui.sh:3033 lib/blame.tcl:281
+msgid "Encoding"
+msgstr "Zeichenkodierung"
+
+#: git-gui.sh:3044
 msgid "Apply/Reverse Hunk"
 msgstr "Kontext anwenden/umkehren"
 
@@ -440,11 +470,7 @@ msgstr "Lokale Version benutzen"
 msgid "Revert To Base"
 msgstr "Ursprüngliche Version benutzen"
 
-#: git-gui.sh:2906
-msgid "Stage Working Copy"
-msgstr "Arbeitskopie bereitstellen"
-
-#: git-gui.sh:2925
+#: git-gui.sh:3091
 msgid "Unstage Hunk From Commit"
 msgstr "Kontext aus Bereitstellung herausnehmen"
 
@@ -583,7 +609,12 @@ msgstr "Eintragender:"
 msgid "Original File:"
 msgstr "Ursprüngliche Datei:"
 
-#: lib/blame.tcl:990
+#: lib/blame.tcl:1013
+#, fuzzy
+msgid "Cannot find HEAD commit:"
+msgstr "Elternversion kann nicht gefunden werden:"
+
+#: lib/blame.tcl:1068
 msgid "Cannot find parent commit:"
 msgstr "Elternversion kann nicht gefunden werden:"
 
@@ -1041,11 +1072,15 @@ msgstr "Datei Â»%s« existiert bereits."
 msgid "Clone"
 msgstr "Klonen"
 
-#: lib/choose_repository.tcl:468
-msgid "URL:"
-msgstr "URL:"
+#: lib/choose_repository.tcl:467
+msgid "Source Location:"
+msgstr ""
+
+#: lib/choose_repository.tcl:478
+msgid "Target Directory:"
+msgstr "Zielverzeichnis:"
 
-#: lib/choose_repository.tcl:489
+#: lib/choose_repository.tcl:490
 msgid "Clone Type:"
 msgstr "Art des Klonens:"
 
@@ -1525,7 +1560,27 @@ msgstr ""
 msgid "Loading diff of %s..."
 msgstr "Vergleich von Â»%s« laden..."
 
-#: lib/diff.tcl:114 lib/diff.tcl:184
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr ""
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr ""
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Datei Â»%s« kann nicht angezeigt werden"
@@ -1542,7 +1597,22 @@ msgstr "Git-Projektarchiv (Unterprojekt)"
 msgid "* Binary file (not showing content)."
 msgstr "* Binärdatei (Inhalt wird nicht angezeigt)"
 
-#: lib/diff.tcl:313
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+
+#: lib/diff.tcl:437
 msgid "Failed to unstage selected hunk."
 msgstr ""
 "Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung."
@@ -1559,6 +1629,19 @@ msgstr "Fehler beim Herausnehmen der gewählten Zeile aus der Bereitstellung."
 msgid "Failed to stage selected line."
 msgstr "Fehler beim Bereitstellen der gewählten Zeile."
 
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Voreinstellung"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Systemweit (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Andere"
+
 #: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "Fehler"
@@ -1811,7 +1894,12 @@ msgstr ""
 "Diese Operation kann nur rückgängig gemacht werden, wenn die\n"
 "Zusammenführung erneut gestartet wird."
 
-#: lib/mergetool.tcl:32
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr "Datei Â»%s« hat nicht aufgelöste Konflikte. Trotzdem bereitstellen?"
+
+#: lib/mergetool.tcl:60
 #, tcl-format
 msgid "Adding resolution for %s"
 msgstr "Auflösung hinzugefügt für %s"
@@ -1868,12 +1956,17 @@ msgstr "Zusammenführungswerkzeug starten..."
 msgid "Merge tool failed."
 msgstr "Zusammenführungswerkzeug fehlgeschlagen."
 
-#: lib/mergetool.tcl:353
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr "Ungültige globale Zeichenkodierung Â»%s«"
+
+#: lib/option.tcl:19
 #, tcl-format
-msgid "File %s unchanged, still accept as resolved?"
-msgstr "Datei Â»%s« unverändert. Trotzdem Konflikt als gelöst akzeptieren?"
+msgid "Invalid repo encoding '%s'"
+msgstr "Ungültige Archiv-Zeichenkodierung Â»%s«"
 
-#: lib/option.tcl:95
+#: lib/option.tcl:117
 msgid "Restore Defaults"
 msgstr "Voreinstellungen wiederherstellen"
 
@@ -1950,7 +2043,15 @@ msgstr "Textbreite der Versionsbeschreibung"
 msgid "New Branch Name Template"
 msgstr "Namensvorschlag für neue Zweige"
 
-#: lib/option.tcl:192
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr "Vorgestellte Zeichenkodierung"
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr "Ändern"
+
+#: lib/option.tcl:230
 msgid "Spelling Dictionary:"
 msgstr "Wörterbuch Rechtschreibprüfung:"
 
@@ -1975,9 +2076,85 @@ msgstr "Einstellungen"
 msgid "Failed to completely save options:"
 msgstr "Optionen konnten nicht gespeichert werden:"
 
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr "Anderes Archiv hinzufügen"
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr "Neues anderes Archiv hinzufügen"
+
+#: lib/remote_add.tcl:28
+msgid "Add"
+msgstr "Hinzufügen"
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr "Einzelheiten des anderen Archivs"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Adresse:"
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr "Weitere Aktion jetzt"
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr "Gleich anfordern"
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr "Anderes Archiv initialisieren und dahin versenden"
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr "Nichts tun"
+
+#: lib/remote_add.tcl:101
+#, fuzzy
+msgid "Please supply a remote name."
+msgstr "Bitte geben Sie einen Zweignamen an."
+
+#: lib/remote_add.tcl:114
+#, fuzzy, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "»%s« ist kein zulässiger Zweigname."
+
+#: lib/remote_add.tcl:125
+#, fuzzy, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Fehler beim Umbenennen von Â»%s«."
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "»%s« anfordern"
+
+#: lib/remote_add.tcl:134
+#, fuzzy, tcl-format
+msgid "Fetching the %s"
+msgstr "Änderungen Â»%s« von Â»%s« anfordern"
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Initialisieren eines anderen Archivs an Adresse Â»%s« ist nicht möglich."
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:71
+#, tcl-format
+msgid "push %s"
+msgstr "»%s« versenden..."
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Einrichten von Â»%s« an Â»%s«"
+
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Remote Branch"
-msgstr "Zweig in anderem Projektarchiv löschen"
+msgid "Delete Branch Remotely"
+msgstr "Zweig in anderem Archiv löschen"
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
@@ -1988,8 +2165,8 @@ msgid "Remote:"
 msgstr "Anderes Archiv:"
 
 #: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
-msgid "Arbitrary URL:"
-msgstr "Archiv-URL:"
+msgid "Arbitrary Location:"
+msgstr "Adresse:"
 
 #: lib/remote_branch_delete.tcl:84
 msgid "Branches"
@@ -2061,7 +2238,11 @@ msgstr "Kein Projektarchiv ausgewählt."
 msgid "Scanning %s..."
 msgstr "»%s« laden..."
 
-#: lib/remote.tcl:165
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "Anderes Archiv entfernen"
+
+#: lib/remote.tcl:168
 msgid "Prune from"
 msgstr "Aufräumen von"
 
@@ -2073,6 +2254,22 @@ msgstr "Anfordern von"
 msgid "Push to"
 msgstr "Versenden nach"
 
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr "Suchen:"
+
+#: lib/search.tcl:22
+msgid "Next"
+msgstr "Nächster"
+
+#: lib/search.tcl:23
+msgid "Prev"
+msgstr "Voriger"
+
+#: lib/search.tcl:24
+msgid "Case-Sensitive"
+msgstr ""
+
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
 msgstr "Fehler beim Schreiben der Verknüpfung:"
@@ -2123,11 +2320,6 @@ msgstr "Rechtschreibprüfung fehlgeschlagen"
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr "%s ... %*i von %*i %s (%3i%%)"
 
-#: lib/transport.tcl:6
-#, tcl-format
-msgid "fetch %s"
-msgstr "»%s« anfordern"
-
 #: lib/transport.tcl:7
 #, tcl-format
 msgid "Fetching new changes from %s"
@@ -2143,11 +2335,6 @@ msgstr "Aufräumen von Â»%s«"
 msgid "Pruning tracking branches deleted from %s"
 msgstr "Ãœbernahmezweige aufräumen und entfernen, die in Â»%s« gelöscht wurden"
 
-#: lib/transport.tcl:25 lib/transport.tcl:71
-#, tcl-format
-msgid "push %s"
-msgstr "»%s« versenden..."
-
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
index 75c36100a2f858bcf2663d2b4560654787963175..664fe34419e9aeef1af8fac820cc423a641f5525 100755 (executable)
@@ -124,7 +124,7 @@ orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
 git fetch --update-head-ok "$@" || exit 1
 
 curr_head=$(git rev-parse --verify HEAD 2>/dev/null)
-if test "$curr_head" != "$orig_head"
+if test -n "$orig_head" && test "$curr_head" != "$orig_head"
 then
        # The fetch involved updating the current branch.
 
@@ -172,7 +172,7 @@ esac
 
 if test -z "$orig_head"
 then
-       git update-ref -m "initial pull" HEAD $merge_head "" &&
+       git update-ref -m "initial pull" HEAD $merge_head "$curr_head" &&
        git read-tree --reset -u HEAD || exit 1
        exit
 fi
index bdbfac66256ffc29e26f5c83531c77a18a263808..94ca5c89ad086bac754fb6b6b907fd4e968e8024 100755 (executable)
@@ -374,10 +374,9 @@ sub read_config {
 
                push @files, grep { -f $_ } map { +$f . "/" . $_ }
                                sort readdir(DH);
-
+               closedir(DH);
        } elsif (-f $f or -p $f) {
                push @files, $f;
-
        } else {
                print STDERR "Skipping $f - not found.\n";
        }
index 65178ae8e32bb20c65c42eb9668fb0ffec749007..b63e5c30874bfa382c7d6d0ca1942d116930b62a 100755 (executable)
@@ -424,7 +424,7 @@ cmd_summary() {
        cd_to_toplevel
        # Get modified modules cared by user
        modules=$(git diff-index $cached --raw $head -- "$@" |
-               grep -e '^:160000' -e '^:[0-7]* 160000' |
+               egrep '^:([0-7]* )?160000' |
                while read mod_src mod_dst sha1_src sha1_dst status name
                do
                        # Always show modules deleted or type-changed (blob<->module)
@@ -438,7 +438,7 @@ cmd_summary() {
        test -z "$modules" && return
 
        git diff-index $cached --raw $head -- $modules |
-       grep -e '^:160000' -e '^:[0-7]* 160000' |
+       egrep '^:([0-7]* )?160000' |
        cut -c2- |
        while read mod_src mod_dst sha1_src sha1_dst status name
        do
index ef6d773df1143b32f0bc421b59421f7e0eab60bd..5702b100f17d32c4d370d2b29374390ad0f03520 100755 (executable)
@@ -1191,7 +1191,7 @@ sub read_repo_config {
                my $v = $opts->{$o};
                my ($key) = ($o =~ /^([a-zA-Z\-]+)/);
                $key =~ s/-//g;
-               my $arg = 'git-config';
+               my $arg = 'git config';
                $arg .= ' --int' if ($o =~ /[:=]i$/);
                $arg .= ' --bool' if ($o !~ /[:=][sfi]$/);
                if (ref $v eq 'ARRAY') {
@@ -2267,7 +2267,7 @@ sub do_git_commit {
        }
        die "Tree is not a valid sha1: $tree\n" if $tree !~ /^$::sha1$/o;
 
-       my @exec = ('git-commit-tree', $tree);
+       my @exec = ('git', 'commit-tree', $tree);
        foreach ($self->get_commit_parents($log_entry)) {
                push @exec, '-p', $_;
        }
index 42f4d78e54edbb73d14952f5fb3992f2cebb40ae..5cecef434a7740a3f853462978c3e071b4da7e74 100644 (file)
@@ -1780,7 +1780,7 @@ static void one_remote_ref(char *refname)
        struct ref *ref;
        struct object *obj;
 
-       ref = alloc_ref_from_str(refname);
+       ref = alloc_ref(refname);
 
        if (http_fetch_ref(remote->url, ref) != 0) {
                fprintf(stderr,
@@ -1887,7 +1887,7 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls)
        char *ref_info;
        struct ref *ref;
 
-       ref = alloc_ref_from_str(ls->dentry_name);
+       ref = alloc_ref(ls->dentry_name);
 
        if (http_fetch_ref(remote->url, ref) != 0) {
                fprintf(stderr,
index aec11cb94066bc2d0fc75cad2b9f675c9d9bdb53..6f89bb9ac7f281434a9759956d8da23e6bc071db 100644 (file)
@@ -880,10 +880,26 @@ int main(int argc, char **argv)
        char *index_name_buf = NULL, *keep_name_buf = NULL;
        struct pack_idx_entry **idx_objects;
        unsigned char pack_sha1[20];
-       int nongit = 0;
 
-       setup_git_directory_gently(&nongit);
-       git_config(git_index_pack_config, NULL);
+       /*
+        * We wish to read the repository's config file if any, and
+        * for that it is necessary to call setup_git_directory_gently().
+        * However if the cwd was inside .git/objects/pack/ then we need
+        * to go back there or all the pack name arguments will be wrong.
+        * And in that case we cannot rely on any prefix returned by
+        * setup_git_directory_gently() either.
+        */
+       {
+               char cwd[PATH_MAX+1];
+               int nongit;
+
+               if (!getcwd(cwd, sizeof(cwd)-1))
+                       die("Unable to get current working directory");
+               setup_git_directory_gently(&nongit);
+               git_config(git_index_pack_config, NULL);
+               if (chdir(cwd))
+                       die("Cannot come back to cwd");
+       }
 
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
index 4023797b00fe21ecbabe3407ef8a12fca0690607..6d756086939b631ab13bf4fcdb8deed2787eed6b 100644 (file)
@@ -121,15 +121,17 @@ static char *resolve_symlink(char *p, size_t s)
 }
 
 
-static int lock_file(struct lock_file *lk, const char *path)
+static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
-       if (strlen(path) >= sizeof(lk->filename)) return -1;
+       if (strlen(path) >= sizeof(lk->filename))
+               return -1;
        strcpy(lk->filename, path);
        /*
         * subtract 5 from size to make sure there's room for adding
         * ".lock" for the lock file name
         */
-       resolve_symlink(lk->filename, sizeof(lk->filename)-5);
+       if (!(flags & LOCK_NODEREF))
+               resolve_symlink(lk->filename, sizeof(lk->filename)-5);
        strcat(lk->filename, ".lock");
        lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
        if (0 <= lk->fd) {
@@ -155,21 +157,21 @@ static int lock_file(struct lock_file *lk, const char *path)
        return lk->fd;
 }
 
-int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on_error)
+int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
 {
-       int fd = lock_file(lk, path);
-       if (fd < 0 && die_on_error)
+       int fd = lock_file(lk, path, flags);
+       if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
                die("unable to create '%s.lock': %s", path, strerror(errno));
        return fd;
 }
 
-int hold_lock_file_for_append(struct lock_file *lk, const char *path, int die_on_error)
+int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
 {
        int fd, orig_fd;
 
-       fd = lock_file(lk, path);
+       fd = lock_file(lk, path, flags);
        if (fd < 0) {
-               if (die_on_error)
+               if (flags & LOCK_DIE_ON_ERROR)
                        die("unable to create '%s.lock': %s", path, strerror(errno));
                return fd;
        }
@@ -177,13 +179,13 @@ int hold_lock_file_for_append(struct lock_file *lk, const char *path, int die_on
        orig_fd = open(path, O_RDONLY);
        if (orig_fd < 0) {
                if (errno != ENOENT) {
-                       if (die_on_error)
+                       if (flags & LOCK_DIE_ON_ERROR)
                                die("cannot open '%s' for copying", path);
                        close(fd);
                        return error("cannot open '%s' for copying", path);
                }
        } else if (copy_fd(orig_fd, fd)) {
-               if (die_on_error)
+               if (flags & LOCK_DIE_ON_ERROR)
                        exit(128);
                close(fd);
                return -1;
@@ -215,7 +217,10 @@ int commit_lock_file(struct lock_file *lk)
 
 int hold_locked_index(struct lock_file *lk, int die_on_error)
 {
-       return hold_lock_file_for_update(lk, get_index_file(), die_on_error);
+       return hold_lock_file_for_update(lk, get_index_file(),
+                                        die_on_error
+                                        ? LOCK_DIE_ON_ERROR
+                                        : 0);
 }
 
 void set_alternate_index_output(const char *name)
index 848d311c2b2c651dbb14893c260584f00c639357..2c76fb181f64e10c65517224b0c09cb648db81ac 100644 (file)
@@ -89,7 +89,8 @@ int pack_refs(unsigned int flags)
        memset(&cbdata, 0, sizeof(cbdata));
        cbdata.flags = flags;
 
-       fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1);
+       fd = hold_lock_file_for_update(&packed, git_path("packed-refs"),
+                                      LOCK_DIE_ON_ERROR);
        cbdata.refs_file = fdopen(fd, "w");
        if (!cbdata.refs_file)
                die("unable to create ref-pack file structure (%s)",
diff --git a/path.c b/path.c
index 76e8872622e435b050f77198ef6eef6e6ff6869e..c1cb54b7b8b478c717861a0205e537ded3fbbfc9 100644 (file)
--- a/path.c
+++ b/path.c
@@ -348,7 +348,7 @@ int normalize_absolute_path(char *buf, const char *path)
                        goto next;
                }
 
-               memcpy(dst, comp_start, comp_len);
+               memmove(dst, comp_start, comp_len);
                dst += comp_len;
        next:
                comp_start = comp_end;
index 6aab712e6ac6513b0b46958d93d536ef975276b4..ba94453781c98a97a95c65999873fc28b013340d 100644 (file)
@@ -1203,8 +1203,7 @@ =head1 COPYRIGHT
 # the method was called upon an instance and (undef, @args) if
 # it was called directly.
 sub _maybe_self {
-       # This breaks inheritance. Oh well.
-       ref $_[0] eq 'Git' ? @_ : (undef, @_);
+       UNIVERSAL::isa($_[0], 'Git') ? @_ : (undef, @_);
 }
 
 # Check if the command id is something reasonable.
index c229fd4d0da68b204e81e1e8127f8529abe06336..2c450866ce92eee2ef484dd798f332cb0047a61d 100644 (file)
@@ -160,7 +160,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
        return 0;
 }
 
-static int is_empty_blob_sha1(const unsigned char *sha1)
+int is_empty_blob_sha1(const unsigned char *sha1)
 {
        static const unsigned char empty_blob_sha1[20] = {
                0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b,
@@ -1489,25 +1489,30 @@ int write_index(const struct index_state *istate, int newfd)
 int read_index_unmerged(struct index_state *istate)
 {
        int i;
-       struct cache_entry **dst;
-       struct cache_entry *last = NULL;
+       int unmerged = 0;
 
        read_index(istate);
-       dst = istate->cache;
        for (i = 0; i < istate->cache_nr; i++) {
                struct cache_entry *ce = istate->cache[i];
-               if (ce_stage(ce)) {
-                       remove_name_hash(ce);
-                       if (last && !strcmp(ce->name, last->name))
-                               continue;
-                       cache_tree_invalidate_path(istate->cache_tree, ce->name);
-                       last = ce;
+               struct cache_entry *new_ce;
+               int size, len;
+
+               if (!ce_stage(ce))
                        continue;
-               }
-               *dst++ = ce;
+               unmerged = 1;
+               len = strlen(ce->name);
+               size = cache_entry_size(len);
+               new_ce = xcalloc(1, size);
+               hashcpy(new_ce->sha1, ce->sha1);
+               memcpy(new_ce->name, ce->name, len);
+               new_ce->ce_flags = create_ce_flags(len, 0);
+               new_ce->ce_mode = ce->ce_mode;
+               if (add_index_entry(istate, new_ce, 0))
+                       return error("%s: cannot drop to stage #0",
+                                    ce->name);
+               i = index_name_pos(istate, new_ce->name, len);
        }
-       istate->cache_nr = dst - istate->cache;
-       return !!last;
+       return unmerged;
 }
 
 struct update_callback_data
@@ -1565,3 +1570,30 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
        return !!data.add_errors;
 }
 
+/*
+ * Returns 1 if the path is an "other" path with respect to
+ * the index; that is, the path is not mentioned in the index at all,
+ * either as a file, a directory with some files in the index,
+ * or as an unmerged entry.
+ *
+ * We helpfully remove a trailing "/" from directories so that
+ * the output of read_directory can be used as-is.
+ */
+int index_name_is_other(const struct index_state *istate, const char *name,
+               int namelen)
+{
+       int pos;
+       if (namelen && name[namelen - 1] == '/')
+               namelen--;
+       pos = index_name_pos(istate, name, namelen);
+       if (0 <= pos)
+               return 0;       /* exact match */
+       pos = -pos - 1;
+       if (pos < istate->cache_nr) {
+               struct cache_entry *ce = istate->cache[pos];
+               if (ce_namelen(ce) == namelen &&
+                   !memcmp(ce->name, name, namelen))
+                       return 0; /* Yup, this one exists unmerged */
+       }
+       return 1;
+}
diff --git a/refs.c b/refs.c
index b6807505e243fd26cae50460bc003254406ae7e5..0a126fa371ae24f5cb251ff52cf7d38322d78ba7 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -796,7 +796,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
        struct ref_lock *lock;
        struct stat st;
        int last_errno = 0;
-       int type;
+       int type, lflags;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
 
        lock = xcalloc(1, sizeof(struct ref_lock));
@@ -836,8 +836,11 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
 
        lock->lk = xcalloc(1, sizeof(struct lock_file));
 
-       if (flags & REF_NODEREF)
+       lflags = LOCK_DIE_ON_ERROR;
+       if (flags & REF_NODEREF) {
                ref = orig_ref;
+               lflags |= LOCK_NODEREF;
+       }
        lock->ref_name = xstrdup(ref);
        lock->orig_ref_name = xstrdup(orig_ref);
        ref_file = git_path("%s", ref);
@@ -851,8 +854,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
                error("unable to create directory for %s", ref_file);
                goto error_return;
        }
-       lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
 
+       lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags);
        return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
 
  error_return:
index 8a04066d6142823183033aaff8754febe31ad1b0..e530a21e5c92e49012c459fba0eb7732672883c2 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -749,17 +749,19 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec)
        return -1;
 }
 
-struct ref *alloc_ref(unsigned namelen)
+static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,
+               const char *name)
 {
-       struct ref *ret = xcalloc(1, sizeof(struct ref) + namelen);
-       return ret;
+       size_t len = strlen(name);
+       struct ref *ref = xcalloc(1, sizeof(struct ref) + prefixlen + len + 1);
+       memcpy(ref->name, prefix, prefixlen);
+       memcpy(ref->name + prefixlen, name, len);
+       return ref;
 }
 
-struct ref *alloc_ref_from_str(const char* str)
+struct ref *alloc_ref(const char *name)
 {
-       struct ref *ret = alloc_ref(strlen(str) + 1);
-       strcpy(ret->name, str);
-       return ret;
+       return alloc_ref_with_prefix("", 0, name);
 }
 
 static struct ref *copy_ref(const struct ref *ref)
@@ -870,21 +872,20 @@ static struct ref *try_explicit_object_name(const char *name)
        struct ref *ref;
 
        if (!*name) {
-               ref = alloc_ref(20);
-               strcpy(ref->name, "(delete)");
+               ref = alloc_ref("(delete)");
                hashclr(ref->new_sha1);
                return ref;
        }
        if (get_sha1(name, sha1))
                return NULL;
-       ref = alloc_ref_from_str(name);
+       ref = alloc_ref(name);
        hashcpy(ref->new_sha1, sha1);
        return ref;
 }
 
 static struct ref *make_linked_ref(const char *name, struct ref ***tail)
 {
-       struct ref *ret = alloc_ref_from_str(name);
+       struct ref *ret = alloc_ref(name);
        tail_link_ref(ret, tail);
        return ret;
 }
@@ -1152,10 +1153,8 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
                        struct ref *cpy = copy_ref(ref);
                        match = ref->name + remote_prefix_len;
 
-                       cpy->peer_ref = alloc_ref(local_prefix_len +
-                                                 strlen(match) + 1);
-                       sprintf(cpy->peer_ref->name, "%s%s",
-                               refspec->dst, match);
+                       cpy->peer_ref = alloc_ref_with_prefix(refspec->dst,
+                                       local_prefix_len, match);
                        if (refspec->force)
                                cpy->peer_ref->force = 1;
                        *tail = cpy;
@@ -1188,25 +1187,18 @@ struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
 
 static struct ref *get_local_ref(const char *name)
 {
-       struct ref *ret;
        if (!name)
                return NULL;
 
-       if (!prefixcmp(name, "refs/")) {
-               return alloc_ref_from_str(name);
-       }
+       if (!prefixcmp(name, "refs/"))
+               return alloc_ref(name);
 
        if (!prefixcmp(name, "heads/") ||
            !prefixcmp(name, "tags/") ||
-           !prefixcmp(name, "remotes/")) {
-               ret = alloc_ref(strlen(name) + 6);
-               sprintf(ret->name, "refs/%s", name);
-               return ret;
-       }
+           !prefixcmp(name, "remotes/"))
+               return alloc_ref_with_prefix("refs/", 5, name);
 
-       ret = alloc_ref(strlen(name) + 12);
-       sprintf(ret->name, "refs/heads/%s", name);
-       return ret;
+       return alloc_ref_with_prefix("refs/heads/", 11, name);
 }
 
 int get_fetch_map(const struct ref *remote_refs,
index c6163ff5b1f7e7c96bedaa9a6b561a9f4a8ed7c2..d2e170ce664c0efd9ded356c171dabf4b26e452c 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -55,9 +55,7 @@ struct refspec {
 
 extern const struct refspec *tag_refspec;
 
-struct ref *alloc_ref(unsigned namelen);
-
-struct ref *alloc_ref_from_str(const char* str);
+struct ref *alloc_ref(const char *name);
 
 struct ref *copy_ref_list(const struct ref *ref);
 
index 5bb5316cdae16154fd4aaffe996710c0bcfc0b94..8e5532b1ff96a88133d837b9a29fa78117c36abd 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -352,7 +352,8 @@ int setup_rerere(struct string_list *merge_rr)
                return -1;
 
        merge_rr_path = xstrdup(git_path("MERGE_RR"));
-       fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
+       fd = hold_lock_file_for_update(&write_lock, merge_rr_path,
+                                      LOCK_DIE_ON_ERROR);
        read_rr(merge_rr);
        return fd;
 }
index 70bb453be25d4e5ba05ee5f2565630a8994842f2..ab2b520f033c75e5e8d3434376a203add59559a4 100644 (file)
@@ -388,7 +388,7 @@ static void read_info_alternates(const char * relative_base, int depth)
 void add_to_alternates_file(const char *reference)
 {
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-       int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), 1);
+       int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
        char *alt = mkpath("%s/objects\n", reference);
        write_or_die(fd, alt, strlen(alt));
        if (commit_lock_file(lock))
index b0d31f5a9bb8b3474665147327d94ad5067fa206..849911683a799981a565416a88fe9092b3727853 100755 (executable)
@@ -27,4 +27,64 @@ test_expect_success 'reset should work' '
   test_cmp expect actual
 '
 
+test_expect_success 'reset should remove remnants from a failed merge' '
+  git read-tree --reset -u HEAD &&
+  git ls-files -s >expect &&
+  sha1=$(git rev-parse :new) &&
+  (
+       echo "100644 $sha1 1    old"
+       echo "100644 $sha1 3    old"
+  ) | git update-index --index-info &&
+  >old &&
+  git ls-files -s &&
+  git read-tree --reset -u HEAD &&
+  git ls-files -s >actual &&
+  ! test -f old
+'
+
+test_expect_success 'Porcelain reset should remove remnants too' '
+  git read-tree --reset -u HEAD &&
+  git ls-files -s >expect &&
+  sha1=$(git rev-parse :new) &&
+  (
+       echo "100644 $sha1 1    old"
+       echo "100644 $sha1 3    old"
+  ) | git update-index --index-info &&
+  >old &&
+  git ls-files -s &&
+  git reset --hard &&
+  git ls-files -s >actual &&
+  ! test -f old
+'
+
+test_expect_success 'Porcelain checkout -f should remove remnants too' '
+  git read-tree --reset -u HEAD &&
+  git ls-files -s >expect &&
+  sha1=$(git rev-parse :new) &&
+  (
+       echo "100644 $sha1 1    old"
+       echo "100644 $sha1 3    old"
+  ) | git update-index --index-info &&
+  >old &&
+  git ls-files -s &&
+  git checkout -f &&
+  git ls-files -s >actual &&
+  ! test -f old
+'
+
+test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
+  git read-tree --reset -u HEAD &&
+  git ls-files -s >expect &&
+  sha1=$(git rev-parse :new) &&
+  (
+       echo "100644 $sha1 1    old"
+       echo "100644 $sha1 3    old"
+  ) | git update-index --index-info &&
+  >old &&
+  git ls-files -s &&
+  git checkout -f HEAD &&
+  git ls-files -s >actual &&
+  ! test -f old
+'
+
 test_done
index 66aca99fd32c6b98f5e6e34a3cf7b096b3e236cf..5b4d6f71387ce7f30cb7ee6b596357aa1942ab6e 100755 (executable)
@@ -187,6 +187,19 @@ test_expect_success 'but with -f it should work.' '
        test_must_fail git ls-files --error-unmatch baz
 '
 
+test_expect_failure 'refuse to remove cached empty file with modifications' '
+       touch empty &&
+       git add empty &&
+       echo content >empty &&
+       test_must_fail git rm --cached empty
+'
+
+test_expect_success 'remove intent-to-add file without --force' '
+       echo content >intent-to-add &&
+       git add -N intent-to-add
+       git rm --cached intent-to-add
+'
+
 test_expect_success 'Recursive test setup' '
        mkdir -p frotz &&
        echo qfwfq >frotz/nitfol &&
index 421f4bba3fbc666cf2ab7ec4289f10d29d61eebb..3cf5b5c4ea05d861d75aa146a52d7d273dcaa4cb 100755 (executable)
@@ -25,11 +25,11 @@ cat > expected <<\EOF
 EOF
 test_expect_success 'diff without --binary' \
        'git diff | git apply --stat --summary >current &&
-        cmp current expected'
+        test_cmp expected current'
 
 test_expect_success 'diff with --binary' \
        'git diff --binary | git apply --stat --summary >current &&
-        cmp current expected'
+        test_cmp expected current'
 
 # apply needs to be able to skip the binary material correctly
 # in order to report the line number of a corrupt patch.
index e395ff4e341bacea21cc5cd909304b7bb4fcb044..c942c8be85339157e22f755d8fc94e64efaee4dd 100755 (executable)
@@ -57,6 +57,11 @@ test_expect_success \
      git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
      git commit-tree $treeid </dev/null)'
 
+test_expect_success \
+    'create bare clone' \
+    'git clone --bare . bare.git &&
+     cp .gitattributes bare.git/info/attributes'
+
 test_expect_success \
     'remove ignored file' \
     'rm a/ignored'
@@ -73,11 +78,19 @@ test_expect_success \
     'git archive vs. git tar-tree' \
     'diff b.tar b2.tar'
 
+test_expect_success \
+    'git archive in a bare repo' \
+    '(cd bare.git && git archive HEAD) >b3.tar'
+
+test_expect_success \
+    'git archive vs. the same in a bare repo' \
+    'test_cmp b.tar b3.tar'
+
 test_expect_success \
     'validate file modification time' \
     'mkdir extract &&
      "$TAR" xf b.tar -C extract a/a &&
-     perl -e '\''print((stat("extract/a/a"))[9], "\n")'\'' >b.mtime &&
+     test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime &&
      echo "1117231200" >expected.mtime &&
      diff expected.mtime b.mtime'
 
@@ -151,6 +164,14 @@ test_expect_success \
     'git archive --format=zip' \
     'git archive --format=zip HEAD >d.zip'
 
+test_expect_success \
+    'git archive --format=zip in a bare repo' \
+    '(cd bare.git && git archive --format=zip HEAD) >d1.zip'
+
+test_expect_success \
+    'git archive --format=zip vs. the same in a bare repo' \
+    'test_cmp d.zip d1.zip'
+
 $UNZIP -v >/dev/null 2>&1
 if [ $? -eq 127 ]; then
        echo "Skipping ZIP tests, because unzip was not found"
index 6424db1f28e11c3ac6eb629ba4db7380812eab72..344ab25b8b6ddcd8b687a72e43b9e26aec18263e 100755 (executable)
@@ -177,4 +177,14 @@ test_expect_success \
        ".git/objects/pack/pack-${pack1}.pack" 2>&1) &&
      echo "$err" | grep "CRC mismatch"'
 
+test_expect_success 'running index-pack in the object store' '
+    rm -f .git/objects/pack/* &&
+    cp test-1-${pack1}.pack .git/objects/pack/pack-${pack1}.pack &&
+    (
+       cd .git/objects/pack
+       git index-pack pack-${pack1}.pack
+    ) &&
+    test -f .git/objects/pack/pack-${pack1}.idx
+'
+
 test_done
index 544771d8fa98ec70d84e5083c2bfc0ebdf45f9a1..6fe2f87b850c1a2d689d5a831084da8915c74ea2 100755 (executable)
@@ -103,6 +103,17 @@ unset GIT_CONFIG GIT_CONFIG_LOCAL
 HOME=`pwd`/no-such-directory
 export HOME ;# this way we force the victim/.git/config to be used.
 
+test_expect_failure \
+       'pushing a delete should be denied with denyDeletes' '
+       cd victim &&
+       git config receive.denyDeletes true &&
+       git branch extra master &&
+       cd .. &&
+       test -f victim/.git/refs/heads/extra &&
+       test_must_fail git send-pack ./victim/.git/ :extra master
+'
+rm -f victim/.git/refs/heads/extra
+
 test_expect_success \
         'pushing with --force should be denied with denyNonFastforwards' '
        cd victim &&
index 86abc6227105e27fdb9ad99e34193efb90a6242f..cb9aacc7bc62e2ecfdca0dc7f6071e5330fe09d0 100755 (executable)
@@ -12,7 +12,7 @@ test_expect_success setup '
        mkdir another && (
                cd another &&
                git init &&
-               git fetch .. master:master
+               git fetch --update-head-ok .. master:master
        ) &&
 
        >file2 && git add file2 && test_tick &&
index c4496635dfba85b0e9fc105c4516096d433881a4..c4380c7e3232f2ccffab4b84358a4f376533249b 100755 (executable)
@@ -28,7 +28,7 @@ tokens_match () {
 }
 
 check_remote_track () {
-       actual=$(git remote show "$1" | sed -n -e '$p') &&
+       actual=$(git remote show "$1" | sed -e '1,/Tracked/d') &&
        shift &&
        tokens_match "$*" "$actual"
 }
@@ -115,9 +115,11 @@ cat > test/expect << EOF
   New remote branch (next fetch will store in remotes/origin)
     master
   Tracked remote branches
-    side master
+    side
+    master
   Local branches pushed with 'git push'
-    master:upstream +refs/tags/lastbackup
+    master:upstream
+    +refs/tags/lastbackup
 EOF
 
 test_expect_success 'show' '
@@ -144,9 +146,11 @@ cat > test/expect << EOF
   Remote branch merged with 'git pull' while on branch master
     master
   Tracked remote branches
-    master side
+    master
+    side
   Local branches pushed with 'git push'
-    master:upstream +refs/tags/lastbackup
+    master:upstream
+    +refs/tags/lastbackup
 EOF
 
 test_expect_success 'show -n' '
@@ -188,7 +192,7 @@ test_expect_success 'prune --dry-run' '
 test_expect_success 'add --mirror && prune' '
        (mkdir mirror &&
         cd mirror &&
-        git init &&
+        git init --bare &&
         git remote add --mirror -f origin ../one) &&
        (cd one &&
         git branch -m side2 side) &&
index 9aae4965dac0f2bf4bb8684c4b8d43dcb60529e9..9e679b402dc826185377c431d353decd8a8a2bed 100755 (executable)
@@ -323,4 +323,16 @@ test_expect_success 'auto tag following fetches minimum' '
        )
 '
 
+test_expect_success 'refuse to fetch into the current branch' '
+
+       test_must_fail git fetch . side:master
+
+'
+
+test_expect_success 'fetch into the current branch with --update-head-ok' '
+
+       git fetch --update-head-ok . side:master
+
+'
+
 test_done
index 997b2db827c4f37512c6b5d2f861e12105e2a32d..725771fac167ea5aac8cf65b916c96918b0f5e0d 100755 (executable)
@@ -29,6 +29,18 @@ test_expect_success 'checking the results' '
        diff file cloned/file
 '
 
+test_expect_success 'pulling into void using master:master' '
+       mkdir cloned-uho &&
+       (
+               cd cloned-uho &&
+               git init &&
+               git pull .. master:master
+       ) &&
+       test -f file &&
+       test -f cloned-uho/file &&
+       test_cmp file cloned-uho/file
+'
+
 test_expect_success 'test . as a remote' '
 
        git branch copy master &&
index 82769b89fc79d91982420ddfc83ce6c3dd672095..0e21632f19e75e1e7bbca1ceb2b9402a4d291584 100755 (executable)
@@ -330,12 +330,26 @@ test_expect_success \
     test "$(git config branch.track2.merge)"
     git config branch.autosetupmerge false'
 
-test_expect_success \
-    'checkout w/--track from non-branch HEAD fails' '
-    git checkout -b delete-me master &&
-    rm .git/refs/heads/delete-me &&
-    test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
-    test_must_fail git checkout --track -b track'
+test_expect_success 'checkout w/--track from non-branch HEAD fails' '
+    git checkout master^0 &&
+    test_must_fail git symbolic-ref HEAD &&
+    test_must_fail git checkout --track -b track &&
+    test_must_fail git rev-parse --verify track &&
+    test_must_fail git symbolic-ref HEAD &&
+    test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)"
+'
+
+test_expect_success 'detach a symbolic link HEAD' '
+    git checkout master &&
+    git config --bool core.prefersymlinkrefs yes &&
+    git checkout side &&
+    git checkout master &&
+    it=$(git symbolic-ref HEAD) &&
+    test "z$it" = zrefs/heads/master &&
+    here=$(git rev-parse --verify refs/heads/master) &&
+    git checkout side^ &&
+    test "z$(git rev-parse --verify refs/heads/master)" = "z$here"
+'
 
 test_expect_success \
     'checkout with --track fakes a sensible -b <name>' '
index 1905fb34cd222aa9443d28fd673b0cc9e02f3335..93f875f50054225e0879843b6e29ac057ba6c020 100755 (executable)
@@ -292,6 +292,12 @@ test_expect_success 'status submodule summary is disabled by default' '
        test_cmp expect output
 '
 
+# we expect the same as the previous test
+test_expect_success 'status --untracked-files=all does not show submodule' '
+       git status --untracked-files=all >output &&
+       test_cmp expect output
+'
+
 head=$(cd sm && git rev-parse --short=7 --verify HEAD)
 
 cat >expect <<EOF
index 328444a3068f5083e3d64e92e88660c724acffdc..91b5aced1bcea74a66c3365e4aceff355d577f6a 100755 (executable)
@@ -983,7 +983,7 @@ test_expect_success \
         git checkout subuse1 &&
         rm -rf sub && mkdir sub && cd sub &&
         git init &&
-        git fetch .. refs/heads/sub:refs/heads/master &&
+        git fetch --update-head-ok .. refs/heads/sub:refs/heads/master &&
         git checkout master &&
         cd .. &&
         git submodule init &&
index 90da448ebec3e5375b7725ba7f297c1c74199b87..d5358cbaac2022483b74366555fc9707a7d8ad97 100644 (file)
@@ -1,39 +1,83 @@
+/*
+ * This program can either change modification time of the given
+ * file(s) or just print it. The program does not change atime nor
+ * ctime (their values are explicitely preserved).
+ *
+ * The mtime can be changed to an absolute value:
+ *
+ *     test-chmtime =<seconds> file...
+ *
+ * Relative to the current time as returned by time(3):
+ *
+ *     test-chmtime =+<seconds> (or =-<seconds>) file...
+ *
+ * Or relative to the current mtime of the file:
+ *
+ *     test-chmtime <seconds> file...
+ *     test-chmtime +<seconds> (or -<seconds>) file...
+ *
+ * Examples:
+ *
+ * To just print the mtime use --verbose and set the file mtime offset to 0:
+ *
+ *     test-chmtime -v +0 file
+ *
+ * To set the mtime to current time:
+ *
+ *     test-chmtime =+0 file
+ *
+ */
 #include "git-compat-util.h"
 #include <utime.h>
 
-static const char usage_str[] = "(+|=|=+|=-|-)<seconds> <file>...";
+static const char usage_str[] = "-v|--verbose (+|=|=+|=-|-)<seconds> <file>...";
 
-int main(int argc, const char *argv[])
+static int timespec_arg(const char *arg, long int *set_time, int *set_eq)
 {
-       int i;
-       int set_eq;
-       long int set_time;
        char *test;
-       const char *timespec;
-
-       if (argc < 3)
-               goto usage;
-
-       timespec = argv[1];
-       set_eq = (*timespec == '=') ? 1 : 0;
-       if (set_eq) {
+       const char *timespec = arg;
+       *set_eq = (*timespec == '=') ? 1 : 0;
+       if (*set_eq) {
                timespec++;
                if (*timespec == '+') {
-                       set_eq = 2; /* relative "in the future" */
+                       *set_eq = 2; /* relative "in the future" */
                        timespec++;
                }
        }
-       set_time = strtol(timespec, &test, 10);
+       *set_time = strtol(timespec, &test, 10);
        if (*test) {
-               fprintf(stderr, "Not a base-10 integer: %s\n", argv[1] + 1);
-               goto usage;
+               fprintf(stderr, "Not a base-10 integer: %s\n", arg + 1);
+               return 0;
        }
-       if ((set_eq && set_time < 0) || set_eq == 2) {
+       if ((*set_eq && *set_time < 0) || *set_eq == 2) {
                time_t now = time(NULL);
-               set_time += now;
+               *set_time += now;
        }
+       return 1;
+}
+
+int main(int argc, const char *argv[])
+{
+       static int verbose;
 
-       for (i = 2; i < argc; i++) {
+       int i = 1;
+       /* no mtime change by default */
+       int set_eq = 0;
+       long int set_time = 0;
+
+       if (argc < 3)
+               goto usage;
+
+       if (strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) {
+               verbose = 1;
+               ++i;
+       }
+       if (timespec_arg(argv[i], &set_time, &set_eq))
+               ++i;
+       else
+               goto usage;
+
+       for (; i < argc; i++) {
                struct stat sb;
                struct utimbuf utb;
 
@@ -46,7 +90,12 @@ int main(int argc, const char *argv[])
                utb.actime = sb.st_atime;
                utb.modtime = set_eq ? set_time : sb.st_mtime + set_time;
 
-               if (utime(argv[i], &utb) < 0) {
+               if (verbose) {
+                       uintmax_t mtime = utb.modtime < 0 ? 0: utb.modtime;
+                       printf("%"PRIuMAX"\t%s\n", mtime, argv[i]);
+               }
+
+               if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) {
                        fprintf(stderr, "Failed to modify time on %s: %s\n",
                                argv[i], strerror(errno));
                        return -1;
index 1c510a3360e26bc7cf622036574fe9d06c1aa6da..56831c57c5cf6500714c46c63581ca98662d58c3 100644 (file)
@@ -75,7 +75,7 @@ static int read_loose_refs(struct strbuf *path, int name_offset,
 
                        if (fd < 0)
                                continue;
-                       next = alloc_ref(path->len - name_offset + 1);
+                       next = alloc_ref(path->buf + name_offset);
                        if (read_in_full(fd, buffer, 40) != 40 ||
                                        get_sha1_hex(buffer, next->old_sha1)) {
                                close(fd);
@@ -83,7 +83,6 @@ static int read_loose_refs(struct strbuf *path, int name_offset,
                                continue;
                        }
                        close(fd);
-                       strcpy(next->name, path->buf + name_offset);
                        (*tail)->next = next;
                        *tail = next;
                }
@@ -127,14 +126,13 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list)
                                      (*list)->next->name)) > 0)
                        list = &(*list)->next;
                if (!(*list)->next || cmp < 0) {
-                       struct ref *next = alloc_ref(len - 40);
+                       struct ref *next = alloc_ref(buffer + 41);
                        buffer[40] = '\0';
                        if (get_sha1_hex(buffer, next->old_sha1)) {
                                warning ("invalid SHA-1: %s", buffer);
                                free(next);
                                continue;
                        }
-                       strcpy(next->name, buffer + 41);
                        next->next = (*list)->next;
                        (*list)->next = next;
                        list = &(*list)->next;
@@ -501,7 +499,7 @@ static struct ref *get_refs_via_curl(struct transport *transport)
 
        strbuf_release(&buffer);
 
-       ref = alloc_ref_from_str("HEAD");
+       ref = alloc_ref("HEAD");
        if (!walker->fetch_ref(walker, ref) &&
            !resolve_remote_symref(ref, refs)) {
                ref->next = refs;
@@ -542,7 +540,7 @@ static struct ref *get_refs_from_bundle(struct transport *transport)
                die ("Could not read bundle '%s'.", transport->url);
        for (i = 0; i < data->header.references.nr; i++) {
                struct ref_list_entry *e = data->header.references.list + i;
-               struct ref *ref = alloc_ref_from_str(e->name);
+               struct ref *ref = alloc_ref(e->name);
                hashcpy(ref->old_sha1, e->sha1);
                ref->next = result;
                result = ref;
index e59d144d28164f2451784513105f6269f0e9167c..e5749ef638b4a9a490894d9d4fc5876d904ab9ac 100644 (file)
@@ -382,7 +382,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        o->merge_size = len;
 
        if (!dfc)
-               dfc = xcalloc(1, sizeof(struct cache_entry) + 1);
+               dfc = xcalloc(1, cache_entry_size(0));
        o->df_conflict_entry = dfc;
 
        if (len) {
diff --git a/userdiff.c b/userdiff.c
new file mode 100644 (file)
index 0000000..d95257a
--- /dev/null
@@ -0,0 +1,175 @@
+#include "userdiff.h"
+#include "cache.h"
+#include "attr.h"
+
+static struct userdiff_driver *drivers;
+static int ndrivers;
+static int drivers_alloc;
+
+#define FUNCNAME(name, pattern) \
+       { name, NULL, -1, { pattern, REG_EXTENDED } }
+static struct userdiff_driver builtin_drivers[] = {
+FUNCNAME("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$"),
+FUNCNAME("java",
+        "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
+        "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$"),
+FUNCNAME("objc",
+        /* Negate C statements that can look like functions */
+        "!^[ \t]*(do|for|if|else|return|switch|while)\n"
+        /* Objective-C methods */
+        "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
+        /* C functions */
+        "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
+        /* Objective-C class/protocol definitions */
+        "^(@(implementation|interface|protocol)[ \t].*)$"),
+FUNCNAME("pascal",
+        "^((procedure|function|constructor|destructor|interface|"
+               "implementation|initialization|finalization)[ \t]*.*)$"
+        "\n"
+        "^(.*=[ \t]*(class|record).*)$"),
+FUNCNAME("php", "^[\t ]*((function|class).*)"),
+FUNCNAME("python", "^[ \t]*((class|def)[ \t].*)$"),
+FUNCNAME("ruby", "^[ \t]*((class|module|def)[ \t].*)$"),
+FUNCNAME("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$"),
+FUNCNAME("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$"),
+{ "default", NULL, -1, { NULL, 0 } },
+};
+#undef FUNCNAME
+
+static struct userdiff_driver driver_true = {
+       "diff=true",
+       NULL,
+       0,
+       { NULL, 0 }
+};
+
+static struct userdiff_driver driver_false = {
+       "!diff",
+       NULL,
+       1,
+       { NULL, 0 }
+};
+
+static struct userdiff_driver *userdiff_find_by_namelen(const char *k, int len)
+{
+       int i;
+       for (i = 0; i < ndrivers; i++) {
+               struct userdiff_driver *drv = drivers + i;
+               if (!strncmp(drv->name, k, len) && !drv->name[len])
+                       return drv;
+       }
+       for (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {
+               struct userdiff_driver *drv = builtin_drivers + i;
+               if (!strncmp(drv->name, k, len) && !drv->name[len])
+                       return drv;
+       }
+       return NULL;
+}
+
+static struct userdiff_driver *parse_driver(const char *var,
+               const char *value, const char *type)
+{
+       struct userdiff_driver *drv;
+       const char *dot;
+       const char *name;
+       int namelen;
+
+       if (prefixcmp(var, "diff."))
+               return NULL;
+       dot = strrchr(var, '.');
+       if (dot == var + 4)
+               return NULL;
+       if (strcmp(type, dot+1))
+               return NULL;
+
+       name = var + 5;
+       namelen = dot - name;
+       drv = userdiff_find_by_namelen(name, namelen);
+       if (!drv) {
+               ALLOC_GROW(drivers, ndrivers+1, drivers_alloc);
+               drv = &drivers[ndrivers++];
+               memset(drv, 0, sizeof(*drv));
+               drv->name = xmemdupz(name, namelen);
+               drv->binary = -1;
+       }
+       return drv;
+}
+
+static int parse_funcname(struct userdiff_funcname *f, const char *k,
+               const char *v, int cflags)
+{
+       if (git_config_string(&f->pattern, k, v) < 0)
+               return -1;
+       f->cflags = cflags;
+       return 1;
+}
+
+static int parse_string(const char **d, const char *k, const char *v)
+{
+       if (git_config_string(d, k, v) < 0)
+               return -1;
+       return 1;
+}
+
+static int parse_tristate(int *b, const char *k, const char *v)
+{
+       if (v && !strcasecmp(v, "auto"))
+               *b = -1;
+       else
+               *b = git_config_bool(k, v);
+       return 1;
+}
+
+int userdiff_config_basic(const char *k, const char *v)
+{
+       struct userdiff_driver *drv;
+
+       if ((drv = parse_driver(k, v, "funcname")))
+               return parse_funcname(&drv->funcname, k, v, 0);
+       if ((drv = parse_driver(k, v, "xfuncname")))
+               return parse_funcname(&drv->funcname, k, v, REG_EXTENDED);
+       if ((drv = parse_driver(k, v, "binary")))
+               return parse_tristate(&drv->binary, k, v);
+
+       return 0;
+}
+
+int userdiff_config_porcelain(const char *k, const char *v)
+{
+       struct userdiff_driver *drv;
+
+       if ((drv = parse_driver(k, v, "command")))
+               return parse_string(&drv->external, k, v);
+       if ((drv = parse_driver(k, v, "textconv")))
+               return parse_string(&drv->textconv, k, v);
+
+       return 0;
+}
+
+struct userdiff_driver *userdiff_find_by_name(const char *name) {
+       int len = strlen(name);
+       return userdiff_find_by_namelen(name, len);
+}
+
+struct userdiff_driver *userdiff_find_by_path(const char *path)
+{
+       static struct git_attr *attr;
+       struct git_attr_check check;
+
+       if (!attr)
+               attr = git_attr("diff", 4);
+       check.attr = attr;
+
+       if (!path)
+               return NULL;
+       if (git_checkattr(path, 1, &check))
+               return NULL;
+
+       if (ATTR_TRUE(check.value))
+               return &driver_true;
+       if (ATTR_FALSE(check.value))
+               return &driver_false;
+       if (ATTR_UNSET(check.value))
+               return NULL;
+       return userdiff_find_by_name(check.value);
+}
diff --git a/userdiff.h b/userdiff.h
new file mode 100644 (file)
index 0000000..f29c18f
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef USERDIFF_H
+#define USERDIFF_H
+
+struct userdiff_funcname {
+       const char *pattern;
+       int cflags;
+};
+
+struct userdiff_driver {
+       const char *name;
+       const char *external;
+       int binary;
+       struct userdiff_funcname funcname;
+       const char *textconv;
+};
+
+int userdiff_config_basic(const char *k, const char *v);
+int userdiff_config_porcelain(const char *k, const char *v);
+struct userdiff_driver *userdiff_find_by_name(const char *name);
+struct userdiff_driver *userdiff_find_by_path(const char *path);
+
+#endif /* USERDIFF */
index 6b4cf70c6af1289501f72cbde78cd04da57293f3..679adab6a0fccef460acaf90b116b8c6cb9bd460 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -191,7 +191,7 @@ static int interpret_target(struct walker *walker, char *target, unsigned char *
        if (!get_sha1_hex(target, sha1))
                return 0;
        if (!check_ref_format(target)) {
-               struct ref *ref = alloc_ref_from_str(target);
+               struct ref *ref = alloc_ref(target);
                if (!walker->fetch_ref(walker, ref)) {
                        hashcpy(sha1, ref->old_sha1);
                        free(ref);
index d2eac36aeaafab2800b4f0802c7db9656c44cdeb..c3a9cab8980daa38e647a13eb2f0fcb42fbfbb8e 100644 (file)
@@ -277,20 +277,9 @@ static void wt_status_print_untracked(struct wt_status *s)
 
        read_directory(&dir, ".", "", 0, NULL);
        for(i = 0; i < dir.nr; i++) {
-               /* check for matching entry, which is unmerged; lifted from
-                * builtin-ls-files:show_other_files */
                struct dir_entry *ent = dir.entries[i];
-               int pos = cache_name_pos(ent->name, ent->len);
-               struct cache_entry *ce;
-               if (0 <= pos)
-                       die("bug in wt_status_print_untracked");
-               pos = -pos - 1;
-               if (pos < active_nr) {
-                       ce = active_cache[pos];
-                       if (ce_namelen(ce) == ent->len &&
-                           !memcmp(ce->name, ent->name, ent->len))
-                               continue;
-               }
+               if (!cache_name_is_other(ent->name, ent->len))
+                       continue;
                if (!shown_header) {
                        s->workdir_untracked = 1;
                        wt_status_print_untracked_header(s);