Merge branch 'master'
authorJunio C Hamano <junkio@cox.net>
Sun, 12 Feb 2006 13:03:40 +0000 (05:03 -0800)
committerJunio C Hamano <junkio@cox.net>
Sun, 12 Feb 2006 13:03:40 +0000 (05:03 -0800)
* master:
  Add howto about separating topics.
  Merge branch 'pb/repo'
  Add support for explicit type specifiers when calling git-repo-config
  Merge branch 'jc/fixdiff'
  diff-tree: do not default to -c
  Avoid using "git-var -l" until it gets fixed.
  t5500: adjust to change in pack-object reporting behaviour.
  Only call git-rerere if $GIT_DIR/rr-cache exists.
  Use a relative path for SVN importing
  fetch-clone progress: finishing touches.
  Fix fetch-clone in the presense of signals
  Make "git clone" pack-fetching download statistics better
  Make "git clone" less of a deathly quiet experience

17 files changed:
Documentation/git-repo-config.txt
Documentation/howto/separating-topic-branches.txt [new file with mode: 0644]
cache.h
clone-pack.c
diff-tree.c
fetch-clone.c
fetch-pack.c
git-am.sh
git-commit.sh
git-merge.sh
git-pull.sh
git-sh-setup.sh
git-svnimport.perl
git-whatchanged.sh
pack-objects.c
repo-config.c
t/t5500-fetch-pack.sh

index 3069464..33fcde4 100644 (file)
@@ -8,12 +8,12 @@ git-repo-config - Get and set options in .git/config.
 
 SYNOPSIS
 --------
-'git-repo-config' name [value [value_regex]]
-'git-repo-config' --replace-all name [value [value_regex]]
-'git-repo-config' --get name [value_regex]
-'git-repo-config' --get-all name [value_regex]
-'git-repo-config' --unset name [value_regex]
-'git-repo-config' --unset-all name [value_regex]
+'git-repo-config' [type] name [value [value_regex]]
+'git-repo-config' [type] --replace-all name [value [value_regex]]
+'git-repo-config' [type] --get name [value_regex]
+'git-repo-config' [type] --get-all name [value_regex]
+'git-repo-config' [type] --unset name [value_regex]
+'git-repo-config' [type] --unset-all name [value_regex]
 
 DESCRIPTION
 -----------
@@ -26,6 +26,12 @@ should provide a POSIX regex for the value. If you want to handle the lines
 *not* matching the regex, just prepend a single exclamation mark in front
 (see EXAMPLES).
 
+The type specifier can be either '--int' or '--bool', which will make
+'git-repo-config' ensure that the variable(s) are of the given type and
+convert the value to the canonical form (simple decimal number for int,
+a "true" or "false" string for bool). If no type specifier is passed,
+no checks or transformations are performed on the value.
+
 This command will fail if
 
 . .git/config is invalid,
