Merge branch 'jm/free'
authorJunio C Hamano <gitster@pobox.com>
Wed, 27 Feb 2008 21:03:50 +0000 (13:03 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 27 Feb 2008 21:03:50 +0000 (13:03 -0800)
* jm/free:
  Avoid unnecessary "if-before-free" tests.

Conflicts:

builtin-branch.c

1  2 
builtin-branch.c
builtin-fast-export.c
builtin-pack-objects.c
diff.c
pretty.c
remote.c

diff --combined builtin-branch.c
@@@ -12,7 -12,6 +12,7 @@@
  #include "builtin.h"
  #include "remote.h"
  #include "parse-options.h"
 +#include "branch.h"
  
  static const char * const builtin_branch_usage[] = {
        "git-branch [options] [-r | -a]",
@@@ -30,6 -29,8 +30,6 @@@
  static const char *head;
  static unsigned char head_sha1[20];
  
 -static int branch_track = 1;
 -
  static int branch_use_color = -1;
  static char branch_colors[][COLOR_MAXLEN] = {
        "\033[m",       /* reset */
@@@ -74,6 -75,10 +74,6 @@@ static int git_branch_config(const cha
                color_parse(value, var, branch_colors[slot]);
                return 0;
        }
 -      if (!strcmp(var, "branch.autosetupmerge")) {
 -              branch_track = git_config_bool(var, value);
 -              return 0;
 -      }
        return git_color_default_config(var, value);
  }
  
@@@ -121,8 -126,7 +121,7 @@@ static int delete_branches(int argc, co
                        continue;
                }
  
-               if (name)
-                       free(name);
+               free(name);
  
                name = xstrdup(mkpath(fmt, argv[i]));
                if (!resolve_ref(name, sha1, 1, NULL)) {
                }
        }
  
-       if (name)
-               free(name);
+       free(name);
  
        return(ret);
  }
