Merge branch 'js/ref-namespaces'
authorJunio C Hamano <gitster@pobox.com>
Thu, 18 Aug 2011 00:35:38 +0000 (17:35 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 18 Aug 2011 00:35:38 +0000 (17:35 -0700)
* js/ref-namespaces:
ref namespaces: tests
ref namespaces: documentation
ref namespaces: Support remote repositories via upload-pack and receive-pack
ref namespaces: infrastructure
Fix prefix handling in ref iteration functions

1  2 
Documentation/Makefile
Documentation/git-receive-pack.txt
Documentation/git-upload-pack.txt
Documentation/git.txt
builtin/receive-pack.c
cache.h
contrib/completion/git-completion.bash
environment.c
git.c
refs.c
refs.h
diff --combined Documentation/Makefile
index 18c71d763f472b1a98ad162626a0bc3d39d7e975,2004fbe6306d6bf3de98addd629a2be358edf9c5..6346a75dda72533667a96eea2bd65bb280383f99
@@@ -6,7 -6,7 +6,7 @@@ MAN5_TXT=gitattributes.txt gitignore.tx
        gitrepository-layout.txt
  MAN7_TXT=gitcli.txt gittutorial.txt gittutorial-2.txt \
        gitcvs-migration.txt gitcore-tutorial.txt gitglossary.txt \
-       gitdiffcore.txt gitrevisions.txt gitworkflows.txt
+       gitdiffcore.txt gitnamespaces.txt gitrevisions.txt gitworkflows.txt
  
  MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
  MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
@@@ -232,7 -232,6 +232,7 @@@ cmd-list.made: cmd-list.perl ../command
  clean:
        $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
        $(RM) *.texi *.texi+ *.texi++ git.info gitman.info
 +      $(RM) *.pdf
        $(RM) howto-index.txt howto/*.html doc.dep
        $(RM) technical/api-*.html technical/api-index.txt
        $(RM) $(cmds_txt) *.made
index d7b68afbc208c0bd067b4073cff75644888b5ea9,3534ba01c49b28d1f7d0498034897ba7d5c080fc..a3a1d8eea3ec733d155475f2d6216e1e71245f10
@@@ -8,8 -8,7 +8,8 @@@ git-receive-pack - Receive what is push
  
  SYNOPSIS
  --------
 -'git-receive-pack' <directory>
 +[verse]
 +'git-receive-pack' [--quiet] <directory>
  
  DESCRIPTION
  -----------
@@@ -35,9 -34,6 +35,9 @@@ are not fast-forwards
  
  OPTIONS
  -------
 +--quiet::
 +      Print only error messages.
 +
  <directory>::
        The repository to sync into.
  
@@@ -153,7 -149,7 +153,7 @@@ if the repository is packed and is serv
  
  SEE ALSO
  --------
- linkgit:git-send-pack[1]
+ linkgit:git-send-pack[1], linkgit:gitnamespaces[7]
  
  GIT
  ---
index a58e90ca8dada4a9847886374968635ea6bc3f9f,61a9a04a8df0bd863522ae281547a9c31f6bb18e..71f16083d6b2c366a54db1d966e17ab2aeda1ef1
@@@ -8,7 -8,6 +8,7 @@@ git-upload-pack - Send objects packed b
  
  SYNOPSIS
  --------
 +[verse]
  'git-upload-pack' [--strict] [--timeout=<n>] <directory>
  
  DESCRIPTION
@@@ -34,6 -33,10 +34,10 @@@ OPTION
  <directory>::
        The repository to sync from.
  
+ SEE ALSO
+ --------
+ linkgit:gitnamespaces[7]
  GIT
  ---
  Part of the linkgit:git[1] suite
diff --combined Documentation/git.txt
index 0172cd70149914897862fbf0f3f221c7ef31ea5c,1fe434179147cc0e19bbf6a71fa31415ee2b9130..710d750cfd7a67bacedaa851afaeed4469d38a77
@@@ -10,8 -10,8 +10,8 @@@ SYNOPSI
  --------
  [verse]
  'git' [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
-     [-p|--paginate|--no-pager] [--no-replace-objects]
-     [--bare] [--git-dir=<path>] [--work-tree=<path>]
+     [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
+     [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
      [-c <name>=<value>]
      [--help] <command> [<args>]
  
@@@ -44,16 -44,9 +44,16 @@@ unreleased) version of git, that is ava
  branch of the `git.git` repository.
  Documentation for older releases are available here:
  
 -* link:v1.7.5.2/git.html[documentation for release 1.7.5.2]
 +* link:v1.7.6/git.html[documentation for release 1.7.6]
  
  * release notes for
 +  link:RelNotes/1.7.6.txt[1.7.6].
 +
 +* link:v1.7.5.4/git.html[documentation for release 1.7.5.4]
 +
 +* release notes for
 +  link:RelNotes/1.7.5.4.txt[1.7.5.4],
 +  link:RelNotes/1.7.5.3.txt[1.7.5.3],
    link:RelNotes/1.7.5.2.txt[1.7.5.2],
    link:RelNotes/1.7.5.1.txt[1.7.5.1],
    link:RelNotes/1.7.5.txt[1.7.5].
@@@ -330,6 -323,11 +330,11 @@@ help ...`
        variable (see core.worktree in linkgit:git-config[1] for a
        more detailed discussion).
  
+ --namespace=<path>::
+       Set the git namespace.  See linkgit:gitnamespaces[7] for more
+       details.  Equivalent to setting the `GIT_NAMESPACE` environment
+       variable.
  --bare::
        Treat the repository as a bare repository.  If GIT_DIR
        environment is not set, it is set to the current working
@@@ -523,15 -521,16 +528,15 @@@ Any git command accepting any <object> 
  symbolic notation:
  
  HEAD::
 -      indicates the head of the current branch (i.e. the
 -      contents of `$GIT_DIR/HEAD`).
 +      indicates the head of the current branch.
  
  <tag>::
        a valid tag 'name'
 -      (i.e. the contents of `$GIT_DIR/refs/tags/<tag>`).
 +      (i.e. a `refs/tags/<tag>` reference).
  
  <head>::
        a valid head 'name'
 -      (i.e. the contents of `$GIT_DIR/refs/heads/<head>`).
 +      (i.e. a `refs/heads/<head>` reference).
  
  For a more complete list of ways to spell object names, see
  "SPECIFYING REVISIONS" section in linkgit:gitrevisions[7].
@@@ -593,6 -592,10 +598,10 @@@ git so take care if using Cogito etc
        This can also be controlled by the '--work-tree' command line
        option and the core.worktree configuration variable.
  
+ 'GIT_NAMESPACE'::
+       Set the git namespace; see linkgit:gitnamespaces[7] for details.
+       The '--namespace' command-line option also sets this value.
  'GIT_CEILING_DIRECTORIES'::
        This should be a colon-separated list of absolute paths.
        If set, it is a list of directories that git should not chdir
diff --combined builtin/receive-pack.c
index 0d51bfb79eb3e8b234ca4ee98844de20d46503b6,e589082b31cab056cc9bcf437c6fdaeac96c66e3..60260d0aa93a878968e5ed72a2a77b96c5c0f040
@@@ -10,7 -10,6 +10,7 @@@
  #include "remote.h"
  #include "transport.h"
  #include "string-list.h"
 +#include "sha1-array.h"
  
  static const char receive_pack_usage[] = "git receive-pack <git-dir>";
  
@@@ -120,9 -119,25 +120,25 @@@ static int show_ref(const char *path, c
        return 0;
  }
  
+ static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+ {
+       path = strip_namespace(path);
+       /*
+        * Advertise refs outside our current namespace as ".have"
+        * refs, so that the client can use them to minimize data
+        * transfer but will otherwise ignore them. This happens to
+        * cover ".have" that are thrown in by add_one_alternate_ref()
+        * to mark histories that are complete in our alternates as
+        * well.
+        */
+       if (!path)
+               path = ".have";
+       return show_ref(path, sha1, flag, cb_data);
+ }
  static void write_head_info(void)
  {
-       for_each_ref(show_ref, NULL);
+       for_each_ref(show_ref_cb, NULL);
        if (!sent_capabilities)
                show_ref("capabilities^{}", null_sha1, 0, NULL);
  
@@@ -333,6 -348,8 +349,8 @@@ static void refuse_unconfigured_deny_de
  static const char *update(struct command *cmd)
  {
        const char *name = cmd->ref_name;
+       struct strbuf namespaced_name_buf = STRBUF_INIT;
+       const char *namespaced_name;
        unsigned char *old_sha1 = cmd->old_sha1;
        unsigned char *new_sha1 = cmd->new_sha1;
        struct ref_lock *lock;
                return "funny refname";
        }
  
-       if (is_ref_checked_out(name)) {
+       strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
+       namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
+       if (is_ref_checked_out(namespaced_name)) {
                switch (deny_current_branch) {
                case DENY_IGNORE:
                        break;
                        return "deletion prohibited";
                }
  
-               if (!strcmp(name, head_name)) {
+               if (!strcmp(namespaced_name, head_name)) {
                        switch (deny_delete_current) {
                        case DENY_IGNORE:
                                break;
                        rp_warning("Allowing deletion of corrupt ref.");
                        old_sha1 = NULL;
                }
-               if (delete_ref(name, old_sha1, 0)) {
+               if (delete_ref(namespaced_name, old_sha1, 0)) {
                        rp_error("failed to delete %s", name);
                        return "failed to delete";
                }
                return NULL; /* good */
        }
        else {
-               lock = lock_any_ref_for_update(name, old_sha1, 0);
+               lock = lock_any_ref_for_update(namespaced_name, old_sha1, 0);
                if (!lock) {
                        rp_error("failed to lock %s", name);
                        return "failed to lock";
@@@ -491,17 -511,29 +512,29 @@@ static void run_update_post_hook(struc
  
  static void check_aliased_update(struct command *cmd, struct string_list *list)
  {
+       struct strbuf buf = STRBUF_INIT;
+       const char *dst_name;
        struct string_list_item *item;
        struct command *dst_cmd;
        unsigned char sha1[20];
        char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41];
        int flag;
  
-       const char *dst_name = resolve_ref(cmd->ref_name, sha1, 0, &flag);
+       strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
+       dst_name = resolve_ref(buf.buf, sha1, 0, &flag);
+       strbuf_release(&buf);
  
        if (!(flag & REF_ISSYMREF))
                return;
  
+       dst_name = strip_namespace(dst_name);
+       if (!dst_name) {
+               rp_error("refusing update to broken symref '%s'", cmd->ref_name);
+               cmd->skip_update = 1;
+               cmd->error_string = "broken symref";
+               return;
+       }
        if ((item = string_list_lookup(list, dst_name)) == NULL)
                return;
  
@@@ -636,7 -668,7 +669,7 @@@ static const char *parse_pack_header(st
  
  static const char *pack_lockfile;
  
 -static const char *unpack(void)
 +static const char *unpack(int quiet)
  {
        struct pack_header hdr;
        const char *hdr_err;
  
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
                int code, i = 0;
 -              const char *unpacker[4];
 +              const char *unpacker[5];
                unpacker[i++] = "unpack-objects";
 +              if (quiet)
 +                      unpacker[i++] = "-q";
                if (receive_fsck_objects)
                        unpacker[i++] = "--strict";
                unpacker[i++] = hdr_arg;
@@@ -734,28 -764,18 +767,28 @@@ static int delete_only(struct command *
        return 1;
  }
  
 -static void add_one_alternate_ref(const struct ref *ref, void *unused)
 +static void add_one_alternate_sha1(const unsigned char sha1[20], void *unused)
  {
 -      add_extra_ref(".have", ref->old_sha1, 0);
 +      add_extra_ref(".have", sha1, 0);
 +}
 +
 +static void collect_one_alternate_ref(const struct ref *ref, void *data)
 +{
 +      struct sha1_array *sa = data;
 +      sha1_array_append(sa, ref->old_sha1);
  }
  
  static void add_alternate_refs(void)
  {
 -      foreach_alt_odb(refs_from_alternate_cb, add_one_alternate_ref);
 +      struct sha1_array sa = SHA1_ARRAY_INIT;
 +      for_each_alternate_ref(collect_one_alternate_ref, &sa);
 +      sha1_array_for_each_unique(&sa, add_one_alternate_sha1, NULL);
 +      sha1_array_clear(&sa);
  }
  
  int cmd_receive_pack(int argc, const char **argv, const char *prefix)
  {
 +      int quiet = 0;
        int advertise_refs = 0;
        int stateless_rpc = 0;
        int i;
                const char *arg = *argv++;
  
                if (*arg == '-') {
 +                      if (!strcmp(arg, "--quiet")) {
 +                              quiet = 1;
 +                              continue;
 +                      }
 +
                        if (!strcmp(arg, "--advertise-refs")) {
                                advertise_refs = 1;
                                continue;
                const char *unpack_status = NULL;
  
                if (!delete_only(commands))
 -                      unpack_status = unpack();
 +                      unpack_status = unpack(quiet);
                execute_commands(commands, unpack_status);
                if (pack_lockfile)
                        unlink_or_warn(pack_lockfile);
diff --combined cache.h
index b33ba73197b8713df752d44db19c78d2285635ab,26310c464ef9f28246d5aa2ab027308de30bab76..fcf4501a60d14105134a2d5be984afde5dda3e3d
+++ b/cache.h
@@@ -6,7 -6,6 +6,7 @@@
  #include "hash.h"
  #include "advice.h"
  #include "gettext.h"
 +#include "convert.h"
  
  #include SHA1_HEADER
  #ifndef git_SHA_CTX
  #endif
  
  #include <zlib.h>
 -#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
 -#define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
 -#endif
 -
 -void git_inflate_init(z_streamp strm);
 -void git_inflate_end(z_streamp strm);
 -int git_inflate(z_streamp strm, int flush);
 +typedef struct git_zstream {
 +      z_stream z;
 +      unsigned long avail_in;
 +      unsigned long avail_out;
 +      unsigned long total_in;
 +      unsigned long total_out;
 +      unsigned char *next_in;
 +      unsigned char *next_out;
 +} git_zstream;
 +
 +void git_inflate_init(git_zstream *);
 +void git_inflate_init_gzip_only(git_zstream *);
 +void git_inflate_end(git_zstream *);
 +int git_inflate(git_zstream *, int flush);
 +
 +void git_deflate_init(git_zstream *, int level);
 +void git_deflate_init_gzip(git_zstream *, int level);
 +void git_deflate_end(git_zstream *);
 +int git_deflate_end_gently(git_zstream *);
 +int git_deflate(git_zstream *, int flush);
 +unsigned long git_deflate_bound(git_zstream *, unsigned long);
  
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
  #define DTYPE(de)     ((de)->d_type)
@@@ -394,6 -379,7 +394,7 @@@ static inline enum object_type object_t
  }
  
  #define GIT_DIR_ENVIRONMENT "GIT_DIR"
+ #define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
@@@ -434,6 -420,8 +435,8 @@@ extern char *get_object_directory(void)
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
  extern int set_git_dir(const char *path);
+ extern const char *get_git_namespace(void);
+ extern const char *strip_namespace(const char *namespaced_ref);
  extern const char *get_git_work_tree(void);
  extern const char *read_gitfile_gently(const char *path);
  extern void set_git_work_tree(const char *tree);
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
  extern const char **get_pathspec(const char *prefix, const char **pathspec);
 +extern const char *pathspec_prefix(const char *prefix, const char **pathspec);
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
@@@ -534,11 -521,8 +537,11 @@@ struct pathspec 
  extern int init_pathspec(struct pathspec *, const char **);
  extern void free_pathspec(struct pathspec *);
  extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
 -extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path, int format_check);
 -extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
 +
 +#define HASH_WRITE_OBJECT 1
 +#define HASH_FORMAT_CHECK 2
 +extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 +extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
  #define REFRESH_REALLY                0x0001  /* ignore_valid */
@@@ -598,6 -582,35 +601,6 @@@ extern int fsync_object_files
  extern int core_preload_index;
  extern int core_apply_sparse_checkout;
  
 -enum safe_crlf {
 -      SAFE_CRLF_FALSE = 0,
 -      SAFE_CRLF_FAIL = 1,
 -      SAFE_CRLF_WARN = 2
 -};
 -
 -extern enum safe_crlf safe_crlf;
 -
 -enum auto_crlf {
 -      AUTO_CRLF_FALSE = 0,
 -      AUTO_CRLF_TRUE = 1,
 -      AUTO_CRLF_INPUT = -1
 -};
 -
 -extern enum auto_crlf auto_crlf;
 -
 -enum eol {
 -      EOL_UNSET,
 -      EOL_CRLF,
 -      EOL_LF,
 -#ifdef NATIVE_CRLF
 -      EOL_NATIVE = EOL_CRLF
 -#else
 -      EOL_NATIVE = EOL_LF
 -#endif
 -};
 -
 -extern enum eol core_eol;
 -
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
@@@ -734,7 -747,7 +737,7 @@@ extern char *expand_user_path(const cha
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
  {
 -      return path[0] == '/' || has_dos_drive_prefix(path);
 +      return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
  }
  int is_directory(const char *);
  const char *real_path(const char *path);
@@@ -767,16 -780,10 +770,16 @@@ extern int hash_sha1_file(const void *b
  extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 +extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
 +extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
 +extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
  
  /* global flag to enable extra checks when accessing packed objects */
  extern int do_check_packed_object_crc;
  
 +/* for development: log offset of pack access */
 +extern const char *log_pack_access;
 +
  extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
  
  extern int move_temp_to_file(const char *tmpfile, const char *filename);
@@@ -1002,7 -1009,7 +1005,7 @@@ extern struct packed_git *find_sha1_pac
  extern void pack_report(void);
  extern int open_pack_index(struct packed_git *);
  extern void close_pack_index(struct packed_git *);
 -extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
 +extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
  extern void close_pack_windows(struct packed_git *);
  extern void unuse_pack(struct pack_window **);
  extern void free_pack_by_name(const char *);
@@@ -1014,36 -1021,7 +1017,36 @@@ extern off_t find_pack_entry_one(const 
  extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
  extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
  extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
 -extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
 +extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
 +
 +struct object_info {
 +      /* Request */
 +      unsigned long *sizep;
 +
 +      /* Response */
 +      enum {
 +              OI_CACHED,
 +              OI_LOOSE,
 +              OI_PACKED,
 +              OI_DBCACHED
 +      } whence;
 +      union {
 +              /*
 +               * struct {
 +               *      ... Nothing to expose in this case
 +               * } cached;
 +               * struct {
 +               *      ... Nothing to expose in this case
 +               * } loose;
 +               */
 +              struct {
 +                      struct packed_git *pack;
 +                      off_t offset;
 +                      unsigned int is_delta;
 +              } packed;
 +      } u;
 +};
 +extern int sha1_object_info_extended(const unsigned char *, struct object_info *);
  
  /* Dumb servers support */
  extern int update_server_info(int);
@@@ -1062,6 -1040,8 +1065,6 @@@ typedef int (*config_fn_t)(const char *
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
  extern void git_config_push_parameter(const char *text);
 -extern int git_config_parse_parameter(const char *text);
 -extern int git_config_parse_environment(void);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
  extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
@@@ -1085,8 -1065,6 +1088,8 @@@ extern int config_error_nonbool(const c
  extern const char *get_log_output_encoding(void);
  extern const char *get_commit_output_encoding(void);
  
 +extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 +
  extern const char *config_exclusive_filename;
  
  #define MAX_GITNAME (1000)
@@@ -1153,6 -1131,13 +1156,6 @@@ extern void trace_strbuf(const char *ke
  
  void packet_trace_identity(const char *prog);
  
 -/* convert.c */
 -/* returns 1 if *dst was used */
 -extern int convert_to_git(const char *path, const char *src, size_t len,
 -                          struct strbuf *dst, enum safe_crlf checksafe);
 -extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
 -extern int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst);
 -
  /* add */
  /*
   * return 0 if success, 1 - if addition of a file failed and
index 5a8309076dc633f431797c4bd59876490b0d7408,9b426ff76c491eeed9cf6e6d36b8e17b6eb56c12..8648a36e7b8239e969dc8971f451431c298e405e
@@@ -1441,9 -1441,8 +1441,9 @@@ _git_grep (
                __gitcomp "
                        --cached
                        --text --ignore-case --word-regexp --invert-match
 -                      --full-name
 +                      --full-name --line-number
                        --extended-regexp --basic-regexp --fixed-strings
 +                      --perl-regexp
                        --files-with-matches --name-only
                        --files-without-match
                        --max-depth
@@@ -1469,7 -1468,7 +1469,7 @@@ _git_help (
        __gitcomp "$__git_all_commands $(__git_aliases)
                attributes cli core-tutorial cvs-migration
                diffcore gitk glossary hooks ignore modules
-               repository-layout tutorial tutorial-2
+               namespaces repository-layout tutorial tutorial-2
                workflows
                "
  }
@@@ -2058,7 -2057,7 +2058,7 @@@ _git_config (
                color.ui
                commit.status
                commit.template
 -              core.abbrevguard
 +              core.abbrev
                core.askpass
                core.attributesfile
                core.autocrlf
@@@ -2640,6 -2639,7 +2640,7 @@@ _git (
                        --exec-path
                        --html-path
                        --work-tree=
+                       --namespace=
                        --help
                        "
                        ;;
diff --combined environment.c
index 19351024f5ef61ba0e5237aa039fc9e657ad81a0,aad274b37de33180ed49c16991367e93da259d36..03d29e8d48f46428c4efdacac84e6a0e1ddf4a0b
@@@ -8,6 -8,7 +8,7 @@@
   * are.
   */
  #include "cache.h"
+ #include "refs.h"
  
  char git_default_email[MAX_GITNAME];
  char git_default_name[MAX_GITNAME];
@@@ -36,7 -37,6 +37,7 @@@ size_t packed_git_window_size = DEFAULT
  size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
  size_t delta_base_cache_limit = 16 * 1024 * 1024;
  unsigned long big_file_threshold = 512 * 1024 * 1024;
 +const char *log_pack_access;
  const char *pager_program;
  int pager_use_color = 1;
  const char *editor_program;
@@@ -66,6 -66,9 +67,9 @@@ int core_preload_index = 0
  char *git_work_tree_cfg;
  static char *work_tree;
  
+ static const char *namespace;
+ static size_t namespace_len;
  static const char *git_dir;
  static char *git_object_dir, *git_index_file, *git_graft_file;
  
@@@ -87,6 -90,27 +91,27 @@@ const char * const local_repo_env[LOCAL
        NULL
  };
  
+ static char *expand_namespace(const char *raw_namespace)
+ {
+       struct strbuf buf = STRBUF_INIT;
+       struct strbuf **components, **c;
+       if (!raw_namespace || !*raw_namespace)
+               return xstrdup("");
+       strbuf_addstr(&buf, raw_namespace);
+       components = strbuf_split(&buf, '/');
+       strbuf_reset(&buf);
+       for (c = components; *c; c++)
+               if (strcmp((*c)->buf, "/") != 0)
+                       strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf);
+       strbuf_list_free(components);
+       if (check_ref_format(buf.buf) != CHECK_REF_FORMAT_OK)
+               die("bad git namespace path \"%s\"", raw_namespace);
+       strbuf_addch(&buf, '/');
+       return strbuf_detach(&buf, NULL);
+ }
  static void setup_git_env(void)
  {
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
                git_graft_file = git_pathdup("info/grafts");
        if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
                read_replace_refs = 0;
+       namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
+       namespace_len = strlen(namespace);
  }
  
  int is_bare_repository(void)
@@@ -132,6 -158,20 +159,20 @@@ const char *get_git_dir(void
        return git_dir;
  }
  
+ const char *get_git_namespace(void)
+ {
+       if (!namespace)
+               setup_git_env();
+       return namespace;
+ }
+ const char *strip_namespace(const char *namespaced_ref)
+ {
+       if (prefixcmp(namespaced_ref, get_git_namespace()) != 0)
+               return NULL;
+       return namespaced_ref + namespace_len;
+ }
  static int git_work_tree_initialized;
  
  /*
diff --combined git.c
index 8828c18d6cce99b8becfbb510fcc9b2b752598ca,ad95efe5b3877f75dcd1e0ff3ae41c05aba7bb5b..304522b5324da5238a027a7bac5e665f1a907005
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -7,8 -7,8 +7,8 @@@
  
  const char git_usage_string[] =
        "git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
-       "           [-p|--paginate|--no-pager] [--no-replace-objects]\n"
-       "           [--bare] [--git-dir=<path>] [--work-tree=<path>]\n"
+       "           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\n"
+       "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
        "           [-c name=value] [--help]\n"
        "           <command> [<args>]";
  
@@@ -66,7 -66,7 +66,7 @@@ static void commit_pager_choice(void) 
  
  static int handle_options(const char ***argv, int *argc, int *envchanged)
  {
 -      int handled = 0;
 +      const char **orig_argv = *argv;
  
        while (*argc > 0) {
                const char *cmd = (*argv)[0];
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
 -                      handled++;
                } else if (!prefixcmp(cmd, "--git-dir=")) {
                        setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
                        if (envchanged)
                                *envchanged = 1;
+               } else if (!strcmp(cmd, "--namespace")) {
+                       if (*argc < 2) {
+                               fprintf(stderr, "No namespace given for --namespace.\n" );
+                               usage(git_usage_string);
+                       }
+                       setenv(GIT_NAMESPACE_ENVIRONMENT, (*argv)[1], 1);
+                       if (envchanged)
+                               *envchanged = 1;
+                       (*argv)++;
+                       (*argc)--;
+               } else if (!prefixcmp(cmd, "--namespace=")) {
+                       setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
+                       if (envchanged)
+                               *envchanged = 1;
                } else if (!strcmp(cmd, "--work-tree")) {
                        if (*argc < 2) {
                                fprintf(stderr, "No directory given for --work-tree.\n" );
  
                (*argv)++;
                (*argc)--;
 -              handled++;
        }
 -      return handled;
 +      return (*argv) - orig_argv;
  }
  
  static int handle_alias(int *argcp, const char ***argv)
                if (alias_string[0] == '!') {
                        const char **alias_argv;
                        int argc = *argcp, i;
 -                      struct strbuf sb = STRBUF_INIT;
 -                      const char *env[2];
  
                        commit_pager_choice();
  
                                alias_argv[i] = (*argv)[i];
                        alias_argv[argc] = NULL;
  
 -                      strbuf_addstr(&sb, "GIT_PREFIX=");
 -                      if (subdir)
 -                              strbuf_addstr(&sb, subdir);
 -                      env[0] = sb.buf;
 -                      env[1] = NULL;
 -                      ret = run_command_v_opt_cd_env(alias_argv, RUN_USING_SHELL, NULL, env);
 -                      strbuf_release(&sb);
 +                      ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
                        if (ret >= 0)   /* normal exit */
                                exit(ret);
  
diff --combined refs.c
index 3a8789d3857d17a3a0a94ba2750e9f22857b8667,e83bbf804ad004437dbfd27f16e2e1f089bdd9fa..6f313a9e0cdec2400a993cf7c3dfa87529a493ae
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -584,7 -584,7 +584,7 @@@ int read_ref(const char *ref, unsigned 
  static int do_one_ref(const char *base, each_ref_fn fn, int trim,
                      int flags, void *cb_data, struct ref_list *entry)
  {
-       if (strncmp(base, entry->name, trim))
+       if (prefixcmp(entry->name, base))
                return 0;
  
        if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
@@@ -728,12 -728,12 +728,12 @@@ int head_ref_submodule(const char *subm
  
  int for_each_ref(each_ref_fn fn, void *cb_data)
  {
-       return do_for_each_ref(NULL, "refs/", fn, 0, 0, cb_data);
+       return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
  }
  
  int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
  {
-       return do_for_each_ref(submodule, "refs/", fn, 0, 0, cb_data);
+       return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
  }
  
  int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
@@@ -782,6 -782,31 +782,31 @@@ int for_each_replace_ref(each_ref_fn fn
        return do_for_each_ref(NULL, "refs/replace/", fn, 13, 0, cb_data);
  }
  
+ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
+ {
+       struct strbuf buf = STRBUF_INIT;
+       int ret = 0;
+       unsigned char sha1[20];
+       int flag;
+       strbuf_addf(&buf, "%sHEAD", get_git_namespace());
+       if (resolve_ref(buf.buf, sha1, 1, &flag))
+               ret = fn(buf.buf, sha1, flag, cb_data);
+       strbuf_release(&buf);
+       return ret;
+ }
+ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
+ {
+       struct strbuf buf = STRBUF_INIT;
+       int ret;
+       strbuf_addf(&buf, "%srefs/", get_git_namespace());
+       ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
+       strbuf_release(&buf);
+       return ret;
+ }
  int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
        const char *prefix, void *cb_data)
  {
@@@ -819,7 -844,7 +844,7 @@@ int for_each_glob_ref(each_ref_fn fn, c
  
  int for_each_rawref(each_ref_fn fn, void *cb_data)
  {
-       return do_for_each_ref(NULL, "refs/", fn, 0,
+       return do_for_each_ref(NULL, "", fn, 0,
                               DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
  }
  
@@@ -1451,7 -1476,7 +1476,7 @@@ int write_ref_sha1(struct ref_lock *loc
        }
        o = parse_object(sha1);
        if (!o) {
 -              error("Trying to write ref %s with nonexistant object %s",
 +              error("Trying to write ref %s with nonexistent object %s",
                        lock->ref_name, sha1_to_hex(sha1));
                unlock_ref(lock);
                return -1;
@@@ -1826,12 -1851,6 +1851,12 @@@ int update_ref(const char *action, cons
        return 0;
  }
  
 +int ref_exists(char *refname)
 +{
 +      unsigned char sha1[20];
 +      return !!resolve_ref(refname, sha1, 1, NULL);
 +}
 +
  struct ref *find_ref_by_name(const struct ref *list, const char *name)
  {
        for ( ; list; list = list->next)
diff --combined refs.h
index 5de06e57e7a9644a7dd51832552e9d1afa53c8cd,9a5c9e02fbe2216f17a07e4059f08c766cc82e3e..dfb086e93346aa3fb6f600b2ed07b5736716bf9c
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -36,6 -36,9 +36,9 @@@ extern int for_each_tag_ref_submodule(c
  extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
  extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
  
+ extern int head_ref_namespaced(each_ref_fn fn, void *cb_data);
+ extern int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
  static inline const char *has_glob_specials(const char *pattern)
  {
        return strpbrk(pattern, "?*[");
@@@ -54,7 -57,6 +57,7 @@@ extern void warn_dangling_symref(FILE *
   */
  extern void add_extra_ref(const char *refname, const unsigned char *sha1, int flags);
  extern void clear_extra_refs(void);
 +extern int ref_exists(char *);
  
  extern int peel_ref(const char *, unsigned char *);