Merge branch 'db/fetch-pack'
authorJunio C Hamano <gitster@pobox.com>
Thu, 25 Oct 2007 04:59:50 +0000 (21:59 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 25 Oct 2007 04:59:50 +0000 (21:59 -0700)
* db/fetch-pack: (60 commits)
Define compat version of mkdtemp for systems lacking it
Avoid scary errors about tagged trees/blobs during git-fetch
fetch: if not fetching from default remote, ignore default merge
Support 'push --dry-run' for http transport
Support 'push --dry-run' for rsync transport
Fix 'push --all branch...' error handling
Fix compilation when NO_CURL is defined
Added a test for fetching remote tags when there is not tags.
Fix a crash in ls-remote when refspec expands into nothing
Remove duplicate ref matches in fetch
Restore default verbosity for http fetches.
fetch/push: readd rsync support
Introduce remove_dir_recursively()
bundle transport: fix an alloc_ref() call
Allow abbreviations in the first refspec to be merged
Prevent send-pack from segfaulting when a branch doesn't match
Cleanup unnecessary break in remote.c
Cleanup style nit of 'x == NULL' in remote.c
Fix memory leaks when disconnecting transport instances
Ensure builtin-fetch honors {fetch,transfer}.unpackLimit
...

59 files changed:
Documentation/RelNotes-1.5.3.5.txt
Documentation/git-cherry-pick.txt
Documentation/git-filter-branch.txt
Documentation/git-gc.txt
Documentation/git-rebase.txt
Documentation/git-send-email.txt
Documentation/git-tag.txt
Documentation/git.txt
Documentation/gitk.txt
Makefile
attr.c
builtin-apply.c
builtin-archive.c
builtin-blame.c
builtin-config.c
builtin-fetch--tool.c
builtin-gc.c
builtin-mailinfo.c
builtin-reset.c
contrib/fast-import/git-p4
contrib/hooks/post-receive-email
daemon.c
diff.c
diffcore-break.c
diffcore-delta.c
dir.c
entry.c
fast-import.c
git-am.sh
git-cvsexportcommit.perl
git-cvsserver.perl
git-filter-branch.sh
git-gui/git-gui.sh
git-gui/lib/commit.tcl
git-gui/lib/console.tcl
git-gui/lib/status_bar.tcl
git-instaweb.sh
git-rebase--interactive.sh
git-send-email.perl
git-stash.sh
git-svn.perl
git.c
gitk
gitweb/gitweb.perl
match-trees.c
read-cache.c
receive-pack.c
remote.c
send-pack.c
setup.c
show-index.c
t/t0020-crlf.sh
t/t1501-worktree.sh
t/t5400-send-pack.sh
t/t7003-filter-branch.sh
t/t8004-blame.sh [new file with mode: 0755]
t/t9101-git-svn-props.sh
t/t9500-gitweb-standalone-no-errors.sh
tree-diff.c
index 6a1901a96d827dd90f38e87e2bb6c56a43b7b9e9..9581e03c49eafa61f8d17b027395aa5613c9b27f 100644 (file)
@@ -4,21 +4,69 @@ GIT v1.5.3.5 Release Notes
 Fixes since v1.5.3.4
 --------------------
 
- * "git-config" silently ignored options after --list; now it wilh
+ * Comes with git-gui 0.8.4.
+
+ * "git-config" silently ignored options after --list; now it will
    error out with a usage message.
 
  * "git-config --file" failed if the argument used a relative path
    as it changed directories before opening the file.
 
+ * "git-config --file" now displays a proper error message if it
+   cannot read the file specified on the command line.
+
+ * "git-config", "git-diff", "git-apply" failed if run from a
+   subdirectory with relative GIT_DIR and GIT_WORK_TREE set.
+
+ * "git-blame" crashed if run during a merge conflict.
+
  * "git-add -i" did not handle single line hunks correctly.
 
+ * "git-rebase -i" and "git-stash apply" failed if external diff
+   drivers were used for one or more files in a commit.  They now
+   avoid calling the external diff drivers.
+
  * "git-log --follow" did not work unless diff generation (e.g. -p)
    was also requested.
 
+ * "git-log --follow -B" did not work at all.  Fixed.
+
+ * "git-log -M -B" did not correctly handle cases of very large files
+   being renamed and replaced by very small files in the same commit.
+
  * "git-log" printed extra newlines between commits when a diff
    was generated internally (e.g. -S or --follow) but not displayed.
 
- * Documention updates for supported (but previously undocumented)
+ * "git-push" error message is more helpful when pushing to a
+   repository with no matching refs and none specified.
+
+ * "git-push" now respects + (force push) on wildcard refspecs,
+   matching the behavior of git-fetch.
+
+ * "git-filter-branch" now updates the working directory when it
+   has finished filtering the current branch.
+
+ * "git-instaweb" no longer fails on Mac OS X.
+
+ * "git-cvsexportcommit" didn't always create new parent directories
+   before trying to create new child directories.  Fixed.
+
+ * "git-fetch" printed a scary (but bogus) error message while
+   fetching a tag that pointed to a tree or blob.  The error did
+   not impact correctness, only user perception.  The bogus error
+   is no longer printed.
+
+ * "git-ls-files --ignored" did not properly descend into non-ignored
+   directories that themselves contained ignored files if d_type
+   was not supported by the filesystem.  This bug impacted systems
+   such as AFS.  Fixed.
+
+ * Git segfaulted when reading an invalid .gitattributes file.  Fixed.
+
+ * post-receive-email example hook fixed was fixed for
+   non-fast-forward updates.
+
+ * Documentation updates for supported (but previously undocumented)
    options of "git-archive" and "git-reflog".
 
  * "make clean" no longer deletes the configure script that ships
index 47b1e8c2fcd567b7e9d673f2d3ff30c9c32a1b83..76a2edfd9b3665b4dbe8e3837c06737e7c5fd232 100644 (file)
@@ -27,11 +27,12 @@ OPTIONS
        message prior committing.
 
 -x::
-       Cause the command to append which commit was
-       cherry-picked after the original commit message when
-       making a commit.  Do not use this option if you are
-       cherry-picking from your private branch because the
-       information is useless to the recipient.  If on the
+       When recording the commit, append to the original commit
+       message a note that indicates which commit this change
+       was cherry-picked from.  Append the note only for cherry
+       picks without conflicts.  Do not use this option if
+       you are cherry-picking from your private branch because
+       the information is useless to the recipient.  If on the
        other hand you are cherry-picking between two publicly
        visible branches (e.g. backporting a fix to a
        maintenance branch for an older release from a
index c878ed395eb27de02efda2e3018ae76fbb799c7b..ba9b4fbca79321003f8cda6341d066f9ccc00349 100644 (file)
@@ -180,8 +180,7 @@ A significantly faster version:
 git filter-branch --index-filter 'git update-index --remove filename' HEAD
 --------------------------------------------------------------------------
 
-Now, you will get the rewritten history saved in the branch 'newbranch'
-(your current branch is left untouched).
+Now, you will get the rewritten history saved in HEAD.
 
 To set a commit (which typically is at the tip of another
 history) to be the parent of the current initial commit, in
index b9d5660eacee03bde2360b97b80f3378972fe678..872056ea040f1f4953b538aaa4a14ded4d2170a9 100644 (file)
@@ -19,7 +19,8 @@ created from prior invocations of gitlink:git-add[1].
 
 Users are encouraged to run this task on a regular basis within
 each repository to maintain good disk space utilization and good
-operating performance.
+operating performance. Some git commands may automatically run
+`git-gc`; see the `--auto` flag below for details.
 
 OPTIONS
 -------
@@ -44,18 +45,23 @@ OPTIONS
        few hundred changesets or so.
 
 --auto::
-       With this option, `git gc` checks if there are too many
-       loose objects in the repository and runs
-       gitlink:git-repack[1] with `-d -l` option to pack them.
-       The threshold for loose objects is set with `gc.auto` configuration
-       variable, and can be disabled by setting it to 0.  Some
-       Porcelain commands use this after they perform operation
-       that could create many loose objects automatically.
-       Additionally, when there are too many packs are present,
-       they are consolidated into one larger pack by running
-       the `git-repack` command with `-A` option.  The
-       threshold for number of packs is set with
-       `gc.autopacklimit` configuration variable.
+       With this option, `git gc` checks whether any housekeeping is
+       required; if not, it exits without performing any work.
+       Some git commands run `git gc --auto` after performing
+       operations that could create many loose objects.
++
+Housekeeping is required if there are too many loose objects or
+too many packs in the repository. If the number of loose objects
+exceeds the value of the `gc.auto` configuration variable, then
+all loose objects are combined into a single pack using
+`git-repack -d -l`.  Setting the value of `gc.auto` to 0
+disables automatic packing of loose objects.
++
+If the number of packs exceeds the value of `gc.autopacklimit`,
+then existing packs (except those marked with a `.keep` file)
+are consolidated into a single pack by using the `-A` option of
+`git-repack`. Setting `gc.autopacklimit` to 0 disables
+automatic consolidation of packs.
 
 Configuration
 -------------
index e8e75790fc21b403f428b8cca72ab42a072e1b38..e4326d3322d45fafbc03ff96a696efc092c12522 100644 (file)
@@ -28,7 +28,10 @@ The current branch is reset to <upstream>, or <newbase> if the
 `git reset --hard <upstream>` (or <newbase>).
 
 The commits that were previously saved into the temporary area are
-then reapplied to the current branch, one by one, in order.
+then reapplied to the current branch, one by one, in order. Note that
+any commits in HEAD which introduce the same textual changes as a commit
+in HEAD..<upstream> are omitted (i.e., a patch already accepted upstream
+with a different commit message or timestamp will be skipped).
 
 It is possible that a merge failure will prevent this process from being
 completely automatic.  You will have to resolve any such merge failure
@@ -62,6 +65,26 @@ would be:
 The latter form is just a short-hand of `git checkout topic`
 followed by `git rebase master`.
 
+If the upstream branch already contains a change you have made (e.g.,
+because you mailed a patch which was applied upstream), then that commit
+will be skipped. For example, running `git-rebase master` on the
+following history (in which A' and A introduce the same set of changes,
+but have different committer information):
+
+------------
+          A---B---C topic
+         /
+    D---E---A'---F master
+------------
+
+will result in:
+
+------------
+                   B'---C' topic
+                  /
+    D---E---A'---F master
+------------
+
 Here is how you would transplant a topic branch based on one
 branch to another, to pretend that you forked the topic branch
 from the latter branch, using `rebase --onto`.
index 3727776a0bf3196cd1d99ba4c2756767dca00567..e38b7021b4d38c07bd794e59be0a5e0747a8b775 100644 (file)
@@ -159,6 +159,9 @@ sendemail.aliasfiletype::
        Format of the file(s) specified in sendemail.aliasesfile. Must be
        one of 'mutt', 'mailrc', 'pine', or 'gnus'.
 
+sendemail.to::
+       Email address (or alias) to always send to.
+
 sendemail.cccmd::
        Command to execute to generate per patch file specific "Cc:"s.
 
index 22a23bf96f9d851546c296ad559f6ae4a23eedb1..10d3e3fa950e00b6004f968ff2c41477e1d57612 100644 (file)
@@ -214,6 +214,27 @@ having tracking branches.  Again, the heuristic to automatically
 follow such tags is a good thing.
 
 
+On Backdating Tags
+~~~~~~~~~~~~~~~~~~
+
+If you have imported some changes from another VCS and would like
+to add tags for major releases of your work, it is useful to be able
+to specify the date to embed inside of the tag object.  The data in
+the tag object affects, for example, the ordering of tags in the
+gitweb interface.
+
+To set the date used in future tag objects, set the environment
+variable GIT_AUTHOR_DATE to one or more of the date and time.  The
+date and time can be specified in a number of ways; the most common
+is "YYYY-MM-DD HH:MM".
+
+An example follows.
+
+------------
+$ GIT_AUTHOR_DATE="2006-10-02 10:31" git tag -s v1.0.1
+------------
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>,
index ce8f923a156ad0e33b15b5ef169887b49d5100ad..c4d87ac201e2d7a9f43a0d288af15578f51e3785 100644 (file)
@@ -49,7 +49,8 @@ Documentation for older releases are available here:
   link:RelNotes-1.5.3.4.txt[1.5.3.4],
   link:RelNotes-1.5.3.3.txt[1.5.3.3],
   link:RelNotes-1.5.3.2.txt[1.5.3.2],
-  link:RelNotes-1.5.3.1.txt[1.5.3.1].
+  link:RelNotes-1.5.3.1.txt[1.5.3.1],
+  link:RelNotes-1.5.3.txt[1.5.3].
 
 * release notes for
   link:RelNotes-1.5.2.5.txt[1.5.2.5],
index e9f82b97b91b92785b76a224e176b695593cdbe7..8dbfb0d5a3ef015367e990f7c24d2a47b176fde4 100644 (file)
@@ -69,7 +69,7 @@ gitk --since="2 weeks ago" \-- gitk::
        The "--" is necessary to avoid confusion with the *branch* named
        'gitk'
 
-gitk --max-count=100 --all -- Makefile::
+gitk --max-count=100 --all \-- Makefile::
 
        Show at most 100 changes made to the file 'Makefile'. Instead of only
        looking for changes in the current branch look in all branches.
index 6287418df3d326f029341fd1055db420ffd90a0d..b7289204ebfa628195acaf8b0e087289c1468c62 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -167,6 +167,7 @@ GITWEB_CONFIG = gitweb_config.perl
 GITWEB_HOME_LINK_STR = projects
 GITWEB_SITENAME =
 GITWEB_PROJECTROOT = /pub/git
+GITWEB_PROJECT_MAXDEPTH = 2007
 GITWEB_EXPORT_OK =
 GITWEB_STRICT_EXPORT =
 GITWEB_BASE_URL =
@@ -842,6 +843,7 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl
            -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
            -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
            -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
+           -e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
            -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
            -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
            -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
diff --git a/attr.c b/attr.c
index 92704a3f61c6d43d0377d86feb6598e68797022b..741db3b468c6a6ebbcd1414e42b4ef7d6ab3cc9d 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -209,8 +209,11 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
                num_attr = 0;
                cp = name + namelen;
                cp = cp + strspn(cp, blank);
-               while (*cp)
+               while (*cp) {
                        cp = parse_attr(src, lineno, cp, &num_attr, res);
+                       if (!cp)
+                               return NULL;
+               }
                if (pass)
                        break;
                res = xcalloc(1,
index 05c6bc3592e98958a4629ab856be33adc9ace33c..8411b38c7963852bffeb6dea1494399e8c9daa02 100644 (file)
@@ -152,7 +152,7 @@ struct patch {
        unsigned int is_rename:1;
        struct fragment *fragments;
        char *result;
-       unsigned long resultsize;
+       size_t resultsize;
        char old_sha1_prefix[41];
        char new_sha1_prefix[41];
        struct patch *next;
index 04385dea05110053db72e30a77e6d4a10bc7875b..6f29c2f40a01b3c60eaa9ecfa1ca4d63fe90c8eb 100644 (file)
@@ -148,12 +148,14 @@ void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
        buffer = read_sha1_file(sha1, type, sizep);
        if (buffer && S_ISREG(mode)) {
                struct strbuf buf;
+               size_t size = 0;
 
                strbuf_init(&buf, 0);
                strbuf_attach(&buf, buffer, *sizep, *sizep + 1);
                convert_to_working_tree(path, buf.buf, buf.len, &buf);
                convert_to_archive(path, buf.buf, buf.len, &buf, commit);
-               buffer = strbuf_detach(&buf, sizep);
+               buffer = strbuf_detach(&buf, &size);
+               *sizep = size;
        }
 
        return buffer;
index e3112a2d5bb2158bcc189942090a936c3f2b7cf6..8432b823e6ef6020a897838e7a561c3919c31f0b 100644 (file)
@@ -2059,6 +2059,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
                if (strbuf_read(&buf, 0, 0) < 0)
                        die("read error %s from stdin", strerror(errno));
        }
+       convert_to_git(path, buf.buf, buf.len, &buf);
        origin->file.ptr = buf.buf;
        origin->file.size = buf.len;
        pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1);
index d98b6c2c4cbbec367e498d33c5f670709dcac893..e5e243f27cb7ecab11ac0933a361d066f5b35ea9 100644 (file)
@@ -175,7 +175,10 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) {
                        if (argc != 2)
                                usage(git_config_set_usage);
-                       return git_config(show_all_config);
+                       if (git_config(show_all_config) < 0 && file && errno)
+                               die("unable to read config file %s: %s", file,
+                                   strerror(errno));
+                       return 0;
                }
                else if (!strcmp(argv[1], "--global")) {
                        char *home = getenv("HOME");
index 1e43d792216248c1abe3504c239ccd325f8d5ef1..6a78517958567e9dee4bfb00236caf5c3d1c3d67 100644 (file)
@@ -25,7 +25,7 @@ static int update_ref_env(const char *action,
                      unsigned char *oldval)
 {
        char msg[1024];
-       char *rla = getenv("GIT_REFLOG_ACTION");
+       const char *rla = getenv("GIT_REFLOG_ACTION");
 
        if (!rla)
                rla = "(reflog update)";
@@ -61,7 +61,7 @@ static int update_local_ref(const char *name,
        }
 
        if (get_sha1(name, sha1_old)) {
-               char *msg;
+               const char *msg;
        just_store:
                /* new ref */
                if (!strncmp(name, "refs/tags/", 10))
@@ -131,7 +131,7 @@ static int append_fetch_head(FILE *fp,
 
        if (get_sha1(head, sha1))
                return error("Not a valid object name: %s", head);
-       commit = lookup_commit_reference(sha1);
+       commit = lookup_commit_reference_gently(sha1, 1);
        if (!commit)
                not_for_merge = 1;
 
index 956c32d1af66444dae26a705159beba9adb7aba7..3a2ca4f901b985c45820c8a5f68061cf1c647f30 100644 (file)
@@ -205,6 +205,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                prune = 0;
                if (!need_to_gc())
                        return 0;
+               fprintf(stderr, "Packing your repository for optimum "
+                       "performance. You may also\n"
+                       "run \"git gc\" manually. See "
+                       "\"git help gc\" for more information.\n");
        } else {
                /*
                 * Use safer (for shared repos) "-A" option to
index d7cb11dc0d6339dbea51c89f3cd4966e8f6b4c3d..fb12248f825807b085f4e5ed761002c30925ead5 100644 (file)
@@ -288,7 +288,7 @@ static void cleanup_space(char *buf)
 }
 
 static void decode_header(char *it, unsigned itsize);
-static char *header[MAX_HDR_PARSED] = {
+static const char *header[MAX_HDR_PARSED] = {
        "From","Subject","Date",
 };
 
index 99d5c082a629c870e268160635c8e900155f164a..e1dc31e0eb80ddc2d1b82f12eda31401b9f40ebc 100644 (file)
@@ -169,7 +169,7 @@ static void prepend_reflog_action(const char *action, char *buf, size_t size)
 }
 
 enum reset_type { MIXED, SOFT, HARD, NONE };
-static char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
+static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
 
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
index 52cd2a46ba839c4d83af13d0ec0553be05e0eb6d..bf33f74b70a0514ebb3e3f3c31fbace65d26ff8a 100755 (executable)
@@ -399,6 +399,7 @@ class P4Submit(Command):
                 optparse.make_option("--dry-run", action="store_true"),
                 optparse.make_option("--direct", dest="directSubmit", action="store_true"),
                 optparse.make_option("--trust-me-like-a-fool", dest="trustMeLikeAFool", action="store_true"),
+                optparse.make_option("-M", dest="detectRename", action="store_true"),
         ]
         self.description = "Submit changes from git to the perforce depot."
         self.usage += " [name of git branch to submit into perforce depot]"
@@ -411,6 +412,7 @@ class P4Submit(Command):
         self.origin = ""
         self.directSubmit = False
         self.trustMeLikeAFool = False
+        self.detectRename = False
         self.verbose = False
         self.isWindows = (platform.system() == "Windows")
 
@@ -491,7 +493,8 @@ class P4Submit(Command):
             diff = self.diffStatus
         else:
             print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
-            diff = read_pipe_lines("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id))
+            diffOpts = ("", "-M")[self.detectRename]
+            diff = read_pipe_lines("git diff-tree -r --name-status %s \"%s^\" \"%s\"" % (diffOpts, id, id))
         filesToAdd = set()
         filesToDelete = set()
         editedFiles = set()
@@ -509,6 +512,13 @@ class P4Submit(Command):
                 filesToDelete.add(path)
                 if path in filesToAdd:
                     filesToAdd.remove(path)
+            elif modifier == "R":
+                src, dest = line.strip().split("\t")[1:3]
+                system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest))
+                system("p4 edit \"%s\"" % (dest))
+                os.unlink(dest)
+                editedFiles.add(dest)
+                filesToDelete.add(src)
             else:
                 die("unknown modifier %s for %s" % (modifier, path))
 
@@ -529,6 +539,10 @@ class P4Submit(Command):
                                      "and with .rej files / [w]rite the patch to a file (patch.txt) ")
             if response == "s":
                 print "Skipping! Good luck with the next patches..."
+                for f in editedFiles:
+                    system("p4 revert \"%s\"" % f);
+                for f in filesToAdd:
+                    system("rm %s" %f)
                 return
             elif response == "a":
                 os.system(applyPatchCmd)
index b188aa3d67eda77b168c61a53162a25abcc56e7d..2aa9bb501c2768770d8aed5de93dda8afc29b427 100644 (file)
@@ -331,7 +331,7 @@ generate_update_branch_email()
                echo "       via  $rev ($revtype)"
        done
 
-       if [ -z "$fastforward" ]; then
+       if [ "$fast_forward" ]; then
                echo "      from  $oldrev ($oldrev_type)"
        else
                #  1. Existing revisions were removed.  In this case newrev is a
index 9cf22fef417cee29e550a35fef1bac1050482afa..660e1552d46b8566f162677af02d91dc351c5c3f 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -9,6 +9,10 @@
 #define HOST_NAME_MAX 256
 #endif
 
+#ifndef NI_MAXSERV
+#define NI_MAXSERV 32
+#endif
+
 static int log_syslog;
 static int verbose;
 static int reuseaddr;
diff --git a/diff.c b/diff.c
index 6648e015213913e693b4450230a5a0896a21f7a0..dfb8595b7086c71b3be0ece408d65a7285f42e9f 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1512,6 +1512,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
 static int populate_from_stdin(struct diff_filespec *s)
 {
        struct strbuf buf;
+       size_t size = 0;
 
        strbuf_init(&buf, 0);
        if (strbuf_read(&buf, 0, 0) < 0)
@@ -1519,7 +1520,8 @@ static int populate_from_stdin(struct diff_filespec *s)
                                     strerror(errno));
 
        s->should_munmap = 0;
-       s->data = strbuf_detach(&buf, &s->size);
+       s->data = strbuf_detach(&buf, &size);
+       s->size = size;
        s->should_free = 1;
        return 0;
 }
@@ -1609,9 +1611,11 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
                 */
                strbuf_init(&buf, 0);
                if (convert_to_git(s->path, s->data, s->size, &buf)) {
+                       size_t size = 0;
                        munmap(s->data, s->size);
                        s->should_munmap = 0;
-                       s->data = strbuf_detach(&buf, &s->size);
+                       s->data = strbuf_detach(&buf, &size);
+                       s->size = size;
                        s->should_free = 1;
                }
        }
index ae8a7d03e2d103739897ac37fe1f9966956b9ec8..c71a22621a4f979f62cd21e9de83ab7129a129e9 100644 (file)
@@ -45,8 +45,8 @@ static int should_break(struct diff_filespec *src,
         * The value we return is 1 if we want the pair to be broken,
         * or 0 if we do not.
         */
-       unsigned long delta_size, base_size, src_copied, literal_added,
-               src_removed;
+       unsigned long delta_size, base_size, max_size;
+       unsigned long src_copied, literal_added, src_removed;
 
        *merge_score_p = 0; /* assume no deletion --- "do not break"
                             * is the default.
@@ -63,7 +63,8 @@ static int should_break(struct diff_filespec *src,
                return 0; /* error but caught downstream */
 
        base_size = ((src->size < dst->size) ? src->size : dst->size);
-       if (base_size < MINIMUM_BREAK_SIZE)
+       max_size = ((src->size > dst->size) ? src->size : dst->size);
+       if (max_size < MINIMUM_BREAK_SIZE)
                return 0; /* we do not break too small filepair */
 
        if (diffcore_count_changes(src, dst,
@@ -89,12 +90,14 @@ static int should_break(struct diff_filespec *src,
         * less than the minimum, after rename/copy runs.
         */
        *merge_score_p = (int)(src_removed * MAX_SCORE / src->size);
+       if (*merge_score_p > break_score)
+               return 1;
 
        /* Extent of damage, which counts both inserts and
         * deletes.
         */
        delta_size = src_removed + literal_added;
-       if (delta_size * MAX_SCORE / base_size < break_score)
+       if (delta_size * MAX_SCORE / max_size < break_score)
                return 0;
 
        /* If you removed a lot without adding new material, that is
index d9729e5ec27d2ff2bacf4ed930195f9293f8ca5a..e670f8512558c38d9a9d6e754cfc609b042b1195 100644 (file)
@@ -46,22 +46,6 @@ struct spanhash_top {
        struct spanhash data[FLEX_ARRAY];
 };
 
-static struct spanhash *spanhash_find(struct spanhash_top *top,
-                                     unsigned int hashval)
-{
-       int sz = 1 << top->alloc_log2;
-       int bucket = hashval & (sz - 1);
-       while (1) {
-               struct spanhash *h = &(top->data[bucket++]);
-               if (!h->cnt)
-                       return NULL;
-               if (h->hashval == hashval)
-                       return h;
-               if (sz <= bucket)
-                       bucket = 0;
-       }
-}
-
 static struct spanhash_top *spanhash_rehash(struct spanhash_top *orig)
 {
        struct spanhash_top *new;
@@ -122,6 +106,20 @@ static struct spanhash_top *add_spanhash(struct spanhash_top *top,
        }
 }
 
+static int spanhash_cmp(const void *a_, const void *b_)
+{
+       const struct spanhash *a = a_;
+       const struct spanhash *b = b_;
+
+       /* A count of zero compares at the end.. */
+       if (!a->cnt)
+               return !b->cnt ? 0 : 1;
+       if (!b->cnt)
+               return -1;
+       return a->hashval < b->hashval ? -1 :
+               a->hashval > b->hashval ? 1 : 0;
+}
+
 static struct spanhash_top *hash_chars(struct diff_filespec *one)
 {
        int i, n;
@@ -158,6 +156,10 @@ static struct spanhash_top *hash_chars(struct diff_filespec *one)
                n = 0;
                accum1 = accum2 = 0;
        }
+       qsort(hash->data,
+               1ul << hash->alloc_log2,
+               sizeof(hash->data[0]),
+               spanhash_cmp);
        return hash;
 }
 
@@ -169,7 +171,7 @@ int diffcore_count_changes(struct diff_filespec *src,
                           unsigned long *src_copied,
                           unsigned long *literal_added)
 {
-       int i, ssz;
+       struct spanhash *s, *d;
        struct spanhash_top *src_count, *dst_count;
        unsigned long sc, la;
 
@@ -190,22 +192,26 @@ int diffcore_count_changes(struct diff_filespec *src,
        }
        sc = la = 0;
 
-       ssz = 1 << src_count->alloc_log2;
-       for (i = 0; i < ssz; i++) {
-               struct spanhash *s = &(src_count->data[i]);
-               struct spanhash *d;
+       s = src_count->data;
+       d = dst_count->data;
+       for (;;) {
                unsigned dst_cnt, src_cnt;
                if (!s->cnt)
-                       continue;
+                       break; /* we checked all in src */
+               while (d->cnt) {
+                       if (d->hashval >= s->hashval)
+                               break;
+                       d++;
+               }
                src_cnt = s->cnt;
-               d = spanhash_find(dst_count, s->hashval);
-               dst_cnt = d ? d->cnt : 0;
+               dst_cnt = d->hashval == s->hashval ? d->cnt : 0;
                if (src_cnt < dst_cnt) {
                        la += dst_cnt - src_cnt;
                        sc += src_cnt;
                }
                else
                        sc += dst_cnt;
+               s++;
        }
 
        if (!src_count_p)
diff --git a/dir.c b/dir.c
index b18257e65ffaf440eb0944c42624e19052178374..4c17d3643eebf0d9071003065b5c120db130f9ab 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -443,6 +443,24 @@ static int in_pathspec(const char *path, int len, const struct path_simplify *si
        return 0;
 }
 
+static int get_dtype(struct dirent *de, const char *path)
+{
+       int dtype = DTYPE(de);
+       struct stat st;
+
+       if (dtype != DT_UNKNOWN)
+               return dtype;
+       if (lstat(path, &st))
+               return dtype;
+       if (S_ISREG(st.st_mode))
+               return DT_REG;
+       if (S_ISDIR(st.st_mode))
+               return DT_DIR;
+       if (S_ISLNK(st.st_mode))
+               return DT_LNK;
+       return dtype;
+}
+
 /*
  * Read a directory tree. We currently ignore anything but
  * directories, regular files and symlinks. That's because git
@@ -466,7 +484,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                exclude_stk = push_exclude_per_directory(dir, base, baselen);
 
                while ((de = readdir(fdir)) != NULL) {
-                       int len;
+                       int len, dtype;
                        int exclude;
 
                        if ((de->d_name[0] == '.') &&
@@ -486,24 +504,30 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                        if (exclude && dir->collect_ignored
                            && in_pathspec(fullname, baselen + len, simplify))
                                dir_add_ignored(dir, fullname, baselen + len);
-                       if (exclude != dir->show_ignored) {
-                               if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
+
+                       /*
+                        * Excluded? If we don't explicitly want to show
+                        * ignored files, ignore it
+                        */
+                       if (exclude && !dir->show_ignored)
+                               continue;
+
+                       dtype = get_dtype(de, fullname);
+
+                       /*
+                        * Do we want to see just the ignored files?
+                        * We still need to recurse into directories,
+                        * even if we don't ignore them, since the
+                        * directory may contain files that we do..
+                        */
+                       if (!exclude && dir->show_ignored) {
+                               if (dtype != DT_DIR)
                                        continue;
-                               }
                        }
 
-                       switch (DTYPE(de)) {
-                       struct stat st;
+                       switch (dtype) {
                        default:
                                continue;
-                       case DT_UNKNOWN:
-                               if (lstat(fullname, &st))
-                                       continue;
-                               if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
-                                       break;
-                               if (!S_ISDIR(st.st_mode))
-                                       continue;
-                               /* fallthrough */
                        case DT_DIR:
                                memcpy(fullname + baselen + len, "/", 2);
                                len++;
diff --git a/entry.c b/entry.c
index 98f5f6d4ecfc0dabae9a920bdf3beadbf20abcaf..cfadc6a292033d349f6b1efff75d2c4f9f2525fe 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -119,8 +119,10 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                 */
                strbuf_init(&buf, 0);
                if (convert_to_working_tree(ce->name, new, size, &buf)) {
+                       size_t newsize = 0;
                        free(new);
-                       new = strbuf_detach(&buf, &size);
+                       new = strbuf_detach(&buf, &newsize);
+                       size = newsize;
                }
 
                if (to_tempfile) {
index e9c80be4cd3f2833dd227e3d2f3c69e2145935e2..6f888f64767ee78ef198180c6e628ddb60e09191 100644 (file)
@@ -1817,7 +1817,7 @@ static void file_change_m(struct branch *b)
        } else if (oe) {
                if (oe->type != OBJ_BLOB)
                        die("Not a blob (actually a %s): %s",
-                               command_buf.buf, typename(oe->type));
+                               typename(oe->type), command_buf.buf);
        } else {
                enum object_type type = sha1_object_info(sha1, NULL);
                if (type < 0)
index b02ae6a76fa8265da1ea3d720df9dc29cc5d5519..2514d07de2ea89598499a35e3e4a2fcc9096bbec 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -394,9 +394,7 @@ do
                stop_here $this
        fi
 
-       echo
        printf 'Applying %s\n' "$SUBJECT"
-       echo
 
        case "$resolved" in
        '')
@@ -452,10 +450,8 @@ do
        fi
 
        tree=$(git write-tree) &&
-       echo Wrote tree $tree &&
        parent=$(git rev-parse --verify HEAD) &&
        commit=$(git commit-tree $tree -p $parent <"$dotest/final-commit") &&
-       echo Committed: $commit &&
        git update-ref -m "$GIT_REFLOG_ACTION: $SUBJECT" HEAD $commit $parent ||
        stop_here $this
 
index a33fa8d4c86da54a4d741cf921d1cf7b2138d186..f284c88a46b5cc7d6e75b78346829ce03e60b060 100755 (executable)
        @cvs = ('cvs');
 }
 
-# setup a tempdir
-our ($tmpdir, $tmpdirname) = tempdir('git-cvsapplycommit-XXXXXX',
-                                    TMPDIR => 1,
-                                    CLEANUP => 1);
-
 # resolve target commit
 my $commit;
 $commit = pop @ARGV;
 print "Checking if patch will apply\n";
 
 my @stat;
-open APPLY, "GIT_DIR= git-apply $context --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
+open APPLY, "GIT_DIR= git-apply $context --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
 @stat=<APPLY>;
 close APPLY || die "Cannot patch";
 my (@bfiles,@files,@afiles,@dfiles);
 }
 
 print "Applying\n";
-`GIT_DIR= git-apply $context --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
+`GIT_DIR= git-apply $context --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
 
 print "Patch applied successfully. Adding new files and directories to CVS\n";
 my $dirtypatch = 0;
+
+#
+# We have to add the directories in order otherwise we will have
+# problems when we try and add the sub-directory of a directory we
+# have not added yet.
+#
+# Luckily this is easy to deal with by sorting the directories and
+# dealing with the shortest ones first.
+#
+@dirs = sort { length $a <=> length $b} @dirs;
+
 foreach my $d (@dirs) {
     if (system(@cvs,'add',$d)) {
        $dirtypatch = 1;
index 13dbd27a80adfee43ed6282041b948bd92e5cc12..0d55fec04fa7e3e2a02987543a857a26ae1f96cb 100755 (executable)
     }
     my $request = $1;
     $line = <STDIN>; chomp $line;
-    req_Root('root', $line) # reuse Root
-       or die "E Invalid root $line \n";
+    unless (req_Root('root', $line)) { # reuse Root
+       print "E Invalid root $line \n";
+       exit 1;
+    }
     $line = <STDIN>; chomp $line;
     unless ($line eq 'anonymous') {
        print "E Only anonymous user allowed via pserver\n";
index a12f6c2d4c7ad3c4f69a783f7255b3010224d0c3..ffcc408ee5217d7a8f6179a0404ffe387e0dab0f 100755 (executable)
@@ -94,6 +94,10 @@ USAGE="[--env-filter <command>] [--tree-filter <command>] \
 
 . git-sh-setup
 
+git diff-files --quiet &&
+       git diff-index --cached --quiet HEAD ||
+       die "Cannot rewrite branch(es) with a dirty working directory."
+
 tempdir=.git-rewrite
 filter_env=
 filter_tree=
@@ -196,6 +200,9 @@ do
        esac
 done < "$tempdir"/backup-refs
 
+ORIG_GIT_DIR="$GIT_DIR"
+ORIG_GIT_WORK_TREE="$GIT_WORK_TREE"
+ORIG_GIT_INDEX_FILE="$GIT_INDEX_FILE"
 export GIT_DIR GIT_WORK_TREE=.
 
 # These refs should be updated if their heads were rewritten
@@ -413,4 +420,12 @@ echo
 test $count -gt 0 && echo "These refs were rewritten:"
 git show-ref | grep ^"$orig_namespace"
 
+unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
+test -z "$ORIG_GIT_DIR" || GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
+test -z "$ORIG_GIT_WORK_TREE" || GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
+       export GIT_WORK_TREE
+test -z "$ORIG_GIT_INDEX_FILE" || GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
+       export GIT_INDEX_FILE
+git read-tree -u -m HEAD
+
 exit $ret
index f789e91b66ae20b67103fb2d09758a9e99107b87..9335a9761b458f5e8d75abf342630672751c7f2a 100755 (executable)
@@ -305,7 +305,7 @@ proc _which {what} {
        global env _search_exe _search_path
 
        if {$_search_path eq {}} {
-               if {[is_Cygwin]} {
+               if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
                        set _search_path [split [exec cygpath \
                                --windows \
                                --path \
@@ -498,7 +498,11 @@ proc rmsel_tag {text} {
 set _git  [_which git]
 if {$_git eq {}} {
        catch {wm withdraw .}
-       error_popup "Cannot find git in PATH."
+       tk_messageBox \
+               -icon error \
+               -type ok \
+               -title [mc "git-gui: fatal error"] \
+               -message [mc "Cannot find git in PATH."]
        exit 1
 }
 
@@ -534,6 +538,7 @@ regsub -- {-dirty$} $_git_version {} _git_version
 regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
 regsub {\.rc[0-9]+$} $_git_version {} _git_version
 regsub {\.GIT$} $_git_version {} _git_version
+regsub {\.[a-zA-Z]+\.[0-9]+$} $_git_version {} _git_version
 
 if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
        catch {wm withdraw .}
@@ -903,6 +908,35 @@ proc rescan {after {honor_trustmtime 1}} {
        }
 }
 
+if {[is_Cygwin]} {
+       set is_git_info_link {}
+       set is_git_info_exclude {}
+       proc have_info_exclude {} {
+               global is_git_info_link is_git_info_exclude
+
+               if {$is_git_info_link eq {}} {
+                       set is_git_info_link [file isfile [gitdir info.lnk]]
+               }
+
+               if {$is_git_info_link} {
+                       if {$is_git_info_exclude eq {}} {
+                               if {[catch {exec test -f [gitdir info exclude]}]} {
+                                       set is_git_info_exclude 0
+                               } else {
+                                       set is_git_info_exclude 1
+                               }
+                       }
+                       return $is_git_info_exclude
+               } else {
+                       return [file readable [gitdir info exclude]]
+               }
+       }
+} else {
+       proc have_info_exclude {} {
+               return [file readable [gitdir info exclude]]
+       }
+}
+
 proc rescan_stage2 {fd after} {
        global rescan_active buf_rdi buf_rdf buf_rlo
 
@@ -913,9 +947,8 @@ proc rescan_stage2 {fd after} {
        }
 
        set ls_others [list --exclude-per-directory=.gitignore]
-       set info_exclude [gitdir info exclude]
-       if {[file readable $info_exclude]} {
-               lappend ls_others "--exclude-from=$info_exclude"
+       if {[have_info_exclude]} {
+               lappend ls_others "--exclude-from=[gitdir info exclude]"
        }
        set user_exclude [get_config core.excludesfile]
        if {$user_exclude ne {} && [file readable $user_exclude]} {
@@ -1093,11 +1126,17 @@ proc mapdesc {state path} {
 }
 
 proc ui_status {msg} {
-       $::main_status show $msg
+       global main_status
+       if {[info exists main_status]} {
+               $main_status show $msg
+       }
 }
 
 proc ui_ready {{test {}}} {
-       $::main_status show {Ready.} $test
+       global main_status
+       if {[info exists main_status]} {
+               $main_status show [mc "Ready."] $test
+       }
 }
 
 proc escape_path {path} {
@@ -1436,7 +1475,27 @@ proc do_gitk {revs} {
        if {! [file exists $exe]} {
                error_popup "Unable to start gitk:\n\n$exe does not exist"
        } else {
+               global env
+
+               if {[info exists env(GIT_DIR)]} {
+                       set old_GIT_DIR $env(GIT_DIR)
+               } else {
+                       set old_GIT_DIR {}
+               }
+
+               set pwd [pwd]
+               cd [file dirname [gitdir]]
+               set env(GIT_DIR) [file tail [gitdir]]
+
                eval exec $cmd $revs &
+
+               if {$old_GIT_DIR eq {}} {
+                       unset env(GIT_DIR)
+               } else {
+                       set env(GIT_DIR) $old_GIT_DIR
+               }
+               cd $pwd
+
                ui_status $::starting_gitk_msg
                after 10000 {
                        ui_ready $starting_gitk_msg
@@ -1648,7 +1707,7 @@ proc apply_config {} {
                set font [lindex $option 1]
                if {[catch {
                        foreach {cn cv} $repo_config(gui.$name) {
-                               font configure $font $cn $cv
+                               font configure $font $cn $cv -weight normal
                        }
                        } err]} {
                        error_popup "Invalid font specified in gui.$name:\n\n$err"
index f857a2ff5bb3d0e151ea471a16a1b233117afc70..57238129e420a313c34e2a99b9f9bdb87ecce39d 100644 (file)
@@ -253,7 +253,7 @@ proc commit_committree {fd_wt curHEAD msg} {
        global repo_config
 
        gets $fd_wt tree_id
-       if {$tree_id eq {} || [catch {close $fd_wt} err]} {
+       if {[catch {close $fd_wt} err]} {
                error_popup "write-tree failed:\n\n$err"
                ui_status {Commit failed.}
                unlock_index
index 6f718fbac3277daed7d0d4e10c76241490c4c216..b038a783581d4fcc92b030669b8a8fb3c09a623e 100644 (file)
@@ -122,7 +122,7 @@ method _read {fd after} {
                        } else {
                                $w.m.t delete $console_cr end
                                $w.m.t insert end "\n"
-                               $w.m.t insert end [string range $buf $c $cr]
+                               $w.m.t insert end [string range $buf $c [expr {$cr - 1}]]
                                set c $cr
                                incr c
                        }
index 72a8fe1fd36932efcb1e2f2e18e4200150436854..3bf79eb6e0608560d00aab82bc1d691db83881af 100644 (file)
@@ -69,7 +69,10 @@ method update_meter {buf} {
 
        set prior [string range $meter 0 $r]
        set meter [string range $meter [expr {$r + 1}] end]
-       if {[regexp "\\((\\d+)/(\\d+)\\)\\s+done\r\$" $prior _j a b]} {
+       set p "\\((\\d+)/(\\d+)\\)"
+       if {[regexp ":\\s*\\d+% $p\(?:, done.\\s*\n|\\s*\r)\$" $prior _j a b]} {
+               update $this $a $b
+       } elseif {[regexp "$p\\s+done\r\$" $prior _j a b]} {
                update $this $a $b
        }
 }
index 2e4eeccaceb434696071be9a8a2f6af24000d976..95c3e5aa1f9bd5f0cec215ee2f81b2d4c36ea929 100755 (executable)
@@ -30,8 +30,7 @@ test -z "$port" && port=1234
 
 start_httpd () {
        httpd_only="`echo $httpd | cut -f1 -d' '`"
-       if test "`expr index $httpd_only /`" -eq '1' || \
-                               which $httpd_only >/dev/null
+       if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac
        then
                $httpd $fqgitdir/gitweb/httpd.conf
        else
index df4cedb8594ef795933bb02447d9a96815e2d64b..0dd77b4005b9d80bfe6c606954d7666df77abc57 100755 (executable)
@@ -110,7 +110,7 @@ pick_one () {
        parent_sha1=$(git rev-parse --verify $sha1^) ||
                die "Could not get the parent of $sha1"
        current_sha1=$(git rev-parse --verify HEAD)
-       if test $no_ff$current_sha1 = $parent_sha1; then
+       if test "$no_ff$current_sha1" = "$parent_sha1"; then
                output git reset --hard $sha1
                test "a$1" = a-n && output git reset --soft $current_sha1
                sha1=$(git rev-parse --short $sha1)
index 62e1429733375356bdeb5054c3dca2581a5524fe..96051bc01e1585a69a3e0746bbef7016ec01663e 100755 (executable)
@@ -191,6 +191,7 @@ sub format_2822_time {
     "smtpserverport" => \$smtp_server_port,
     "smtpuser" => \$smtp_authuser,
     "smtppass" => \$smtp_authpass,
+    "to" => \@to,
     "cccmd" => \$cc_cmd,
     "aliasfiletype" => \$aliasfiletype,
     "bcc" => \@bcclist,
index 7ba61625baff8236f51d6c0babe9747c43664351..5bbda47b7b6e286e7e8e5d002d7ed461a831b579 100755 (executable)
@@ -139,7 +139,7 @@ apply_stash () {
        unstashed_index_tree=
        if test -n "$unstash_index" && test "$b_tree" != "$i_tree"
        then
-               git diff --binary $s^2^..$s^2 | git apply --cached
+               git diff-tree --binary $s^2^..$s^2 | git apply --cached
                test $? -ne 0 &&
                        die 'Conflicts in index. Try without --index.'
                unstashed_index_tree=$(git-write-tree) ||
@@ -162,7 +162,7 @@ apply_stash () {
                        git read-tree "$unstashed_index_tree"
                else
                        a="$TMP-added" &&
-                       git diff --cached --name-only --diff-filter=A $c_tree >"$a" &&
+                       git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
                        git read-tree --reset $c_tree &&
                        git update-index --add --stdin <"$a" ||
                                die "Cannot unstage modified files"
index 2c8a1580f8495b0f158802f8513a5df50aca3a02..22bb47b34d030ec4d1b3dc7cda05465559e0a09d 100755 (executable)
@@ -9,6 +9,11 @@
 $AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
 $VERSION = '@@GIT_VERSION@@';
 
+# From which subdir have we been invoked?
+my $cmd_dir_prefix = eval {
+       command_oneline([qw/rev-parse --show-prefix/], STDERR => 0)
+} || '';
+
 my $git_dir_user_set = 1 if defined $ENV{GIT_DIR};
 $ENV{GIT_DIR} ||= '.git';
 $Git::SVN::default_repo_id = 'svn';
 $ENV{TZ} = 'UTC';
 $| = 1; # unbuffer STDOUT
 
-sub fatal (@) { print STDERR @_; exit 1 }
+sub fatal (@) { print STDERR "@_\n"; exit 1 }
 require SVN::Core; # use()-ing this causes segfaults for me... *shrug*
 require SVN::Ra;
 require SVN::Delta;
 if ($SVN::Core::VERSION lt '1.1.0') {
-       fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)\n";
+       fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)";
 }
 push @Git::SVN::Ra::ISA, 'SVN::Ra';
 push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
@@ -123,6 +128,16 @@ BEGIN
        'set-tree' => [ \&cmd_set_tree,
                        "Set an SVN repository to a git tree-ish",
                        { 'stdin|' => \$_stdin, %cmt_opts, %fc_opts, } ],
+       'create-ignore' => [ \&cmd_create_ignore,
+                            'Create a .gitignore per svn:ignore',
+                            { 'revision|r=i' => \$_revision
+                            } ],
+        'propget' => [ \&cmd_propget,
+                      'Print the value of a property on a file or directory',
+                      { 'revision|r=i' => \$_revision } ],
+        'proplist' => [ \&cmd_proplist,
+                      'List all properties of a file or directory',
+                      { 'revision|r=i' => \$_revision } ],
        'show-ignore' => [ \&cmd_show_ignore, "Show svn:ignore listings",
                        { 'revision|r=i' => \$_revision
                        } ],
@@ -357,7 +372,7 @@ sub cmd_set_tree {
                } elsif (scalar @tmp > 1) {
                        push @revs, reverse(command('rev-list',@tmp));
                } else {
-                       fatal "Failed to rev-parse $c\n";
+                       fatal "Failed to rev-parse $c";
                }
        }
        my $gs = Git::SVN->new;
@@ -367,7 +382,7 @@ sub cmd_set_tree {
                fatal "There are new revisions that were fetched ",
                      "and need to be merged (or acknowledged) ",
                      "before committing.\nlast rev: $r_last\n",
-                     " current: $gs->{last_rev}\n";
+                     " current: $gs->{last_rev}";
        }
        $gs->set_tree($_) foreach @revs;
        print "Done committing ",scalar @revs," revisions to SVN\n";
@@ -396,7 +411,7 @@ sub cmd_dcommit {
                        (undef, $last_rev, undef) = cmt_metadata("$d~1");
                        unless (defined $last_rev) {
                                fatal "Unable to extract revision information ",
-                                     "from commit $d~1\n";
+                                     "from commit $d~1";
                        }
                }
                if ($_dry_run) {
@@ -488,7 +503,100 @@ sub cmd_show_ignore {
        my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
        $gs ||= Git::SVN->new;
        my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
-       $gs->traverse_ignore(\*STDOUT, $gs->{path}, $r);
+       $gs->prop_walk($gs->{path}, $r, sub {
+               my ($gs, $path, $props) = @_;
+               print STDOUT "\n# $path\n";
+               my $s = $props->{'svn:ignore'} or return;
+               $s =~ s/[\r\n]+/\n/g;
+               chomp $s;
+               $s =~ s#^#$path#gm;
+               print STDOUT "$s\n";
+       });
+}
+
+sub cmd_create_ignore {
+       my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+       $gs ||= Git::SVN->new;
+       my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
+       $gs->prop_walk($gs->{path}, $r, sub {
+               my ($gs, $path, $props) = @_;
+               # $path is of the form /path/to/dir/
+               my $ignore = '.' . $path . '.gitignore';
+               my $s = $props->{'svn:ignore'} or return;
+               open(GITIGNORE, '>', $ignore)
+                 or fatal("Failed to open `$ignore' for writing: $!");
+               $s =~ s/[\r\n]+/\n/g;
+               chomp $s;
+               # Prefix all patterns so that the ignore doesn't apply
+               # to sub-directories.
+               $s =~ s#^#/#gm;
+               print GITIGNORE "$s\n";
+               close(GITIGNORE)
+                 or fatal("Failed to close `$ignore': $!");
+               command_noisy('add', $ignore);
+       });
+}
+
+# get_svnprops(PATH)
+# ------------------
+# Helper for cmd_propget and cmd_proplist below.
+sub get_svnprops {
+       my $path = shift;
+       my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
+       $gs ||= Git::SVN->new;
+
+       # prefix THE PATH by the sub-directory from which the user
+       # invoked us.
+       $path = $cmd_dir_prefix . $path;
+       fatal("No such file or directory: $path") unless -e $path;
+       my $is_dir = -d $path ? 1 : 0;
+       $path = $gs->{path} . '/' . $path;
+
+       # canonicalize the path (otherwise libsvn will abort or fail to
+       # find the file)
+       # File::Spec->canonpath doesn't collapse x/../y into y (for a
+       # good reason), so let's do this manually.
+       $path =~ s#/+#/#g;
+       $path =~ s#/\.(?:/|$)#/#g;
+       $path =~ s#/[^/]+/\.\.##g;
+       $path =~ s#/$##g;
+
+       my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
+       my $props;
+       if ($is_dir) {
+               (undef, undef, $props) = $gs->ra->get_dir($path, $r);
+       }
+       else {
+               (undef, $props) = $gs->ra->get_file($path, $r, undef);
+       }
+       return $props;
+}
+
+# cmd_propget (PROP, PATH)
+# ------------------------
+# Print the SVN property PROP for PATH.
+sub cmd_propget {
+       my ($prop, $path) = @_;
+       $path = '.' if not defined $path;
+       usage(1) if not defined $prop;
+       my $props = get_svnprops($path);
+       if (not defined $props->{$prop}) {
+               fatal("`$path' does not have a `$prop' SVN property.");
+       }
+       print $props->{$prop} . "\n";
+}
+
+# cmd_proplist (PATH)
+# -------------------
+# Print the list of SVN properties for PATH.
+sub cmd_proplist {
+       my $path = shift;
+       $path = '.' if not defined $path;
+       my $props = get_svnprops($path);
+       print "Properties on '$path':\n";
+       foreach (sort keys %{$props}) {
+               print "  $_\n";
+       }
 }
 
 sub cmd_multi_init {
@@ -537,7 +645,7 @@ sub cmd_multi_fetch {
 sub cmd_commit_diff {
        my ($ta, $tb, $url) = @_;
        my $usage = "Usage: $0 commit-diff -r<revision> ".
-                   "<tree-ish> <tree-ish> [<URL>]\n";
+                   "<tree-ish> <tree-ish> [<URL>]";
        fatal($usage) if (!defined $ta || !defined $tb);
        my $svn_path;
        if (!defined $url) {
@@ -555,7 +663,7 @@ sub cmd_commit_diff {
        if (defined $_message && defined $_file) {
                fatal("Both --message/-m and --file/-F specified ",
                      "for the commit message.\n",
-                     "I have no idea what you mean\n");
+                     "I have no idea what you mean");
        }
        if (defined $_file) {
                $_message = file_to_s($_file);
@@ -618,7 +726,7 @@ sub complete_svn_url {
        if ($path !~ m#^[a-z\+]+://#) {
                if (!defined $url || $url !~ m#^[a-z\+]+://#) {
                        fatal("E: '$path' is not a complete URL ",
-                             "and a separate URL is not specified\n");
+                             "and a separate URL is not specified");
                }
                return ($url, $path);
        }
@@ -639,7 +747,7 @@ sub complete_url_ls_init {
                $repo_path =~ s#^/+##;
                unless ($ra) {
                        fatal("E: '$repo_path' is not a complete URL ",
-                             "and a separate URL is not specified\n");
+                             "and a separate URL is not specified");
                }
        }
        my $url = $ra->{url};
@@ -1480,28 +1588,45 @@ sub rel_path {
        $url;
 }
 
-sub traverse_ignore {
-       my ($self, $fh, $path, $r) = @_;
-       $path =~ s#^/+##g;
-       my $ra = $self->ra;
-       my ($dirent, undef, $props) = $ra->get_dir($path, $r);
+# prop_walk(PATH, REV, SUB)
+# -------------------------
+# Recursively traverse PATH at revision REV and invoke SUB for each
+# directory that contains a SVN property.  SUB will be invoked as
+# follows:  &SUB(gs, path, props);  where `gs' is this instance of
+# Git::SVN, `path' the path to the directory where the properties
+# `props' were found.  The `path' will be relative to point of checkout,
+# that is, if url://repo/trunk is the current Git branch, and that
+# directory contains a sub-directory `d', SUB will be invoked with `/d/'
+# as `path' (note the trailing `/').
+sub prop_walk {
+       my ($self, $path, $rev, $sub) = @_;
+
+       my ($dirent, undef, $props) = $self->ra->get_dir($path, $rev);
+       $path =~ s#^/*#/#g;
        my $p = $path;
-       $p =~ s#^\Q$self->{path}\E(/|$)##;
-       print $fh length $p ? "\n# $p\n" : "\n# /\n";
-       if (my $s = $props->{'svn:ignore'}) {
-               $s =~ s/[\r\n]+/\n/g;
-               chomp $s;
-               if (length $p == 0) {
-                       $s =~ s#\n#\n/$p#g;
-                       print $fh "/$s\n";
-               } else {
-                       $s =~ s#\n#\n/$p/#g;
-                       print $fh "/$p/$s\n";
-               }
-       }
+       # Strip the irrelevant part of the path.
+       $p =~ s#^/+\Q$self->{path}\E(/|$)#/#;
+       # Ensure the path is terminated by a `/'.
+       $p =~ s#/*$#/#;
+
+       # The properties contain all the internal SVN stuff nobody
+       # (usually) cares about.
+       my $interesting_props = 0;
+       foreach (keys %{$props}) {
+               # If it doesn't start with `svn:', it must be a
+               # user-defined property.
+               ++$interesting_props and next if $_ !~ /^svn:/;
+               # FIXME: Fragile, if SVN adds new public properties,
+               # this needs to be updated.
+               ++$interesting_props if /^svn:(?:ignore|keywords|executable
+                                                |eol-style|mime-type
+                                                |externals|needs-lock)$/x;
+       }
+       &$sub($self, $p, $props) if $interesting_props;
+
        foreach (sort keys %$dirent) {
                next if $dirent->{$_}->{kind} != $SVN::Node::dir;
-               $self->traverse_ignore($fh, "$path/$_", $r);
+               $self->prop_walk($path . '/' . $_, $rev, $sub);
        }
 }
 
@@ -1628,7 +1753,7 @@ sub assert_index_clean {
                $x = command_oneline('write-tree');
                if ($y ne $x) {
                        ::fatal "trees ($treeish) $y != $x\n",
-                               "Something is seriously wrong...\n";
+                               "Something is seriously wrong...";
                }
        });
 }
@@ -2054,7 +2179,7 @@ sub set_tree {
        my ($self, $tree) = (shift, shift);
        my $log_entry = ::get_commit_entry($tree);
        unless ($self->{last_rev}) {
-               fatal("Must have an existing revision to commit\n");
+               fatal("Must have an existing revision to commit");
        }
        my %ed_opts = ( r => $self->{last_rev},
                        log => $log_entry->{log},
@@ -2303,23 +2428,31 @@ sub ssl_server_trust {
        my ($cred, $realm, $failures, $cert_info, $may_save, $pool) = @_;
        $may_save = undef if $_no_auth_cache;
        print STDERR "Error validating server certificate for '$realm':\n";
-       if ($failures & $SVN::Auth::SSL::UNKNOWNCA) {
-               print STDERR " - The certificate is not issued by a trusted ",
-                     "authority. Use the\n",
-                     "   fingerprint to validate the certificate manually!\n";
-       }
-       if ($failures & $SVN::Auth::SSL::CNMISMATCH) {
-               print STDERR " - The certificate hostname does not match.\n";
-       }
-       if ($failures & $SVN::Auth::SSL::NOTYETVALID) {
-               print STDERR " - The certificate is not yet valid.\n";
-       }
-       if ($failures & $SVN::Auth::SSL::EXPIRED) {
-               print STDERR " - The certificate has expired.\n";
-       }
-       if ($failures & $SVN::Auth::SSL::OTHER) {
-               print STDERR " - The certificate has an unknown error.\n";
-       }
+       {
+               no warnings 'once';
+               # All variables SVN::Auth::SSL::* are used only once,
+               # so we're shutting up Perl warnings about this.
+               if ($failures & $SVN::Auth::SSL::UNKNOWNCA) {
+                       print STDERR " - The certificate is not issued ",
+                           "by a trusted authority. Use the\n",
+                           "   fingerprint to validate ",
+                           "the certificate manually!\n";
+               }
+               if ($failures & $SVN::Auth::SSL::CNMISMATCH) {
+                       print STDERR " - The certificate hostname ",
+                           "does not match.\n";
+               }
+               if ($failures & $SVN::Auth::SSL::NOTYETVALID) {
+                       print STDERR " - The certificate is not yet valid.\n";
+               }
+               if ($failures & $SVN::Auth::SSL::EXPIRED) {
+                       print STDERR " - The certificate has expired.\n";
+               }
+               if ($failures & $SVN::Auth::SSL::OTHER) {
+                       print STDERR " - The certificate has ",
+                           "an unknown error.\n";
+               }
+       } # no warnings 'once'
        printf STDERR
                "Certificate information:\n".
                " - Hostname: %s\n".
@@ -2403,20 +2536,6 @@ sub _read_password {
        $password;
 }
 
-package main;
-
-{
-       my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
-                               $SVN::Node::dir.$SVN::Node::unknown.
-                               $SVN::Node::none.$SVN::Node::file.
-                               $SVN::Node::dir.$SVN::Node::unknown.
-                               $SVN::Auth::SSL::CNMISMATCH.
-                               $SVN::Auth::SSL::NOTYETVALID.
-                               $SVN::Auth::SSL::EXPIRED.
-                               $SVN::Auth::SSL::UNKNOWNCA.
-                               $SVN::Auth::SSL::OTHER;
-}
-
 package SVN::Git::Fetcher;
 use vars qw/@ISA/;
 use strict;
@@ -2833,16 +2952,21 @@ sub open_or_add_dir {
        if (!defined $t) {
                die "$full_path not known in r$self->{r} or we have a bug!\n";
        }
-       if ($t == $SVN::Node::none) {
-               return $self->add_directory($full_path, $baton,
-                                               undef, -1, $self->{pool});
-       } elsif ($t == $SVN::Node::dir) {
-               return $self->open_directory($full_path, $baton,
-                                               $self->{r}, $self->{pool});
-       }
-       print STDERR "$full_path already exists in repository at ",
-               "r$self->{r} and it is not a directory (",
-               ($t == $SVN::Node::file ? 'file' : 'unknown'),"/$t)\n";
+       {
+               no warnings 'once';
+               # SVN::Node::none and SVN::Node::file are used only once,
+               # so we're shutting up Perl's warnings about them.
+               if ($t == $SVN::Node::none) {
+                       return $self->add_directory($full_path, $baton,
+                           undef, -1, $self->{pool});
+               } elsif ($t == $SVN::Node::dir) {
+                       return $self->open_directory($full_path, $baton,
+                           $self->{r}, $self->{pool});
+               } # no warnings 'once'
+               print STDERR "$full_path already exists in repository at ",
+                   "r$self->{r} and it is not a directory (",
+                   ($t == $SVN::Node::file ? 'file' : 'unknown'),"/$t)\n";
+       } # no warnings 'once'
        exit 1;
 }
 
@@ -3004,7 +3128,7 @@ sub apply_diff {
                if (defined $o{$f}) {
                        $self->$f($m);
                } else {
-                       fatal("Invalid change type: $f\n");
+                       fatal("Invalid change type: $f");
                }
        }
        $self->rmdirs if $_rmdir;
@@ -3068,11 +3192,11 @@ sub new {
        my $dont_store_passwords = 1;
        my $conf_t = ${$config}{'config'};
        {
+               no warnings 'once';
                # The usage of $SVN::_Core::SVN_CONFIG_* variables
                # produces warnings that variables are used only once.
                # I had not found the better way to shut them up, so
-               # warnings are disabled in this block.
-               no warnings;
+               # the warnings of type 'once' are disabled in this block.
                if (SVN::_Core::svn_config_get_bool($conf_t,
                    $SVN::_Core::SVN_CONFIG_SECTION_AUTH,
                    $SVN::_Core::SVN_CONFIG_OPTION_STORE_PASSWORDS,
@@ -3087,7 +3211,7 @@ sub new {
                    1) == 0) {
                        $Git::SVN::Prompt::_no_auth_cache = 1;
                }
-       }
+       } # no warnings 'once'
        my $self = SVN::Ra->new(url => $url, auth => $baton,
                              config => $config,
                              pool => SVN::Pool->new,
@@ -3635,15 +3759,15 @@ sub config_pager {
 sub run_pager {
        return unless -t *STDOUT && defined $pager;
        pipe my $rfd, my $wfd or return;
-       defined(my $pid = fork) or ::fatal "Can't fork: $!\n";
+       defined(my $pid = fork) or ::fatal "Can't fork: $!";
        if (!$pid) {
                open STDOUT, '>&', $wfd or
-                                    ::fatal "Can't redirect to stdout: $!\n";
+                                    ::fatal "Can't redirect to stdout: $!";
                return;
        }
-       open STDIN, '<&', $rfd or ::fatal "Can't redirect stdin: $!\n";
+       open STDIN, '<&', $rfd or ::fatal "Can't redirect stdin: $!";
        $ENV{LESS} ||= 'FRSX';
-       exec $pager or ::fatal "Can't run pager: $! ($pager)\n";
+       exec $pager or ::fatal "Can't run pager: $! ($pager)";
 }
 
 sub tz_to_s_offset {
@@ -3779,7 +3903,7 @@ sub cmd_show_log {
                        $r_min = $r_max = $::_revision;
                } else {
                        ::fatal "-r$::_revision is not supported, use ",
-                               "standard \'git log\' arguments instead\n";
+                               "standard 'git log' arguments instead";
                }
        }
 
diff --git a/git.c b/git.c
index d7c6bcaed6596a9c9c9bccf4c2d14d076b1201b3..23a430c3690ed1f921ec22196edf1f0062bc6dcd 100644 (file)
--- a/git.c
+++ b/git.c
@@ -414,13 +414,14 @@ int main(int argc, const char **argv)
        /*
         * Take the basename of argv[0] as the command
         * name, and the dirname as the default exec_path
-        * if it's an absolute path and we don't have
-        * anything better.
+        * if we don't have anything better.
         */
        if (slash) {
                *slash++ = 0;
                if (*cmd == '/')
                        exec_path = cmd;
+               else
+                       exec_path = xstrdup(make_absolute_path(cmd));
                cmd = slash;
        }
 
diff --git a/gitk b/gitk
index 300fdceb350ca6d0419ef3b0bf3f1e7b82700a59..41a1c69e19c52cc07afaf4c14fc6ef8a6178e541 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -92,7 +92,7 @@ proc start_rev_list {view} {
        set order "--date-order"
     }
     if {[catch {
-       set fd [open [concat | git log -z --pretty=raw $order --parents \
+       set fd [open [concat | git log --no-color -z --pretty=raw $order --parents \
                         --boundary $viewargs($view) "--" $viewfiles($view)] r]
     } err]} {
        error_popup "Error executing git rev-list: $err"
@@ -843,6 +843,12 @@ proc makewindow {} {
     } else {
        bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
        bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
+        if {[tk windowingsystem] eq "aqua"} {
+            bindall <MouseWheel> {
+                set delta [expr {- (%D)}]
+                allcanvs yview scroll $delta units
+            }
+        }
     }
     bindall <2> "canvscan mark %W %x %y"
     bindall <B2-Motion> "canvscan dragto %W %x %y"
@@ -3689,34 +3695,23 @@ proc drawcommits {row {endrow {}}} {
        drawcmitrow $r
        if {$r == $er} break
        set nextid [lindex $displayorder [expr {$r + 1}]]
-       if {$wasdrawn && [info exists iddrawn($nextid)]} {
-           catch {unset prevlines}
-           continue
-       }
+       if {$wasdrawn && [info exists iddrawn($nextid)]} continue
        drawparentlinks $id $r
 
-       if {[info exists lineends($r)]} {
-           foreach lid $lineends($r) {
-               unset prevlines($lid)
-           }
-       }
        set rowids [lindex $rowidlist $r]
        foreach lid $rowids {
            if {$lid eq {}} continue
+           if {[info exists lineend($lid)] && $lineend($lid) > $r} continue
            if {$lid eq $id} {
                # see if this is the first child of any of its parents
                foreach p [lindex $parentlist $r] {
                    if {[lsearch -exact $rowids $p] < 0} {
                        # make this line extend up to the child
-                       set le [drawlineseg $p $r $er 0]
-                       lappend lineends($le) $p
-                       set prevlines($p) 1
+                       set lineend($p) [drawlineseg $p $r $er 0]
                    }
                }
-           } elseif {![info exists prevlines($lid)]} {
-               set le [drawlineseg $lid $r $er 1]
-               lappend lineends($le) $lid
-               set prevlines($lid) 1
+           } else {
+               set lineend($lid) [drawlineseg $lid $r $er 1]
            }
        }
     }
@@ -5215,8 +5210,7 @@ proc getblobdiffline {bdf ids} {
            set diffinhdr 0
 
        } elseif {$diffinhdr} {
-           if {![string compare -length 12 "rename from " $line] ||
-               ![string compare -length 10 "copy from " $line]} {
+           if {![string compare -length 12 "rename from " $line]} {
                set fname [string range $line [expr 6 + [string first " from " $line] ] end]
                if {[string index $fname 0] eq "\""} {
                    set fname [lindex $fname 0]
@@ -6643,7 +6637,7 @@ proc addnewchild {id p} {
     global arcnos arcids arctags arcout arcend arcstart archeads growing
     global seeds allcommits
 
-    if {![info exists allcommits]} return
+    if {![info exists allcommits] || ![info exists arcnos($p)]} return
     lappend allids $id
     set allparents($id) [list $p]
     set allchildren($id) {}
@@ -7833,6 +7827,13 @@ proc tcl_encoding {enc} {
     return {}
 }
 
+# First check that Tcl/Tk is recent enough
+if {[catch {package require Tk 8.4} err]} {
+    show_error {} . "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
+                    Gitk requires at least Tcl/Tk 8.4."
+    exit 1
+}
+
 # defaults...
 set datemode 0
 set diffopts "-U 5 -p"
index 3064298f28837ced72897b6bc6401b3061882926..48e21dad6cec580cd874e32e981a86dc909893f7 100755 (executable)
@@ -35,6 +35,10 @@ BEGIN
 #our $projectroot = "/pub/scm";
 our $projectroot = "++GITWEB_PROJECTROOT++";
 
+# fs traversing limit for getting project list
+# the number is relative to the projectroot
+our $project_maxdepth = "++GITWEB_PROJECT_MAXDEPTH++";
+
 # target of the home link on top of all pages
 our $home_link = $my_uri || "/";
 
@@ -1509,6 +1513,7 @@ sub git_get_projects_list {
                # remove the trailing "/"
                $dir =~ s!/+$!!;
                my $pfxlen = length("$dir");
+               my $pfxdepth = ($dir =~ tr!/!!);
 
                File::Find::find({
                        follow_fast => 1, # follow symbolic links
@@ -1519,6 +1524,11 @@ sub git_get_projects_list {
                                return if (m!^[/.]$!);
                                # only directories can be git repositories
                                return unless (-d $_);
+                               # don't traverse too deep (Find is super slow on os x)
+                               if (($File::Find::name =~ tr!/!!) - $pfxdepth > $project_maxdepth) {
+                                       $File::Find::prune = 1;
+                                       return;
+                               }
 
                                my $subdir = substr($File::Find::name, $pfxlen + 1);
                                # we check related file in $projectroot
index d7e29c4d1d3e44c85e0eeb28040e8ea945090594..0fd6df7d6ed839eaed536bc332312c2688a6bbad 100644 (file)
@@ -132,7 +132,7 @@ static void match_trees(const unsigned char *hash1,
                        const unsigned char *hash2,
                        int *best_score,
                        char **best_match,
-                       char *base,
+                       const char *base,
                        int recurse_limit)
 {
        struct tree_desc one;
index 56202d13dfcfe4ed583fee7b4063596bc1ef417e..056b322fb0c83aeda378f548e13f84d4a65c1e29 100644 (file)
@@ -149,6 +149,8 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
                else if (ce_compare_gitlink(ce))
                        changed |= DATA_CHANGED;
                return changed;
+       case 0: /* Special case: unmerged file in index */
+               return MODE_CHANGED | DATA_CHANGED | TYPE_CHANGED;
        default:
                die("internal error: ce_mode is %o", ntohl(ce->ce_mode));
        }
index 61e9929763133e9dbfb17b9c3097b2149b4fade0..38e35c06b9e73376adde597c4fe28490a2e886b1 100644 (file)
@@ -166,7 +166,7 @@ static const char *update(struct command *cmd)
        struct ref_lock *lock;
 
        if (!prefixcmp(name, "refs/") && check_ref_format(name + 5)) {
-               error("refusing to create funny ref '%s' locally", name);
+               error("refusing to create funny ref '%s' remotely", name);
                return "funny refname";
        }
 
index b20e2be4332b3b4a17c50988f2acdcd80834a6f7..170015aabfc18d028b22a5200cbddc5a5ec3f042 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -762,6 +762,8 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
                        hashcpy(dst_peer->new_sha1, src->new_sha1);
                }
                dst_peer->peer_ref = src;
+               if (pat)
+                       dst_peer->force = pat->force;
        free_name:
                free(dst_name);
        }
index 7b776243e666014521c62276923b3b17fc739fe2..e9b9a39f411b6cfff1c0a4bc3f7e31274c8d2782 100644 (file)
@@ -205,7 +205,8 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha
                return -1;
 
        if (!remote_refs) {
-               fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
+               fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
+                       "Perhaps you should specify a branch such as 'master'.\n");
                return 0;
        }
 
diff --git a/setup.c b/setup.c
index 06004f15874fbcbab50eafd272c5898a34fcdcfe..145eca50f41d811c4c8fcb21ed2604e6b2971aba 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -227,9 +227,20 @@ const char *setup_git_directory_gently(int *nongit_ok)
                if (PATH_MAX - 40 < strlen(gitdirenv))
                        die("'$%s' too big", GIT_DIR_ENVIRONMENT);
                if (is_git_directory(gitdirenv)) {
+                       static char buffer[1024 + 1];
+                       const char *retval;
+
                        if (!work_tree_env)
                                return set_work_tree(gitdirenv);
-                       return NULL;
+                       retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
+                                       get_git_work_tree());
+                       if (!retval || !*retval)
+                               return NULL;
+                       set_git_dir(make_absolute_path(gitdirenv));
+                       if (chdir(work_tree_env) < 0)
+                               die ("Could not chdir to %s", work_tree_env);
+                       strcat(buffer, "/");
+                       return retval;
                }
                if (nongit_ok) {
                        *nongit_ok = 1;
index 57ed9e87b7fca6c899d4c23d709a97dabce28106..7253991fff9f6240ee6413986dfc66cfa3ff184e 100644 (file)
@@ -68,7 +68,7 @@ int main(int argc, char **argv)
                                                     ntohl(off64[1]);
                                off64_nr++;
                        }
-                       printf("%llu %s (%08x)\n", (unsigned long long) offset,
+                       printf("%" PRIuMAX " %s (%08x)\n", (uintmax_t) offset,
                               sha1_to_hex(entries[i].sha1),
                               ntohl(entries[i].crc));
                }
index 0807d9f01a178567e433c9c057064d026109ade2..62bc4bb077d06920509228b9fcd6464472e96dc2 100755 (executable)
@@ -371,4 +371,11 @@ test_expect_success 'in-tree .gitattributes (4)' '
        }
 '
 
+test_expect_success 'invalid .gitattributes (must not crash)' '
+
+       echo "three +crlf" >>.gitattributes &&
+       git diff
+
+'
+
 test_done
index 732216184ffc255a385aac81ba970edfc6a9e83a..7ee3820ce97b6c26c9465685a7bc64a962aad3cb 100755 (executable)
@@ -103,4 +103,13 @@ test_expect_success 'repo finds its work tree from work tree, too' '
         test sub/dir/tracked = "$(git ls-files)")
 '
 
+test_expect_success '_gently() groks relative GIT_DIR & GIT_WORK_TREE' '
+       cd repo.git/work/sub/dir &&
+       GIT_DIR=../../.. GIT_WORK_TREE=../.. GIT_PAGER= \
+               git diff --exit-code tracked &&
+       echo changed > tracked &&
+       ! GIT_DIR=../../.. GIT_WORK_TREE=../.. GIT_PAGER= \
+               git diff --exit-code tracked
+'
+
 test_done
index 57c6397be116bb93af2ccffb0cee2d4bb3901ca4..2d0c07fd6a38d786efc895bc5c5c0d7dd268b31f 100755 (executable)
@@ -123,4 +123,52 @@ test_expect_success \
        git-branch -a >branches && ! grep -q origin/master branches
 '
 
+rewound_push_setup() {
+       rm -rf parent child &&
+       mkdir parent && cd parent &&
+       git-init && echo one >file && git-add file && git-commit -m one &&
+       echo two >file && git-commit -a -m two &&
+       cd .. &&
+       git-clone parent child && cd child && git-reset --hard HEAD^
+}
+
+rewound_push_succeeded() {
+       cmp ../parent/.git/refs/heads/master .git/refs/heads/master
+}
+
+rewound_push_failed() {
+       if rewound_push_succeeded
+       then
+               false
+       else
+               true
+       fi
+}
+
+test_expect_success \
+       'pushing explicit refspecs respects forcing' '
+       rewound_push_setup &&
+       if git-send-pack ../parent/.git refs/heads/master:refs/heads/master
+       then
+               false
+       else
+               true
+       fi && rewound_push_failed &&
+       git-send-pack ../parent/.git +refs/heads/master:refs/heads/master &&
+       rewound_push_succeeded
+'
+
+test_expect_success \
+       'pushing wildcard refspecs respects forcing' '
+       rewound_push_setup &&
+       if git-send-pack ../parent/.git refs/heads/*:refs/heads/*
+       then
+               false
+       else
+               true
+       fi && rewound_push_failed &&
+       git-send-pack ../parent/.git +refs/heads/*:refs/heads/* &&
+       rewound_push_succeeded
+'
+
 test_done
index e935b2000ac6589a4940f585616f8bf3be95223b..2089351f7d157ce96a07ed1c6c1465fc58819ec0 100755 (executable)
@@ -41,7 +41,9 @@ test_expect_success 'rewrite, renaming a specific file' '
 '
 
 test_expect_success 'test that the file was renamed' '
-       test d = $(git show HEAD:doh)
+       test d = $(git show HEAD:doh) &&
+       test -f doh &&
+       test d = $(cat doh)
 '
 
 git tag oldD HEAD~4
diff --git a/t/t8004-blame.sh b/t/t8004-blame.sh
new file mode 100755 (executable)
index 0000000..ba19ac1
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+# Based on a test case submitted by Björn Steinbrink.
+
+test_description='git blame on conflicted files'
+. ./test-lib.sh
+
+test_expect_success 'setup first case' '
+       # Create the old file
+       echo "Old line" > file1 &&
+       git add file1 &&
+       git commit --author "Old Line <ol@localhost>" -m file1.a &&
+
+       # Branch
+       git checkout -b foo &&
+
+       # Do an ugly move and change
+       git rm file1 &&
+       echo "New line ..."  > file2 &&
+       echo "... and more" >> file2 &&
+       git add file2 &&
+       git commit --author "U Gly <ug@localhost>" -m ugly &&
+
+       # Back to master and change something
+       git checkout master &&
+       echo "
+
+bla" >> file1 &&
+       git commit --author "Old Line <ol@localhost>" -a -m file1.b &&
+
+       # Back to foo and merge master
+       git checkout foo &&
+       if git merge master; then
+               echo needed conflict here
+               exit 1
+       else
+               echo merge failed - resolving automatically
+       fi &&
+       echo "New line ...
+... and more
+
+bla
+Even more" > file2 &&
+       git rm file1 &&
+       git commit --author "M Result <mr@localhost>" -a -m merged &&
+
+       # Back to master and change file1 again
+       git checkout master &&
+       sed s/bla/foo/ <file1 >X &&
+       rm file1 &&
+       mv X file1 &&
+       git commit --author "No Bla <nb@localhost>" -a -m replace &&
+
+       # Try to merge into foo again
+       git checkout foo &&
+       if git merge master; then
+               echo needed conflict here
+               exit 1
+       else
+               echo merge failed - test is setup
+       fi
+'
+
+test_expect_success \
+       'blame runs on unconflicted file while other file has conflicts' '
+       git blame file2
+'
+
+test_expect_success 'blame runs on conflicted file in stages 1,3' '
+       git blame file1
+'
+
+test_done
index 5aac644223cf217255a0fdbbb1238b4125fd2e6e..3c83127a0e862dd89837755103a97ed967c80e0b 100755 (executable)
@@ -126,19 +126,20 @@ cat > show-ignore.expect <<\EOF
 # /
 /no-such-file*
 
-# deeply
+# /deeply/
 /deeply/no-such-file*
 
-# deeply/nested
+# /deeply/nested/
 /deeply/nested/no-such-file*
 
-# deeply/nested/directory
+# /deeply/nested/directory/
 /deeply/nested/directory/no-such-file*
 EOF
 
 test_expect_success 'test show-ignore' "
        cd test_wc &&
        mkdir -p deeply/nested/directory &&
+       touch deeply/nested/directory/.keep &&
        svn add deeply &&
        svn up &&
        svn propset -R svn:ignore 'no-such-file*' .
@@ -148,4 +149,69 @@ test_expect_success 'test show-ignore' "
        cmp show-ignore.expect show-ignore.got
        "
 
+cat >create-ignore.expect <<\EOF
+/no-such-file*
+EOF
+
+cat >create-ignore-index.expect <<\EOF
+100644 8c52e5dfcd0a8b6b6bcfe6b41b89bcbf493718a5 0      .gitignore
+100644 8c52e5dfcd0a8b6b6bcfe6b41b89bcbf493718a5 0      deeply/.gitignore
+100644 8c52e5dfcd0a8b6b6bcfe6b41b89bcbf493718a5 0      deeply/nested/.gitignore
+100644 8c52e5dfcd0a8b6b6bcfe6b41b89bcbf493718a5 0      deeply/nested/directory/.gitignore
+EOF
+
+test_expect_success 'test create-ignore' "
+       git-svn fetch && git pull . remotes/git-svn &&
+       git-svn create-ignore &&
+       cmp ./.gitignore create-ignore.expect &&
+       cmp ./deeply/.gitignore create-ignore.expect &&
+       cmp ./deeply/nested/.gitignore create-ignore.expect &&
+       cmp ./deeply/nested/directory/.gitignore create-ignore.expect &&
+       git ls-files -s | grep gitignore | cmp - create-ignore-index.expect
+       "
+
+cat >prop.expect <<\EOF
+no-such-file*
+
+EOF
+cat >prop2.expect <<\EOF
+8
+EOF
+
+# This test can be improved: since all the svn:ignore contain the same
+# pattern, it can pass even though the propget did not execute on the
+# right directory.
+test_expect_success 'test propget' "
+       git-svn propget svn:ignore . | cmp - prop.expect &&
+       cd deeply &&
+       git-svn propget svn:ignore . | cmp - ../prop.expect &&
+       git-svn propget svn:entry:committed-rev nested/directory/.keep \
+         | cmp - ../prop2.expect &&
+       git-svn propget svn:ignore .. | cmp - ../prop.expect &&
+       git-svn propget svn:ignore nested/ | cmp - ../prop.expect &&
+       git-svn propget svn:ignore ./nested | cmp - ../prop.expect &&
+       git-svn propget svn:ignore .././deeply/nested | cmp - ../prop.expect
+       "
+
+cat >prop.expect <<\EOF
+Properties on '.':
+  svn:entry:committed-date
+  svn:entry:committed-rev
+  svn:entry:last-author
+  svn:entry:uuid
+  svn:ignore
+EOF
+cat >prop2.expect <<\EOF
+Properties on 'nested/directory/.keep':
+  svn:entry:committed-date
+  svn:entry:committed-rev
+  svn:entry:last-author
+  svn:entry:uuid
+EOF
+
+test_expect_success 'test proplist' "
+       git-svn proplist . | cmp - prop.expect &&
+       git-svn proplist nested/directory/.keep | cmp - prop2.expect
+       "
+
 test_done
index 642b836d64f2260aa0f21618e5af7f2a00cf97ec..f7bad5bb2f20cf274eb30f8ceed34a3f83000989 100755 (executable)
@@ -18,6 +18,7 @@ gitweb_init () {
 our \$version = "current";
 our \$GIT = "git";
 our \$projectroot = "$(pwd)";
+our \$project_maxdepth = 8;
 our \$home_link_str = "projects";
 our \$site_name = "[localhost]";
 our \$site_header = "";
index 26bdbdd2bfea5eab99b9f1c38936efe56f1ab45a..7c261fd7c3b38d2c2dbd722617c628d98848cbc1 100644 (file)
@@ -319,6 +319,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
        diff_opts.detect_rename = DIFF_DETECT_RENAME;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_opts.single_follow = opt->paths[0];
+       diff_opts.break_opt = opt->break_opt;
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
        if (diff_setup_done(&diff_opts) < 0)