@@@ -354,6 -357,140 +352,6 @@@ static void print_ref_list(int kinds, i
        free_ref_list(&ref_list);
  }
  
 -struct tracking {
 -      struct refspec spec;
 -      char *src;
 -      const char *remote;
 -      int matches;
 -};
 -
 -static int find_tracked_branch(struct remote *remote, void *priv)
 -{
 -      struct tracking *tracking = priv;
 -
 -      if (!remote_find_tracking(remote, &tracking->spec)) {
 -              if (++tracking->matches == 1) {
 -                      tracking->src = tracking->spec.src;
 -                      tracking->remote = remote->name;
 -              } else {
 -                      free(tracking->spec.src);
 -                      if (tracking->src) {
 -                              free(tracking->src);
 -                              tracking->src = NULL;
 -                      }
 -              }
 -              tracking->spec.src = NULL;
 -      }
 -
 -      return 0;
 -}
 -
 -
 -/*
 - * This is called when new_ref is branched off of orig_ref, and tries
 - * to infer the settings for branch.<new_ref>.{remote,merge} from the
 - * config.
 - */
 -static int setup_tracking(const char *new_ref, const char *orig_ref)
 -{
 -      char key[1024];
 -      struct tracking tracking;
 -
 -      if (strlen(new_ref) > 1024 - 7 - 7 - 1)
 -              return error("Tracking not set up: name too long: %s",
 -                              new_ref);
 -
 -      memset(&tracking, 0, sizeof(tracking));
 -      tracking.spec.dst = (char *)orig_ref;
 -      if (for_each_remote(find_tracked_branch, &tracking) ||
 -                      !tracking.matches)
 -              return 1;
 -
 -      if (tracking.matches > 1)
 -              return error("Not tracking: ambiguous information for ref %s",
 -                              orig_ref);
 -
 -      if (tracking.matches == 1) {
 -              sprintf(key, "branch.%s.remote", new_ref);
 -              git_config_set(key, tracking.remote ?  tracking.remote : ".");
 -              sprintf(key, "branch.%s.merge", new_ref);
 -              git_config_set(key, tracking.src);
 -              free(tracking.src);
 -              printf("Branch %s set up to track remote branch %s.\n",
 -                             new_ref, orig_ref);
 -      }
 -
 -      return 0;
 -}
 -
 -static void create_branch(const char *name, const char *start_name,
 -                        int force, int reflog, int track)
 -{
 -      struct ref_lock *lock;
 -      struct commit *commit;
 -      unsigned char sha1[20];
 -      char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
 -      int forcing = 0;
 -
 -      snprintf(ref, sizeof ref, "refs/heads/%s", name);
 -      if (check_ref_format(ref))
 -              die("'%s' is not a valid branch name.", name);
 -
 -      if (resolve_ref(ref, sha1, 1, NULL)) {
 -              if (!force)
 -                      die("A branch named '%s' already exists.", name);
 -              else if (!is_bare_repository() && !strcmp(head, name))
 -                      die("Cannot force update the current branch.");
 -              forcing = 1;
 -      }
 -
 -      real_ref = NULL;
 -      if (get_sha1(start_name, sha1))
 -              die("Not a valid object name: '%s'.", start_name);
 -
 -      switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
 -      case 0:
 -              /* Not branching from any existing branch */
 -              real_ref = NULL;
 -              break;
 -      case 1:
 -              /* Unique completion -- good */
 -              break;
 -      default:
 -              die("Ambiguous object name: '%s'.", start_name);
 -              break;
 -      }
 -
 -      if ((commit = lookup_commit_reference(sha1)) == NULL)
 -              die("Not a valid branch point: '%s'.", start_name);
 -      hashcpy(sha1, commit->object.sha1);
 -
 -      lock = lock_any_ref_for_update(ref, NULL, 0);
 -      if (!lock)
 -              die("Failed to lock ref for update: %s.", strerror(errno));
 -
 -      if (reflog)
 -              log_all_ref_updates = 1;
 -
 -      if (forcing)
 -              snprintf(msg, sizeof msg, "branch: Reset from %s",
 -                       start_name);
 -      else
 -              snprintf(msg, sizeof msg, "branch: Created from %s",
 -                       start_name);
 -
 -      /* When branching off a remote branch, set up so that git-pull
 -         automatically merges from there.  So far, this is only done for
 -         remotes registered via .git/config.  */
 -      if (real_ref && track)
 -              setup_tracking(name, real_ref);
 -
 -      if (write_ref_sha1(lock, sha1, msg) < 0)
 -              die("Failed to write ref: %s.", strerror(errno));
 -
 -      free(real_ref);
 -}
 -
  static void rename_branch(const char *oldname, const char *newname, int force)
  {
        char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
@@@ -414,16 -551,14 +412,16 @@@ int cmd_branch(int argc, const char **a
  {
        int delete = 0, rename = 0, force_create = 0;
        int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
 -      int reflog = 0, track;
 +      int reflog = 0;
 +      enum branch_track track;
        int kinds = REF_LOCAL_BRANCH;
        struct commit_list *with_commit = NULL;
  
        struct option options[] = {
                OPT_GROUP("Generic options"),
                OPT__VERBOSE(&verbose),
 -              OPT_BOOLEAN( 0 , "track",  &track, "set up tracking mode (see git-pull(1))"),
 +              OPT_SET_INT( 0 , "track",  &track, "set up tracking mode (see git-pull(1))",
 +                      BRANCH_TRACK_EXPLICIT),
                OPT_BOOLEAN( 0 , "color",  &branch_use_color, "use colored output"),
                OPT_SET_INT('r', NULL,     &kinds, "act on remote-tracking branches",
                        REF_REMOTE_BRANCH),
        if (branch_use_color == -1)
                branch_use_color = git_use_color_default;
  
 -      track = branch_track;
 +      track = git_branch_track;
        argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
        if (!!delete + !!rename + !!force_create > 1)
                usage_with_options(builtin_branch_usage, options);
        else if (rename && (argc == 2))
                rename_branch(argv[0], argv[1], rename > 1);
        else if (argc <= 2)
 -              create_branch(argv[0], (argc == 2) ? argv[1] : head,
 +              create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
                              force_create, reflog, track);
        else
                usage_with_options(builtin_branch_usage, options);
diff --combined builtin-fast-export.c
@@@ -123,7 -123,7 +123,7 @@@ static void show_filemodify(struct diff
                        printf("D %s\n", spec->path);
                else {
                        struct object *object = lookup_object(spec->sha1);
 -                      printf("M 0%06o :%d %s\n", spec->mode,
 +                      printf("M %06o :%d %s\n", spec->mode,
                               get_object_mark(object), spec->path);
                }
        }
@@@ -196,8 -196,7 +196,7 @@@ static void handle_commit(struct commi
                          ? strlen(reencoded) : message
                          ? strlen(message) : 0),
               reencoded ? reencoded : message ? message : "");
-       if (reencoded)
-               free(reencoded);
+       free(reencoded);
  
        for (i = 0, p = commit->parents; p; p = p->next) {
                int mark = get_object_mark(&p->item->object);
diff --combined builtin-pack-objects.c
@@@ -16,7 -16,6 +16,7 @@@
  #include "progress.h"
  
  #ifdef THREADED_DELTA_SEARCH
 +#include "thread-utils.h"
  #include <pthread.h>
  #endif
  
@@@ -1429,8 -1428,7 +1429,7 @@@ static int try_delta(struct unpacked *t
         * accounting lock.  Compiler will optimize the strangeness
         * away when THREADED_DELTA_SEARCH is not defined.
         */
-       if (trg_entry->delta_data)
-               free(trg_entry->delta_data);
+       free(trg_entry->delta_data);
        cache_lock();
        if (trg_entry->delta_data) {
                delta_cache_size -= trg_entry->delta_size;
@@@ -1853,11 -1851,11 +1852,11 @@@ static int git_pack_config(const char *
        }
        if (!strcmp(k, "pack.threads")) {
                delta_search_threads = git_config_int(k, v);
 -              if (delta_search_threads < 1)
 +              if (delta_search_threads < 0)
                        die("invalid number of threads specified (%d)",
                            delta_search_threads);
  #ifndef THREADED_DELTA_SEARCH
 -              if (delta_search_threads > 1)
 +              if (delta_search_threads != 1)
                        warning("no threads support, ignoring %s", k);
  #endif
                return 0;
@@@ -2123,10 -2121,10 +2122,10 @@@ int cmd_pack_objects(int argc, const ch
                if (!prefixcmp(arg, "--threads=")) {
                        char *end;
                        delta_search_threads = strtoul(arg+10, &end, 0);
 -                      if (!arg[10] || *end || delta_search_threads < 1)
 +                      if (!arg[10] || *end || delta_search_threads < 0)
                                usage(pack_usage);
  #ifndef THREADED_DELTA_SEARCH
 -                      if (delta_search_threads > 1)
 +                      if (delta_search_threads != 1)
                                warning("no threads support, "
                                        "ignoring %s", arg);
  #endif
        if (!pack_to_stdout && thin)
                die("--thin cannot be used to build an indexable pack.");
  
 +#ifdef THREADED_DELTA_SEARCH
 +      if (!delta_search_threads)      /* --threads=0 means autodetect */
 +              delta_search_threads = online_cpus();
 +#endif
 +
        prepare_packed_git();
  
        if (progress)
diff --combined diff.c
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -118,8 -118,7 +118,7 @@@ static int parse_funcname_pattern(cons
                pp->next = funcname_pattern_list;
                funcname_pattern_list = pp;
        }
-       if (pp->pattern)
-               free(pp->pattern);
+       free(pp->pattern);
        pp->pattern = xstrdup(value);
        return 0;
  }
@@@ -272,8 -271,8 +271,8 @@@ static void print_line_count(int count
        }
  }
  
 -static void copy_file(int prefix, const char *data, int size,
 -              const char *set, const char *reset)
 +static void copy_file_with_prefix(int prefix, const char *data, int size,
 +                                const char *set, const char *reset)
  {
        int ch, nl_just_seen = 1;
        while (0 < size--) {
@@@ -331,9 -330,9 +330,9 @@@ static void emit_rewrite_diff(const cha
        print_line_count(lc_b);
        printf(" @@%s\n", reset);
        if (lc_a)
 -              copy_file('-', one->data, one->size, old, reset);
 +              copy_file_with_prefix('-', one->data, one->size, old, reset);
        if (lc_b)
 -              copy_file('+', two->data, two->size, new, reset);
 +              copy_file_with_prefix('+', two->data, two->size, new, reset);
  }
  
  static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
@@@ -492,10 -491,8 +491,8 @@@ static void free_diff_words_data(struc
                                ecbdata->diff_words->plus.text.size)
                        diff_words_show(ecbdata->diff_words);
  
-               if (ecbdata->diff_words->minus.text.ptr)
-                       free (ecbdata->diff_words->minus.text.ptr);
-               if (ecbdata->diff_words->plus.text.ptr)
-                       free (ecbdata->diff_words->plus.text.ptr);
+               free (ecbdata->diff_words->minus.text.ptr);
+               free (ecbdata->diff_words->plus.text.ptr);
                free(ecbdata->diff_words);
                ecbdata->diff_words = NULL;
        }
@@@ -982,90 -979,6 +979,90 @@@ static void show_numstat(struct diffsta
        }
  }
  
 +struct diffstat_dir {
 +      struct diffstat_file **files;
 +      int nr, percent, cumulative;
 +};
 +
 +static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
 +{
 +      unsigned long this_dir = 0;
 +      unsigned int sources = 0;
 +
 +      while (dir->nr) {
 +              struct diffstat_file *f = *dir->files;
 +              int namelen = strlen(f->name);
 +              unsigned long this;
 +              char *slash;
 +
 +              if (namelen < baselen)
 +                      break;
 +              if (memcmp(f->name, base, baselen))
 +                      break;
 +              slash = strchr(f->name + baselen, '/');
 +              if (slash) {
 +                      int newbaselen = slash + 1 - f->name;
 +                      this = gather_dirstat(dir, changed, f->name, newbaselen);
 +                      sources++;
 +              } else {
 +                      if (f->is_unmerged || f->is_binary)
 +                              this = 0;
 +                      else
 +                              this = f->added + f->deleted;
 +                      dir->files++;
 +                      dir->nr--;
 +                      sources += 2;
 +              }
 +              this_dir += this;
 +      }
 +
 +      /*
 +       * We don't report dirstat's for
 +       *  - the top level
 +       *  - or cases where everything came from a single directory
 +       *    under this directory (sources == 1).
 +       */
 +      if (baselen && sources != 1) {
 +              int permille = this_dir * 1000 / changed;
 +              if (permille) {
 +                      int percent = permille / 10;
 +                      if (percent >= dir->percent) {
 +                              printf("%4d.%01d%% %.*s\n", percent, permille % 10, baselen, base);
 +                              if (!dir->cumulative)
 +                                      return 0;
 +                      }
 +              }
 +      }
 +      return this_dir;
 +}
 +
 +static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
 +{
 +      int i;
 +      unsigned long changed;
 +      struct diffstat_dir dir;
 +
 +      /* Calculate total changes */
 +      changed = 0;
 +      for (i = 0; i < data->nr; i++) {
 +              if (data->files[i]->is_binary || data->files[i]->is_unmerged)
 +                      continue;
 +              changed += data->files[i]->added;
 +              changed += data->files[i]->deleted;
 +      }
 +
 +      /* This can happen even with many files, if everything was renames */
 +      if (!changed)
 +              return;
 +
 +      /* Show all directories with more than x% of the changes */
 +      dir.files = data->files;
 +      dir.nr = data->nr;
 +      dir.percent = options->dirstat_percent;
 +      dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
 +      gather_dirstat(&dir, changed, "", 0);
 +}
 +
  static void free_diffstat_info(struct diffstat_t *diffstat)
  {
        int i;
@@@ -1483,7 -1396,6 +1480,7 @@@ static void builtin_diffstat(const cha
  }
  
  static void builtin_checkdiff(const char *name_a, const char *name_b,
 +                            const char *attr_path,
                             struct diff_filespec *one,
                             struct diff_filespec *two, struct diff_options *o)
  {
        data.filename = name_b ? name_b : name_a;
        data.lineno = 0;
        data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
 -      data.ws_rule = whitespace_rule(data.filename);
 +      data.ws_rule = whitespace_rule(attr_path);
  
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
@@@ -1923,9 -1835,6 +1920,9 @@@ static const char *external_diff_attr(c
  {
        struct git_attr_check attr_diff_check;
  
 +      if (!name)
 +              return NULL;
 +
        setup_diff_attr_check(&attr_diff_check);
        if (!git_checkattr(name, 1, &attr_diff_check)) {
                const char *value = attr_diff_check.value;
  static void run_diff_cmd(const char *pgm,
                         const char *name,
                         const char *other,
 +                       const char *attr_path,
                         struct diff_filespec *one,
                         struct diff_filespec *two,
                         const char *xfrm_msg,
        if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
                pgm = NULL;
        else {
 -              const char *cmd = external_diff_attr(name);
 +              const char *cmd = external_diff_attr(attr_path);
                if (cmd)
                        pgm = cmd;
        }
@@@ -1996,15 -1904,6 +1993,15 @@@ static int similarity_index(struct diff
        return p->score * 100 / MAX_SCORE;
  }
  
 +static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
 +{
 +      /* Strip the prefix but do not molest /dev/null and absolute paths */
 +      if (*namep && **namep != '/')
 +              *namep += prefix_length;
 +      if (*otherp && **otherp != '/')
 +              *otherp += prefix_length;
 +}
 +
  static void run_diff(struct diff_filepair *p, struct diff_options *o)
  {
        const char *pgm = external_diff();
        struct diff_filespec *two = p->two;
        const char *name;
        const char *other;
 +      const char *attr_path;
        int complete_rewrite = 0;
  
 +      name  = p->one->path;
 +      other = (strcmp(name, p->two->path) ? p->two->path : NULL);
 +      attr_path = name;
 +      if (o->prefix_length)
 +              strip_prefix(o->prefix_length, &name, &other);
  
        if (DIFF_PAIR_UNMERGED(p)) {
 -              run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
 +              run_diff_cmd(pgm, name, NULL, attr_path,
 +                           NULL, NULL, NULL, o, 0);
                return;
        }
  
 -      name  = p->one->path;
 -      other = (strcmp(name, p->two->path) ? p->two->path : NULL);
        diff_fill_sha1_info(one);
        diff_fill_sha1_info(two);
  
                 * needs to be split into deletion and creation.
                 */
                struct diff_filespec *null = alloc_filespec(two->path);
 -              run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0);
 +              run_diff_cmd(NULL, name, other, attr_path,
 +                           one, null, xfrm_msg, o, 0);
                free(null);
                null = alloc_filespec(one->path);
 -              run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0);
 +              run_diff_cmd(NULL, name, other, attr_path,
 +                           null, two, xfrm_msg, o, 0);
                free(null);
        }
        else
 -              run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
 -                           complete_rewrite);
 +              run_diff_cmd(pgm, name, other, attr_path,
 +                           one, two, xfrm_msg, o, complete_rewrite);
  
        strbuf_release(&msg);
  }
@@@ -2122,9 -2014,6 +2119,9 @@@ static void run_diffstat(struct diff_fi
        name = p->one->path;
        other = (strcmp(name, p->two->path) ? p->two->path : NULL);
  
 +      if (o->prefix_length)
 +              strip_prefix(o->prefix_length, &name, &other);
 +
        diff_fill_sha1_info(p->one);
        diff_fill_sha1_info(p->two);
  
@@@ -2137,7 -2026,6 +2134,7 @@@ static void run_checkdiff(struct diff_f
  {
        const char *name;
        const char *other;
 +      const char *attr_path;
  
        if (DIFF_PAIR_UNMERGED(p)) {
                /* unmerged */
  
        name = p->one->path;
        other = (strcmp(name, p->two->path) ? p->two->path : NULL);
 +      attr_path = other ? other : name;
 +
 +      if (o->prefix_length)
 +              strip_prefix(o->prefix_length, &name, &other);
  
        diff_fill_sha1_info(p->one);
        diff_fill_sha1_info(p->two);
  
 -      builtin_checkdiff(name, other, p->one, p->two, o);
 +      builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
  }
  
  void diff_setup(struct diff_options *options)
        options->line_termination = '\n';
        options->break_opt = -1;
        options->rename_limit = -1;
 +      options->dirstat_percent = 3;
        options->context = 3;
        options->msg_sep = "";
  
@@@ -2197,13 -2080,6 +2194,13 @@@ int diff_setup_done(struct diff_option
        if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
                options->detect_rename = DIFF_DETECT_COPY;
  
 +      if (!DIFF_OPT_TST(options, RELATIVE_NAME))
 +              options->prefix = NULL;
 +      if (options->prefix)
 +              options->prefix_length = strlen(options->prefix);
 +      else
 +              options->prefix_length = 0;
 +
        if (options->output_format & (DIFF_FORMAT_NAME |
                                      DIFF_FORMAT_NAME_STATUS |
                                      DIFF_FORMAT_CHECKDIFF |
                                            DIFF_FORMAT_NUMSTAT |
                                            DIFF_FORMAT_DIFFSTAT |
                                            DIFF_FORMAT_SHORTSTAT |
 +                                          DIFF_FORMAT_DIRSTAT |
                                            DIFF_FORMAT_SUMMARY |
                                            DIFF_FORMAT_PATCH);
  
                                      DIFF_FORMAT_NUMSTAT |
                                      DIFF_FORMAT_DIFFSTAT |
                                      DIFF_FORMAT_SHORTSTAT |
 +                                    DIFF_FORMAT_DIRSTAT |
                                      DIFF_FORMAT_SUMMARY |
                                      DIFF_FORMAT_CHECKDIFF))
                DIFF_OPT_SET(options, RECURSIVE);
@@@ -2335,10 -2209,6 +2332,10 @@@ int diff_opt_parse(struct diff_options 
                options->output_format |= DIFF_FORMAT_NUMSTAT;
        else if (!strcmp(arg, "--shortstat"))
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
 +      else if (opt_arg(arg, 'X', "dirstat", &options->dirstat_percent))
 +              options->output_format |= DIFF_FORMAT_DIRSTAT;
 +      else if (!strcmp(arg, "--cumulative"))
 +              options->output_format |= DIFF_FORMAT_CUMULATIVE;
        else if (!strcmp(arg, "--check"))
                options->output_format |= DIFF_FORMAT_CHECKDIFF;
        else if (!strcmp(arg, "--summary"))
        }
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
 +      else if (!strcmp(arg, "--relative"))
 +              DIFF_OPT_SET(options, RELATIVE_NAME);
 +      else if (!prefixcmp(arg, "--relative=")) {
 +              DIFF_OPT_SET(options, RELATIVE_NAME);
 +              options->prefix = arg + 11;
 +      }
  
        /* xdiff options */
        else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
@@@ -2615,20 -2479,12 +2612,20 @@@ static void diff_flush_raw(struct diff_
                printf("%c%c", p->status, inter_name_termination);
        }
  
 -      if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) {
 -              write_name_quoted(p->one->path, stdout, inter_name_termination);
 -              write_name_quoted(p->two->path, stdout, line_termination);
 +      if (p->status == DIFF_STATUS_COPIED ||
 +          p->status == DIFF_STATUS_RENAMED) {
 +              const char *name_a, *name_b;
 +              name_a = p->one->path;
 +              name_b = p->two->path;
 +              strip_prefix(opt->prefix_length, &name_a, &name_b);
 +              write_name_quoted(name_a, stdout, inter_name_termination);
 +              write_name_quoted(name_b, stdout, line_termination);
        } else {
 -              const char *path = p->one->mode ? p->one->path : p->two->path;
 -              write_name_quoted(path, stdout, line_termination);
 +              const char *name_a, *name_b;
 +              name_a = p->one->mode ? p->one->path : p->two->path;
 +              name_b = NULL;
 +              strip_prefix(opt->prefix_length, &name_a, &name_b);
 +              write_name_quoted(name_a, stdout, line_termination);
        }
  }
  
@@@ -2825,13 -2681,8 +2822,13 @@@ static void flush_one_pair(struct diff_
                diff_flush_checkdiff(p, opt);
        else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
                diff_flush_raw(p, opt);
 -      else if (fmt & DIFF_FORMAT_NAME)
 -              write_name_quoted(p->two->path, stdout, opt->line_termination);
 +      else if (fmt & DIFF_FORMAT_NAME) {
 +              const char *name_a, *name_b;
 +              name_a = p->two->path;
 +              name_b = NULL;
 +              strip_prefix(opt->prefix_length, &name_a, &name_b);
 +              write_name_quoted(name_a, stdout, opt->line_termination);
 +      }
  }
  
  static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
@@@ -3076,7 -2927,7 +3073,7 @@@ void diff_flush(struct diff_options *op
                separator++;
        }
  
 -      if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
 +      if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) {
                struct diffstat_t diffstat;
  
                memset(&diffstat, 0, sizeof(struct diffstat_t));
                        if (check_pair_status(p))
                                diff_flush_stat(p, options, &diffstat);
                }
 +              if (output_format & DIFF_FORMAT_DIRSTAT)
 +                      show_dirstat(&diffstat, options);
                if (output_format & DIFF_FORMAT_NUMSTAT)
                        show_numstat(&diffstat, options);
                if (output_format & DIFF_FORMAT_DIFFSTAT)
@@@ -3319,11 -3168,6 +3316,11 @@@ void diff_addremove(struct diff_option
  
        if (!path) path = "";
        sprintf(concatpath, "%s%s", base, path);
 +
 +      if (options->prefix &&
 +          strncmp(concatpath, options->prefix, options->prefix_length))
 +              return;
 +
        one = alloc_filespec(concatpath);
        two = alloc_filespec(concatpath);
  
@@@ -3353,11 -3197,6 +3350,11 @@@ void diff_change(struct diff_options *o
        }
        if (!path) path = "";
        sprintf(concatpath, "%s%s", base, path);
 +
 +      if (options->prefix &&
 +          strncmp(concatpath, options->prefix, options->prefix_length))
 +              return;
 +
        one = alloc_filespec(concatpath);
        two = alloc_filespec(concatpath);
        fill_filespec(one, old_sha1, old_mode);
@@@ -3372,11 -3211,6 +3369,11 @@@ void diff_unmerge(struct diff_options *
                  unsigned mode, const unsigned char *sha1)
  {
        struct diff_filespec *one, *two;
 +
 +      if (options->prefix &&
 +          strncmp(path, options->prefix, options->prefix_length))
 +              return;
 +
        one = alloc_filespec(path);
        two = alloc_filespec(path);
        fill_filespec(one, sha1, mode);
diff --combined pretty.c
+++ b/pretty.c
@@@ -30,8 -30,7 +30,7 @@@ enum cmit_fmt get_commit_format(const c
        if (*arg == '=')
                arg++;
        if (!prefixcmp(arg, "format:")) {
-               if (user_format)
-                       free(user_format);
+               free(user_format);
                user_format = xstrdup(arg + 7);
                return CMIT_FMT_USERFORMAT;
        }
@@@ -110,9 -109,9 +109,9 @@@ needquote
        strbuf_addstr(sb, "?=");
  }
  
 -static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
 -                       const char *line, enum date_mode dmode,
 -                       const char *encoding)
 +void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb,
 +                const char *line, enum date_mode dmode,
 +                const char *encoding)
  {
        char *date;
        int namelen;
@@@ -621,23 -620,23 +620,23 @@@ static void pp_header(enum cmit_fmt fmt
                 */
                if (!memcmp(line, "author ", 7)) {
                        strbuf_grow(sb, linelen + 80);
 -                      add_user_info("Author", fmt, sb, line + 7, dmode, encoding);
 +                      pp_user_info("Author", fmt, sb, line + 7, dmode, encoding);
                }
                if (!memcmp(line, "committer ", 10) &&
                    (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) {
                        strbuf_grow(sb, linelen + 80);
 -                      add_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
 +                      pp_user_info("Commit", fmt, sb, line + 10, dmode, encoding);
                }
        }
  }
  
 -static void pp_title_line(enum cmit_fmt fmt,
 -                        const char **msg_p,
 -                        struct strbuf *sb,
 -                        const char *subject,
 -                        const char *after_subject,
 -                        const char *encoding,
 -                        int plain_non_ascii)
 +void pp_title_line(enum cmit_fmt fmt,
 +                 const char **msg_p,
 +                 struct strbuf *sb,
 +                 const char *subject,
 +                 const char *after_subject,
 +                 const char *encoding,
 +                 int plain_non_ascii)
  {
        struct strbuf title;
  
        strbuf_release(&title);
  }
  
 -static void pp_remainder(enum cmit_fmt fmt,
 -                       const char **msg_p,
 -                       struct strbuf *sb,
 -                       int indent)
 +void pp_remainder(enum cmit_fmt fmt,
 +                const char **msg_p,
 +                struct strbuf *sb,
 +                int indent)
  {
        int first = 1;
        for (;;) {
diff --combined remote.c
+++ b/remote.c
  #include "remote.h"
  #include "refs.h"
  
 +struct counted_string {
 +      size_t len;
 +      const char *s;
 +};
 +struct rewrite {
 +      const char *base;
 +      size_t baselen;
 +      struct counted_string *instead_of;
 +      int instead_of_nr;
 +      int instead_of_alloc;
 +};
 +
  static struct remote **remotes;
 -static int allocated_remotes;
 +static int remotes_alloc;
 +static int remotes_nr;
  
  static struct branch **branches;
 -static int allocated_branches;
 +static int branches_alloc;
 +static int branches_nr;
  
  static struct branch *current_branch;
  static const char *default_remote_name;
  
 +static struct rewrite **rewrite;
 +static int rewrite_alloc;
 +static int rewrite_nr;
 +
  #define BUF_SIZE (2048)
  static char buffer[BUF_SIZE];
  
 +static const char *alias_url(const char *url)
 +{
 +      int i, j;
 +      char *ret;
 +      struct counted_string *longest;
 +      int longest_i;
 +
 +      longest = NULL;
 +      longest_i = -1;
 +      for (i = 0; i < rewrite_nr; i++) {
 +              if (!rewrite[i])
 +                      continue;
 +              for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
 +                      if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
 +                          (!longest ||
 +                           longest->len < rewrite[i]->instead_of[j].len)) {
 +                              longest = &(rewrite[i]->instead_of[j]);
 +                              longest_i = i;
 +                      }
 +              }
 +      }
 +      if (!longest)
 +              return url;
 +
 +      ret = malloc(rewrite[longest_i]->baselen +
 +                   (strlen(url) - longest->len) + 1);
 +      strcpy(ret, rewrite[longest_i]->base);
 +      strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
 +      return ret;
 +}
 +
  static void add_push_refspec(struct remote *remote, const char *ref)
  {
 -      int nr = remote->push_refspec_nr + 1;
 -      remote->push_refspec =
 -              xrealloc(remote->push_refspec, nr * sizeof(char *));
 -      remote->push_refspec[nr-1] = ref;
 -      remote->push_refspec_nr = nr;
 +      ALLOC_GROW(remote->push_refspec,
 +                 remote->push_refspec_nr + 1,
 +                 remote->push_refspec_alloc);
 +      remote->push_refspec[remote->push_refspec_nr++] = ref;
  }
  
  static void add_fetch_refspec(struct remote *remote, const char *ref)
  {
 -      int nr = remote->fetch_refspec_nr + 1;
 -      remote->fetch_refspec =
 -              xrealloc(remote->fetch_refspec, nr * sizeof(char *));
 -      remote->fetch_refspec[nr-1] = ref;
 -      remote->fetch_refspec_nr = nr;
 +      ALLOC_GROW(remote->fetch_refspec,
 +                 remote->fetch_refspec_nr + 1,
 +                 remote->fetch_refspec_alloc);
 +      remote->fetch_refspec[remote->fetch_refspec_nr++] = ref;
  }
  
  static void add_url(struct remote *remote, const char *url)
  {
 -      int nr = remote->url_nr + 1;
 -      remote->url =
 -              xrealloc(remote->url, nr * sizeof(char *));
 -      remote->url[nr-1] = url;
 -      remote->url_nr = nr;
 +      ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
 +      remote->url[remote->url_nr++] = url;
 +}
 +
 +static void add_url_alias(struct remote *remote, const char *url)
 +{
 +      add_url(remote, alias_url(url));
  }
  
  static struct remote *make_remote(const char *name, int len)
  {
 -      int i, empty = -1;
 +      struct remote *ret;
 +      int i;
  
 -      for (i = 0; i < allocated_remotes; i++) {
 -              if (!remotes[i]) {
 -                      if (empty < 0)
 -                              empty = i;
 -              } else {
 -                      if (len ? (!strncmp(name, remotes[i]->name, len) &&
 -                                 !remotes[i]->name[len]) :
 -                          !strcmp(name, remotes[i]->name))
 -                              return remotes[i];
 -              }
 +      for (i = 0; i < remotes_nr; i++) {
 +              if (len ? (!strncmp(name, remotes[i]->name, len) &&
 +                         !remotes[i]->name[len]) :
 +                  !strcmp(name, remotes[i]->name))
 +                      return remotes[i];
        }
  
 -      if (empty < 0) {
 -              empty = allocated_remotes;
 -              allocated_remotes += allocated_remotes ? allocated_remotes : 1;
 -              remotes = xrealloc(remotes,
 -                                 sizeof(*remotes) * allocated_remotes);
 -              memset(remotes + empty, 0,
 -                     (allocated_remotes - empty) * sizeof(*remotes));
 -      }
 -      remotes[empty] = xcalloc(1, sizeof(struct remote));
 +      ret = xcalloc(1, sizeof(struct remote));
 +      ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
 +      remotes[remotes_nr++] = ret;
        if (len)
 -              remotes[empty]->name = xstrndup(name, len);
 +              ret->name = xstrndup(name, len);
        else
 -              remotes[empty]->name = xstrdup(name);
 -      return remotes[empty];
 +              ret->name = xstrdup(name);
 +      return ret;
  }
  
  static void add_merge(struct branch *branch, const char *name)
  {
 -      int nr = branch->merge_nr + 1;
 -      branch->merge_name =
 -              xrealloc(branch->merge_name, nr * sizeof(char *));
 -      branch->merge_name[nr-1] = name;
 -      branch->merge_nr = nr;
 +      ALLOC_GROW(branch->merge_name, branch->merge_nr + 1,
 +                 branch->merge_alloc);
 +      branch->merge_name[branch->merge_nr++] = name;
  }
  
  static struct branch *make_branch(const char *name, int len)
  {
 -      int i, empty = -1;
 +      struct branch *ret;
 +      int i;
        char *refname;
  
 -      for (i = 0; i < allocated_branches; i++) {
 -              if (!branches[i]) {
 -                      if (empty < 0)
 -                              empty = i;
 -              } else {
 -                      if (len ? (!strncmp(name, branches[i]->name, len) &&
 -                                 !branches[i]->name[len]) :
 -                          !strcmp(name, branches[i]->name))
 -                              return branches[i];
 -              }
 +      for (i = 0; i < branches_nr; i++) {
 +              if (len ? (!strncmp(name, branches[i]->name, len) &&
 +                         !branches[i]->name[len]) :
 +                  !strcmp(name, branches[i]->name))
 +                      return branches[i];
        }
  
 -      if (empty < 0) {
 -              empty = allocated_branches;
 -              allocated_branches += allocated_branches ? allocated_branches : 1;
 -              branches = xrealloc(branches,
 -                                 sizeof(*branches) * allocated_branches);
 -              memset(branches + empty, 0,
 -                     (allocated_branches - empty) * sizeof(*branches));
 -      }
 -      branches[empty] = xcalloc(1, sizeof(struct branch));
 +      ALLOC_GROW(branches, branches_nr + 1, branches_alloc);
 +      ret = xcalloc(1, sizeof(struct branch));
 +      branches[branches_nr++] = ret;
        if (len)
 -              branches[empty]->name = xstrndup(name, len);
 +              ret->name = xstrndup(name, len);
        else
 -              branches[empty]->name = xstrdup(name);
 +              ret->name = xstrdup(name);
        refname = malloc(strlen(name) + strlen("refs/heads/") + 1);
        strcpy(refname, "refs/heads/");
 -      strcpy(refname + strlen("refs/heads/"),
 -             branches[empty]->name);
 -      branches[empty]->refname = refname;
 +      strcpy(refname + strlen("refs/heads/"), ret->name);
 +      ret->refname = refname;
 +
 +      return ret;
 +}
 +
 +static struct rewrite *make_rewrite(const char *base, int len)
 +{
 +      struct rewrite *ret;
 +      int i;
 +
 +      for (i = 0; i < rewrite_nr; i++) {
 +              if (len
 +                  ? (len == rewrite[i]->baselen &&
 +                     !strncmp(base, rewrite[i]->base, len))
 +                  : !strcmp(base, rewrite[i]->base))
 +                      return rewrite[i];
 +      }
 +
 +      ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
 +      ret = xcalloc(1, sizeof(struct rewrite));
 +      rewrite[rewrite_nr++] = ret;
 +      if (len) {
 +              ret->base = xstrndup(base, len);
 +              ret->baselen = len;
 +      }
 +      else {
 +              ret->base = xstrdup(base);
 +              ret->baselen = strlen(base);
 +      }
 +      return ret;
 +}
  
 -      return branches[empty];
 +static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
 +{
 +      ALLOC_GROW(rewrite->instead_of, rewrite->instead_of_nr + 1, rewrite->instead_of_alloc);
 +      rewrite->instead_of[rewrite->instead_of_nr].s = instead_of;
 +      rewrite->instead_of[rewrite->instead_of_nr].len = strlen(instead_of);
 +      rewrite->instead_of_nr++;
  }
  
  static void read_remotes_file(struct remote *remote)
  
                switch (value_list) {
                case 0:
 -                      add_url(remote, xstrdup(s));
 +                      add_url_alias(remote, xstrdup(s));
                        break;
                case 1:
                        add_push_refspec(remote, xstrdup(s));
@@@ -267,7 -206,7 +267,7 @@@ static void read_branches_file(struct r
        } else {
                branch = "refs/heads/master";
        }
 -      add_url(remote, p);
 +      add_url_alias(remote, p);
        add_fetch_refspec(remote, branch);
        remote->fetch_tags = 1; /* always auto-follow */
  }
@@@ -297,19 -236,6 +297,19 @@@ static int handle_config(const char *ke
                }
                return 0;
        }
 +      if (!prefixcmp(key, "url.")) {
 +              struct rewrite *rewrite;
 +              name = key + 5;
 +              subkey = strrchr(name, '.');
 +              if (!subkey)
 +                      return 0;
 +              rewrite = make_rewrite(name, subkey - name);
 +              if (!strcmp(subkey, ".insteadof")) {
 +                      if (!value)
 +                              return config_error_nonbool(key);
 +                      add_instead_of(rewrite, xstrdup(value));
 +              }
 +      }
        if (prefixcmp(key,  "remote."))
                return 0;
        name = key + 7;
        return 0;
  }
  
 +static void alias_all_urls(void)
 +{
 +      int i, j;
 +      for (i = 0; i < remotes_nr; i++) {
 +              if (!remotes[i])
 +                      continue;
 +              for (j = 0; j < remotes[i]->url_nr; j++) {
 +                      remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
 +              }
 +      }
 +}
 +
  static void read_config(void)
  {
        unsigned char sha1[20];
                        make_branch(head_ref + strlen("refs/heads/"), 0);
        }
        git_config(handle_config);
 +      alias_all_urls();
  }
  
  struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
@@@ -455,7 -368,7 +455,7 @@@ struct remote *remote_get(const char *n
                        read_branches_file(ret);
        }
        if (!ret->url)
 -              add_url(ret, name);
 +              add_url_alias(ret, name);
        if (!ret->url)
                return NULL;
        ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec);
@@@ -467,7 -380,7 +467,7 @@@ int for_each_remote(each_remote_fn fn, 
  {
        int i, result = 0;
        read_config();
 -      for (i = 0; i < allocated_remotes && !result; i++) {
 +      for (i = 0; i < remotes_nr && !result; i++) {
                struct remote *r = remotes[i];
                if (!r)
                        continue;
@@@ -593,8 -506,7 +593,7 @@@ void free_refs(struct ref *ref
        struct ref *next;
        while (ref) {
                next = ref->next;
-               if (ref->peer_ref)
-                       free(ref->peer_ref);
+               free(ref->peer_ref);
                free(ref);
                ref = next;
        }
@@@ -730,17 -642,9 +729,17 @@@ static int match_explicit(struct ref *s
                errs = 1;
  
        if (!dst_value) {
 +              unsigned char sha1[20];
 +              int flag;
 +
                if (!matched_src)
                        return errs;
 -              dst_value = matched_src->name;
 +              dst_value = resolve_ref(matched_src->name, sha1, 1, &flag);
 +              if (!dst_value ||
 +                  ((flag & REF_ISSYMREF) &&
 +                   prefixcmp(dst_value, "refs/heads/")))
 +                      die("%s cannot be resolved to branch.",
 +                          matched_src->name);
        }
  
        switch (count_refspec_match(dst_value, dst, &matched_dst)) {