diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt index 306946412f..33fcde452a 100644 --- a/Documentation/git-repo-config.txt +++ b/Documentation/git-repo-config.txt @@ -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 index 0000000000..090e2c9b01 --- /dev/null +++ b/Documentation/howto/separating-topic-branches.txt @@ -0,0 +1,91 @@ +From: Junio C Hamano +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 cd58fada15..d7f5bdef07 100644 --- 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 */ diff --git a/clone-pack.c b/clone-pack.c index f634431be1..a4370f595f 100644 --- a/clone-pack.c +++ b/clone-pack.c @@ -6,6 +6,8 @@ static const char clone_pack_usage[] = "git-clone-pack [--exec=] [:] []*"; 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; diff --git a/diff-tree.c b/diff-tree.c index b170b03fd3..f55a35a9d5 100644 --- a/diff-tree.c +++ b/diff-tree.c @@ -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")) { diff --git a/fetch-clone.c b/fetch-clone.c index 859f400941..da1b3ffbaa 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -1,6 +1,7 @@ #include "cache.h" #include "exec_cmd.h" #include +#include 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); diff --git a/fetch-pack.c b/fetch-pack.c index 27f5d2a5ff..aa6f42ae1b 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -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); diff --git a/git-am.sh b/git-am.sh index ee6886f300..98b9215f70 100755 --- 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 } diff --git a/git-commit.sh b/git-commit.sh index 073ec81e14..59551d99f9 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -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 diff --git a/git-merge.sh b/git-merge.sh index dc17baf6e0..74f07610fa 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -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 diff --git a/git-pull.sh b/git-pull.sh index 0991d5f14c..6caf1aad47 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -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 diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 1e638e493d..157c7e4d6c 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -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 diff --git a/git-svnimport.perl b/git-svnimport.perl index b6799d81ee..f17d5a27c8 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -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; } diff --git a/git-whatchanged.sh b/git-whatchanged.sh index 574fc3558e..1fb9feb348 100755 --- a/git-whatchanged.sh +++ b/git-whatchanged.sh @@ -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" && diff --git a/pack-objects.c b/pack-objects.c index c3f25317bb..2135e9a92e 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -3,6 +3,7 @@ #include "delta.h" #include "pack.h" #include "csum-file.h" +#include 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; diff --git a/repo-config.c b/repo-config.c index c31e441a3d..9cf65193f9 100644 --- a/repo-config.c +++ b/repo-config.c @@ -2,7 +2,7 @@ #include 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); diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 0781bd287e..e15e14fc32 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -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"