diff --git a/Documentation/howto/separating-topic-branches.txt b/Documentation/howto/separating-topic-branches.txt
new file mode 100644 (file)
index 0000000..090e2c9
--- /dev/null
@@ -0,0 +1,91 @@
+From: Junio C Hamano <junkio@cox.net>
+Subject: Separating topic branches
+Abstract: In this article, JC describes how to separate topic branches.
+
+This text was originally a footnote to a discussion about the
+behaviour of the git diff commands.
+
+Often I find myself doing that [running diff against something other
+than HEAD] while rewriting messy development history.  For example, I
+start doing some work without knowing exactly where it leads, and end
+up with a history like this:
+
+            "master"
+        o---o
+             \                    "topic" 
+              o---o---o---o---o---o
+
+At this point, "topic" contains something I know I want, but it
+contains two concepts that turned out to be completely independent.
+And often, one topic component is larger than the other.  It may
+contain more than two topics.
+
+In order to rewrite this mess to be more manageable, I would first do
+"diff master..topic", to extract the changes into a single patch, start
+picking pieces from it to get logically self-contained units, and
+start building on top of "master":
+
+        $ git diff master..topic >P.diff
+        $ git checkout -b topicA master
+        ... pick and apply pieces from P.diff to build
+        ... commits on topicA branch.
+                      
+              o---o---o
+             /        "topicA"
+        o---o"master"
+             \                    "topic" 
+              o---o---o---o---o---o
+
+Before doing each commit on "topicA" HEAD, I run "diff HEAD"
+before update-index the affected paths, or "diff --cached HEAD"
+after.  Also I would run "diff --cached master" to make sure
+that the changes are only the ones related to "topicA".  Usually
+I do this for smaller topics first.
+
+After that, I'd do the remainder of the original "topic", but
+for that, I do not start from the patchfile I extracted by
+comparing "master" and "topic" I used initially.  Still on
+"topicA", I extract "diff topic", and use it to rebuild the
+other topic:
+
+        $ git diff -R topic >P.diff ;# --cached also would work fine
+        $ git checkout -b topicB master
+        ... pick and apply pieces from P.diff to build
+        ... commits on topicB branch.
+
+                                "topicB"
+               o---o---o---o---o
+              /
+             /o---o---o
+            |/        "topicA"
+        o---o"master"
+             \                    "topic" 
+              o---o---o---o---o---o
+
+After I am done, I'd try a pretend-merge between "topicA" and
+"topicB" in order to make sure I have not missed anything:
+
+        $ git pull . topicA ;# merge it into current "topicB"
+        $ git diff topic
+                                "topicB"
+               o---o---o---o---o---* (pretend merge)
+              /                   /
+             /o---o---o----------'
+            |/        "topicA"
+        o---o"master"
+             \                    "topic" 
+              o---o---o---o---o---o
+
+The last diff better not to show anything other than cleanups
+for crufts.  Then I can finally clean things up:
+
+        $ git branch -D topic
+        $ git reset --hard HEAD^ ;# nuke pretend merge
+
+                                "topicB"
+               o---o---o---o---o
+              / 
+             /o---o---o
+            |/        "topicA"
+        o---o"master"
+
diff --git a/cache.h b/cache.h
index cd58fad..d7f5bde 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -350,6 +350,6 @@ extern int copy_fd(int ifd, int ofd);
 
 /* Finish off pack transfer receiving end */
 extern int receive_unpack_pack(int fd[2], const char *me, int quiet);
-extern int receive_keep_pack(int fd[2], const char *me);
+extern int receive_keep_pack(int fd[2], const char *me, int quiet);
 
 #endif /* CACHE_H */
index f634431..a4370f5 100644 (file)
@@ -6,6 +6,8 @@ static const char clone_pack_usage[] =
 "git-clone-pack [--exec=<git-upload-pack>] [<host>:]<directory> [<heads>]*";
 static const char *exec = "git-upload-pack";
 
+static int quiet = 0;
+
 static void clone_handshake(int fd[2], struct ref *ref)
 {
        unsigned char sha1[20];
@@ -123,7 +125,9 @@ static int clone_pack(int fd[2], int nr_match, char **match)
        }
        clone_handshake(fd, refs);
 
-       status = receive_keep_pack(fd, "git-clone-pack");
+       status = receive_keep_pack(fd, "git-clone-pack", quiet);
+       if (!quiet)
+               fprintf(stderr, "\n");
 
        if (!status) {
                if (nr_match == 0)
@@ -154,8 +158,10 @@ int main(int argc, char **argv)
                char *arg = argv[i];
 
                if (*arg == '-') {
-                       if (!strcmp("-q", arg))
+                       if (!strcmp("-q", arg)) {
+                               quiet = 1;
                                continue;
+                       }
                        if (!strncmp("--exec=", arg, 7)) {
                                exec = arg + 7;
                                continue;
index b170b03..f55a35a 100644 (file)
@@ -6,7 +6,7 @@ static int show_root_diff = 0;
 static int no_commit_id = 0;
 static int verbose_header = 0;
 static int ignore_merges = 1;
-static int combine_merges = 1;
+static int combine_merges = 0;
 static int dense_combined_merges = 0;
 static int read_stdin = 0;
 static int always_show_header = 0;
@@ -248,7 +248,7 @@ int main(int argc, const char **argv)
                        continue;
                }
                if (!strcmp(arg, "-m")) {
-                       combine_merges = ignore_merges = 0;
+                       ignore_merges = 0;
                        continue;
                }
                if (!strcmp(arg, "-c")) {
index 859f400..da1b3ff 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "exec_cmd.h"
 #include <sys/wait.h>
+#include <sys/time.h>
 
 static int finish_pack(const char *pack_tmp_name, const char *me)
 {
@@ -129,10 +130,35 @@ int receive_unpack_pack(int fd[2], const char *me, int quiet)
        die("git-unpack-objects died of unnatural causes %d", status);
 }
 
-int receive_keep_pack(int fd[2], const char *me)
+/*
+ * We average out the download speed over this many "events", where
+ * an event is a minimum of about half a second. That way, we get
+ * a reasonably stable number.
+ */
+#define NR_AVERAGE (4)
+
+/*
+ * A "binary msec" is a power-of-two-msec, aka 1/1024th of a second.
+ * Keeing the time in that format means that "bytes / msecs" means
+ * is the same as kB/s (modulo rounding).
+ *
+ * 1000512 is a magic number (usecs in a second, rounded up by half
+ * of 1024, to make "rounding" come out right ;)
+ */
+#define usec_to_binarymsec(x) ((int)(x) / (1000512 >> 10))
+
+int receive_keep_pack(int fd[2], const char *me, int quiet)
 {
        char tmpfile[PATH_MAX];
        int ofd, ifd;
+       unsigned long total;
+       static struct timeval prev_tv;
+       struct average {
+               unsigned long bytes;
+               unsigned long time;
+       } download[NR_AVERAGE] = { {0, 0}, };
+       unsigned long avg_bytes, avg_time;
+       int idx = 0;
 
        ifd = fd[0];
        snprintf(tmpfile, sizeof(tmpfile),
@@ -141,6 +167,10 @@ int receive_keep_pack(int fd[2], const char *me)
        if (ofd < 0)
                return error("unable to create temporary file %s", tmpfile);
 
+       gettimeofday(&prev_tv, NULL);
+       total = 0;
+       avg_bytes = 0;
+       avg_time = 0;
        while (1) {
                char buf[8192];
                ssize_t sz, wsz, pos;
@@ -148,10 +178,13 @@ int receive_keep_pack(int fd[2], const char *me)
                if (sz == 0)
                        break;
                if (sz < 0) {
-                       error("error reading pack (%s)", strerror(errno));
-                       close(ofd);
-                       unlink(tmpfile);
-                       return -1;
+                       if (errno != EINTR && errno != EAGAIN) {
+                               error("error reading pack (%s)", strerror(errno));
+                               close(ofd);
+                               unlink(tmpfile);
+                               return -1;
+                       }
+                       sz = 0;
                }
                pos = 0;
                while (pos < sz) {
@@ -165,6 +198,40 @@ int receive_keep_pack(int fd[2], const char *me)
                        }
                        pos += wsz;
                }
+               total += sz;
+               if (!quiet) {
+                       static unsigned long last;
+                       struct timeval tv;
+                       unsigned long diff = total - last;
+                       /* not really "msecs", but a power-of-two millisec (1/1024th of a sec) */
+                       unsigned long msecs;
+
+                       gettimeofday(&tv, NULL);
+                       msecs = tv.tv_sec - prev_tv.tv_sec;
+                       msecs <<= 10;
+                       msecs += usec_to_binarymsec(tv.tv_usec - prev_tv.tv_usec);
+
+                       if (msecs > 500) {
+                               prev_tv = tv;
+                               last = total;
+
+                               /* Update averages ..*/
+                               avg_bytes += diff;
+                               avg_time += msecs;
+                               avg_bytes -= download[idx].bytes;
+                               avg_time -= download[idx].time;
+                               download[idx].bytes = diff;
+                               download[idx].time = msecs;
+                               idx++;
+                               if (idx >= NR_AVERAGE)
+                                       idx = 0;
+
+                               fprintf(stderr, "%4lu.%03luMB  (%lu kB/s)      \r",
+                                       total >> 20,
+                                       1000*((total >> 10) & 1023)>>10,
+                                       avg_bytes / avg_time );
+                       }
+               }
        }
        close(ofd);
        return finish_pack(tmpfile, me);
index 27f5d2a..aa6f42a 100644 (file)
@@ -378,7 +378,7 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
                fprintf(stderr, "warning: no common commits\n");
 
        if (keep_pack)
-               status = receive_keep_pack(fd, "git-fetch-pack");
+               status = receive_keep_pack(fd, "git-fetch-pack", quiet);
        else
                status = receive_unpack_pack(fd, "git-fetch-pack", quiet);
 
index ee6886f..98b9215 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -88,7 +88,10 @@ fall_back_3way () {
     # saying that we reverted all those changes.
 
     git-merge-resolve $orig_tree -- HEAD $his_tree || {
-           git-rerere
+           if test -d "$GIT_DIR/rr-cache"
+           then
+               git-rerere
+           fi
            echo Failed to merge in the changes.
            exit 1
     }
index 073ec81..59551d9 100755 (executable)
@@ -638,7 +638,10 @@ else
 fi
 ret="$?"
 rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG"
-git-rerere
+if test -d "$GIT_DIR/rr-cache"
+then
+       git-rerere
+fi
 
 if test -x "$GIT_DIR"/hooks/post-commit && test "$ret" = 0
 then
index dc17baf..74f0761 100755 (executable)
@@ -309,6 +309,9 @@ Conflicts:
                sed -e 's/^[^   ]*      /       /' |
                uniq
        } >>"$GIT_DIR/MERGE_MSG"
-       git rerere
+       if test -d "$GIT_DIR/rr-cache"
+       then
+               git-rerere
+       fi
        die "Automatic merge failed; fix up by hand"
 fi
index 0991d5f..6caf1aa 100755 (executable)
@@ -70,21 +70,21 @@ case "$merge_head" in
        exit 0
        ;;
 ?*' '?*)
-       var=`git-var -l | sed -ne 's/^pull\.octopus=/-s /p'`
+       var=`git repo-config --get pull.octopus`
        if test '' = "$var"
        then
                strategy_default_args='-s octopus'
        else
-               strategy_default_args=$var
+               strategy_default_args="-s $var"
        fi
        ;;
 *)
-       var=`git-var -l | sed -ne 's/^pull\.twohead=/-s /p'`
+       var=`git repo-config --get pull.twohead`
        if test '' = "$var"
        then
                strategy_default_args='-s recursive'
        else
-               strategy_default_args=$var
+               strategy_default_args="-s $var"
        fi
        ;;
 esac
index 1e638e4..157c7e4 100755 (executable)
@@ -41,7 +41,11 @@ then
        : ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
 
        # Make sure we are in a valid repository of a vintage we understand.
-       GIT_DIR="$GIT_DIR" git-var GIT_AUTHOR_IDENT >/dev/null || exit
+       GIT_DIR="$GIT_DIR" git repo-config --get core.nosuch >/dev/null
+       if test $? == 128
+       then
+           exit
+       fi
 else
        GIT_DIR=$(git-rev-parse --git-dir) || exit
 fi
index b6799d8..f17d5a2 100755 (executable)
@@ -318,7 +318,7 @@ sub get_file($$$) {
                        die $res->status_line." at $url\n";
                }
        } else {
-               $name = $svn->file("/$svnpath",$rev);
+               $name = $svn->file("$svnpath",$rev);
                return undef unless defined $name;
        }
 
index 574fc35..1fb9feb 100755 (executable)
@@ -10,7 +10,7 @@ case "$0" in
        count=
        test -z "$diff_tree_flags" &&
                diff_tree_flags=$(git-repo-config --get whatchanged.difftree)
-       diff_tree_default_flags='-M --abbrev' ;;
+       diff_tree_default_flags='-c -M --abbrev' ;;
 *show)
        count=-n1
        test -z "$diff_tree_flags" &&
index c3f2531..2135e9a 100644 (file)
@@ -3,6 +3,7 @@
 #include "delta.h"
 #include "pack.h"
 #include "csum-file.h"
+#include <sys/time.h>
 
 static const char pack_usage[] = "git-pack-objects [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
 
@@ -26,6 +27,7 @@ static struct object_entry *objects = NULL;
 static int nr_objects = 0, nr_alloc = 0;
 static const char *base_name;
 static unsigned char pack_file_sha1[20];
+static int progress = 0;
 
 static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
 {
@@ -362,10 +364,13 @@ static void find_deltas(struct object_entry **list, int window, int depth)
        int i, idx;
        unsigned int array_size = window * sizeof(struct unpacked);
        struct unpacked *array = xmalloc(array_size);
+       int eye_candy;
 
        memset(array, 0, array_size);
        i = nr_objects;
        idx = 0;
+       eye_candy = i - (nr_objects / 20);
+
        while (--i >= 0) {
                struct object_entry *entry = list[i];
                struct unpacked *n = array + idx;
@@ -373,6 +378,10 @@ static void find_deltas(struct object_entry **list, int window, int depth)
                char type[10];
                int j;
 
+               if (progress && i <= eye_candy) {
+                       eye_candy -= nr_objects / 20;
+                       fputc('.', stderr);
+               }
                free(n->data);
                n->entry = entry;
                n->data = read_sha1_file(entry->sha1, type, &size);
@@ -404,11 +413,13 @@ static void prepare_pack(int window, int depth)
 {
        get_object_details();
 
-       fprintf(stderr, "Packing %d objects\n", nr_objects);
-
+       if (progress)
+               fprintf(stderr, "Packing %d objects", nr_objects);
        sorted_by_type = create_sorted_list(type_size_sort);
        if (window && depth)
                find_deltas(sorted_by_type, window+1, depth);
+       if (progress)
+               fputc('\n', stderr);
        write_pack_file();
 }
 
@@ -472,6 +483,10 @@ int main(int argc, char **argv)
        int window = 10, depth = 10, pack_to_stdout = 0;
        struct object_entry **list;
        int i;
+       struct timeval prev_tv;
+       int eye_candy = 0;
+       int eye_candy_incr = 500;
+
 
        setup_git_directory();
 
@@ -519,12 +534,34 @@ int main(int argc, char **argv)
        if (pack_to_stdout != !base_name)
                usage(pack_usage);
 
+       progress = isatty(2);
+
        prepare_packed_git();
+       if (progress) {
+               fprintf(stderr, "Generating pack...\n");
+               gettimeofday(&prev_tv, NULL);
+       }
        while (fgets(line, sizeof(line), stdin) != NULL) {
                unsigned int hash;
                char *p;
                unsigned char sha1[20];
 
+               if (progress && (eye_candy <= nr_objects)) {
+                       fprintf(stderr, "Counting objects...%d\r", nr_objects);
+                       if (eye_candy && (50 <= eye_candy_incr)) {
+                               struct timeval tv;
+                               int time_diff;
+                               gettimeofday(&tv, NULL);
+                               time_diff = (tv.tv_sec - prev_tv.tv_sec);
+                               time_diff <<= 10;
+                               time_diff += (tv.tv_usec - prev_tv.tv_usec);
+                               if ((1 << 9) < time_diff)
+                                       eye_candy_incr += 50;
+                               else if (50 < eye_candy_incr)
+                                       eye_candy_incr -= 50;
+                       }
+                       eye_candy += eye_candy_incr;
+               }
                if (get_sha1_hex(line, sha1))
                        die("expected sha1, got garbage:\n %s", line);
                hash = 0;
@@ -537,6 +574,8 @@ int main(int argc, char **argv)
                }
                add_object_entry(sha1, hash);
        }
+       if (progress)
+               fprintf(stderr, "Done counting %d objects.\n", nr_objects);
        if (non_empty && !nr_objects)
                return 0;
 
index c31e441..9cf6519 100644 (file)
@@ -2,7 +2,7 @@
 #include <regex.h>
 
 static const char git_config_set_usage[] =
-"git-repo-config [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]";
+"git-repo-config [ --bool | --int ] [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]";
 
 static char* key = NULL;
 static char* value = NULL;
@@ -10,6 +10,7 @@ static regex_t* regexp = NULL;
 static int do_all = 0;
 static int do_not_match = 0;
 static int seen = 0;
+static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
 
 static int show_config(const char* key_, const char* value_)
 {
@@ -25,7 +26,17 @@ static int show_config(const char* key_, const char* value_)
                        fprintf(stderr, "More than one value: %s\n", value);
                        free(value);
                }
-               value = strdup(value_);
+
+               if (type == T_INT) {
+                       value = malloc(256);
+                       sprintf(value, "%d", git_config_int(key_, value_));
+               } else if (type == T_BOOL) {
+                       value = malloc(256);
+                       sprintf(value, "%s", git_config_bool(key_, value_)
+                                            ? "true" : "false");
+               } else {
+                       value = strdup(value_ ? value_ : "");
+               }
                seen++;
        }
        return 0;
@@ -73,6 +84,18 @@ static int get_value(const char* key_, const char* regex_)
 int main(int argc, const char **argv)
 {
        setup_git_directory();
+
+       while (1 < argc) {
+               if (!strcmp(argv[1], "--int"))
+                       type = T_INT;
+               else if (!strcmp(argv[1], "--bool"))
+                       type = T_BOOL;
+               else
+                       break;
+               argc--;
+               argv++;
+       }
+
        switch (argc) {
        case 2:
                return get_value(argv[1], NULL);
index 0781bd2..e15e14f 100755 (executable)
@@ -76,7 +76,7 @@ function pull_to_client () {
        git-symbolic-ref HEAD refs/heads/${heads:0:1}
        test_expect_success "fsck" 'git-fsck-objects --full > fsck.txt 2>&1'
        test_expect_object_count "after $number pull" $count
-       pack_count=$(grep Packing log.txt|tr -dc "0-9")
+       pack_count=$(grep Unpacking log.txt|tr -dc "0-9")
        test -z "$pack_count" && pack_count=0
        if [ -z "$no_strict_count_check" ]; then
                test_expect_success "minimal count" "test $count = $pack_count"