Merge tag 'v2.10.4' into maint-2.11
authorJunio C Hamano <gitster@pobox.com>
Sun, 30 Jul 2017 22:01:31 +0000 (15:01 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 30 Jul 2017 22:01:31 +0000 (15:01 -0700)
Git 2.10.4

1  2 
cache.h
connect.c
path.c

diff --combined cache.h
+++ b/cache.h
@@@ -173,7 -173,7 +173,7 @@@ struct cache_entry 
        unsigned int ce_flags;
        unsigned int ce_namelen;
        unsigned int index;     /* for link extension */
 -      unsigned char sha1[20];
 +      struct object_id oid;
        char name[FLEX_ARRAY]; /* more */
  };
  
@@@ -409,7 -409,6 +409,7 @@@ static inline enum object_type object_t
  #define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
 +#define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
  #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
  #define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
  #define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
  #define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
 +#define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
  
  /*
   * This environment variable is expected to contain a boolean indicating
   */
  extern const char * const local_repo_env[];
  
 +/*
 + * Returns true iff we have a configured git repository (either via
 + * setup_git_directory, or in the environment via $GIT_DIR).
 + */
 +int have_git_dir(void);
 +
  extern int is_bare_repository_cfg;
  extern int is_bare_repository(void);
  extern int is_inside_git_dir(void);
@@@ -477,7 -469,6 +477,7 @@@ extern int get_common_dir_noenv(struct 
  extern int get_common_dir(struct strbuf *sb, const char *gitdir);
  extern const char *get_git_namespace(void);
  extern const char *strip_namespace(const char *namespaced_ref);
 +extern const char *get_super_prefix(void);
  extern const char *get_git_work_tree(void);
  
  /*
@@@ -529,10 -520,9 +529,10 @@@ extern void verify_non_filename(const c
  extern int path_inside_repo(const char *prefix, const char *path);
  
  #define INIT_DB_QUIET 0x0001
 +#define INIT_DB_EXIST_OK 0x0002
  
 -extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
 -extern int init_db(const char *template_dir, unsigned int flags);
 +extern int init_db(const char *git_dir, const char *real_git_dir,
 +                 const char *template_dir, unsigned int flags);
  
  extern void sanitize_stdfds(void);
  extern int daemonize(void);
@@@ -575,26 -565,7 +575,26 @@@ extern int verify_path(const char *path
  extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
  extern void adjust_dirname_case(struct index_state *istate, char *name);
  extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 +
 +/*
 + * Searches for an entry defined by name and namelen in the given index.
 + * If the return value is positive (including 0) it is the position of an
 + * exact match. If the return value is negative, the negated value minus 1
 + * is the position where the entry would be inserted.
 + * Example: The current index consists of these files and its stages:
 + *
 + *   b#0, d#0, f#1, f#3
 + *
 + * index_name_pos(&index, "a", 1) -> -1
 + * index_name_pos(&index, "b", 1) ->  0
 + * index_name_pos(&index, "c", 1) -> -2
 + * index_name_pos(&index, "d", 1) ->  1
 + * index_name_pos(&index, "e", 1) -> -3
 + * index_name_pos(&index, "f", 1) -> -3
 + * index_name_pos(&index, "g", 1) -> -5
 + */
  extern int index_name_pos(const struct index_state *, const char *name, int namelen);
 +
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
  #define ADD_CACHE_SKIP_DFCHECK 4      /* Ok to skip DF conflict checks */
  #define ADD_CACHE_KEEP_CACHE_TREE 32  /* Do not invalidate cache-tree */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 +
 +/* Remove entry, return true if there are more entries to go. */
  extern int remove_index_entry_at(struct index_state *, int pos);
 +
  extern void remove_marked_cache_entries(struct index_state *istate);
  extern int remove_file_from_index(struct index_state *, const char *path);
  #define ADD_CACHE_VERBOSE 1
  #define ADD_CACHE_IGNORE_ERRORS       4
  #define ADD_CACHE_IGNORE_REMOVAL 8
  #define ADD_CACHE_INTENT 16
 +/*
 + * These two are used to add the contents of the file at path
 + * to the index, marking the working tree up-to-date by storing
 + * the cached stat info in the resulting cache entry.  A caller
 + * that has already run lstat(2) on the path can call
 + * add_to_index(), and all others can call add_file_to_index();
 + * the latter will do necessary lstat(2) internally before
 + * calling the former.
 + */
  extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
  extern int add_file_to_index(struct index_state *, const char *path, int flags);
 +
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
  extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
@@@ -702,22 -660,15 +702,22 @@@ extern const char *git_attributes_file
  extern const char *git_hooks_path;
  extern int zlib_compression_level;
  extern int core_compression_level;
 -extern int core_compression_seen;
 +extern int pack_compression_level;
  extern size_t packed_git_window_size;
  extern size_t packed_git_limit;
  extern size_t delta_base_cache_limit;
  extern unsigned long big_file_threshold;
  extern unsigned long pack_size_limit_cfg;
  
 +/*
 + * Accessors for the core.sharedrepository config which lazy-load the value
 + * from the config (if not already set). The "reset" function can be
 + * used to unset "set" or cached value, meaning that the value will be loaded
 + * fresh from the config file on the next call to get_shared_repository().
 + */
  void set_shared_repository(int value);
  int get_shared_repository(void);
 +void reset_shared_repository(void);
  
  /*
   * Do replace refs need to be checked this run?  This variable is
@@@ -870,8 -821,8 +870,8 @@@ extern void strbuf_git_common_path(stru
        __attribute__((format (printf, 2, 3)));
  extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 -extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
 -                                    const char *fmt, ...)
 +extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
 +                                   const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
  extern char *git_pathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
@@@ -935,8 -886,8 +935,8 @@@ extern char *sha1_pack_index_name(cons
   * The result will be at least `len` characters long, and will be NUL
   * terminated.
   *
 - * The non-`_r` version returns a static buffer which will be overwritten by
 - * subsequent calls.
 + * The non-`_r` version returns a static buffer which remains valid until 4
 + * more calls to find_unique_abbrev are made.
   *
   * The `_r` variant writes to a buffer supplied by the caller, which must be at
   * least `GIT_SHA1_HEXSZ + 1` bytes. The return value is the number of bytes
@@@ -1004,39 -955,22 +1004,39 @@@ static inline void oidclr(struct object
  #define EMPTY_TREE_SHA1_BIN_LITERAL \
         "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
         "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
 -#define EMPTY_TREE_SHA1_BIN \
 -       ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
 +extern const struct object_id empty_tree_oid;
 +#define EMPTY_TREE_SHA1_BIN (empty_tree_oid.hash)
  
  #define EMPTY_BLOB_SHA1_HEX \
        "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
  #define EMPTY_BLOB_SHA1_BIN_LITERAL \
        "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
        "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
 -#define EMPTY_BLOB_SHA1_BIN \
 -      ((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
 +extern const struct object_id empty_blob_oid;
 +#define EMPTY_BLOB_SHA1_BIN (empty_blob_oid.hash)
 +
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
        return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
  }
  
 +static inline int is_empty_blob_oid(const struct object_id *oid)
 +{
 +      return !hashcmp(oid->hash, EMPTY_BLOB_SHA1_BIN);
 +}
 +
 +static inline int is_empty_tree_sha1(const unsigned char *sha1)
 +{
 +      return !hashcmp(sha1, EMPTY_TREE_SHA1_BIN);
 +}
 +
 +static inline int is_empty_tree_oid(const struct object_id *oid)
 +{
 +      return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
 +}
 +
 +
  int git_mkstemp(char *path, size_t n, const char *template);
  
  /* set default permissions by passing mode arguments to open(2) */
@@@ -1108,6 -1042,14 +1108,14 @@@ char *strip_path_suffix(const char *pat
  int daemon_avoid_alias(const char *path);
  extern int is_ntfs_dotgit(const char *name);
  
+ /*
+  * Returns true iff "str" could be confused as a command-line option when
+  * passed to a sub-program like "ssh". Note that this has nothing to do with
+  * shell-quoting, which should be handled separately; we're assuming here that
+  * the string makes it verbatim to the sub-program.
+  */
+ int looks_like_command_line_option(const char *str);
  /**
   * Return a newly allocated string with the evaluation of
   * "$XDG_CONFIG_HOME/git/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
@@@ -1157,7 -1099,7 +1165,7 @@@ extern int write_sha1_file(const void *
  extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
  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 int git_open_noatime(const char *name);
 +extern int git_open(const char *name);
  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);
@@@ -1222,9 -1164,6 +1230,9 @@@ static inline int hex2chr(const char *s
  #define MINIMUM_ABBREV minimum_abbrev
  #define DEFAULT_ABBREV default_abbrev
  
 +/* used when the code does not know or care what the default abbrev is */
 +#define FALLBACK_DEFAULT_ABBREV 7
 +
  struct object_context {
        unsigned char tree[20];
        char path[PATH_MAX];
  #define GET_SHA1_FOLLOW_SYMLINKS 0100
  #define GET_SHA1_ONLY_TO_DIE    04000
  
 +#define GET_SHA1_DISAMBIGUATORS \
 +      (GET_SHA1_COMMIT | GET_SHA1_COMMITTISH | \
 +      GET_SHA1_TREE | GET_SHA1_TREEISH | \
 +      GET_SHA1_BLOB)
 +
  extern int get_sha1(const char *str, unsigned char *sha1);
  extern int get_sha1_commit(const char *str, unsigned char *sha1);
  extern int get_sha1_committish(const char *str, unsigned char *sha1);
@@@ -1264,8 -1198,6 +1272,8 @@@ extern int get_oid(const char *str, str
  typedef int each_abbrev_fn(const unsigned char *sha1, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
  
 +extern int set_disambiguate_hint_config(const char *var, const char *value);
 +
  /*
   * Try to read a SHA1 in hexadecimal format from the 40 characters
   * starting at hex.  Write the 20-byte result to sha1 in binary form.
@@@ -1293,7 -1225,7 +1301,7 @@@ extern char *sha1_to_hex(const unsigne
  extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
  
  extern int interpret_branch_name(const char *str, int len, struct strbuf *);
 -extern int get_sha1_mb(const char *str, unsigned char *sha1);
 +extern int get_oid_mb(const char *str, struct object_id *oid);
  
  extern int validate_headref(const char *ref);
  
@@@ -1403,7 -1335,6 +1411,7 @@@ struct checkout 
                 not_new:1,
                 refresh_cache:1;
  };
 +#define CHECKOUT_INIT { NULL, "" }
  
  #define TEMPORARY_FILENAME_LENGTH 25
  extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
@@@ -1429,46 -1360,15 +1437,46 @@@ extern void remove_scheduled_dirs(void)
  
  extern struct alternate_object_database {
        struct alternate_object_database *next;
 -      char *name;
 -      char base[FLEX_ARRAY]; /* more */
 +
 +      /* see alt_scratch_buf() */
 +      struct strbuf scratch;
 +      size_t base_len;
 +
 +      char path[FLEX_ARRAY];
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
  extern void read_info_alternates(const char * relative_base, int depth);
 -extern void add_to_alternates_file(const char *reference);
 +extern char *compute_alternate_path(const char *path, struct strbuf *err);
  typedef int alt_odb_fn(struct alternate_object_database *, void *);
  extern int foreach_alt_odb(alt_odb_fn, void*);
  
 +/*
 + * Allocate a "struct alternate_object_database" but do _not_ actually
 + * add it to the list of alternates.
 + */
 +struct alternate_object_database *alloc_alt_odb(const char *dir);
 +
 +/*
 + * Add the directory to the on-disk alternates file; the new entry will also
 + * take effect in the current process.
 + */
 +extern void add_to_alternates_file(const char *dir);
 +
 +/*
 + * Add the directory to the in-memory list of alternates (along with any
 + * recursive alternates it points to), but do not modify the on-disk alternates
 + * file.
 + */
 +extern void add_to_alternates_memory(const char *dir);
 +
 +/*
 + * Returns a scratch strbuf pre-filled with the alternate object directory,
 + * including a trailing slash, which can be used to access paths in the
 + * alternate. Always use this over direct access to alt->scratch, as it
 + * cleans up any previous use of the scratch buffer.
 + */
 +extern struct strbuf *alt_scratch_buf(struct alternate_object_database *alt);
 +
  struct pack_window {
        struct pack_window *next;
        unsigned char *base;
@@@ -1525,12 -1425,6 +1533,12 @@@ extern void prepare_packed_git(void)
  extern void reprepare_packed_git(void);
  extern void install_packed_git(struct packed_git *pack);
  
 +/*
 + * Give a rough count of objects in the repository. This sacrifices accuracy
 + * for speed.
 + */
 +unsigned long approximate_object_count(void);
 +
  extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
                                         struct packed_git *packs);
  
@@@ -1677,15 -1571,7 +1685,15 @@@ struct object_info 
                } packed;
        } u;
  };
 +
 +/*
 + * Initializer for a "struct object_info" that wants no items. You may
 + * also memset() the memory to all-zeroes.
 + */
 +#define OBJECT_INFO_INIT {NULL}
 +
  extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 +extern int packed_object_info(struct packed_git *pack, off_t offset, struct object_info *);
  
  /* Dumb servers support */
  extern int update_server_info(int);
@@@ -1920,6 -1806,7 +1928,6 @@@ extern void write_file(const char *path
  
  /* pager.c */
  extern void setup_pager(void);
 -extern const char *pager_program;
  extern int pager_in_use(void);
  extern int pager_use_color;
  extern int term_columns(void);
diff --combined connect.c
+++ b/connect.c
@@@ -43,14 -43,14 +43,14 @@@ int check_ref_type(const struct ref *re
        return check_ref(ref->name, flags);
  }
  
 -static void die_initial_contact(int got_at_least_one_head)
 +static void die_initial_contact(int unexpected)
  {
 -      if (got_at_least_one_head)
 -              die("The remote end hung up upon initial contact");
 +      if (unexpected)
 +              die(_("The remote end hung up upon initial contact"));
        else
 -              die("Could not read from remote repository.\n\n"
 -                  "Please make sure you have the correct access rights\n"
 -                  "and the repository exists.");
 +              die(_("Could not read from remote repository.\n\n"
 +                    "Please make sure you have the correct access rights\n"
 +                    "and the repository exists."));
  }
  
  static void parse_one_symref_info(struct string_list *symref, const char *val, int len)
@@@ -115,18 -115,10 +115,18 @@@ struct ref **get_remote_heads(int in, c
                              struct sha1_array *shallow_points)
  {
        struct ref **orig_list = list;
 -      int got_at_least_one_head = 0;
 +
 +      /*
 +       * A hang-up after seeing some response from the other end
 +       * means that it is unexpected, as we know the other end is
 +       * willing to talk to us.  A hang-up before seeing any
 +       * response does not necessarily mean an ACL problem, though.
 +       */
 +      int saw_response;
 +      int got_dummy_ref_with_capabilities_declaration = 0;
  
        *list = NULL;
 -      for (;;) {
 +      for (saw_response = 0; ; saw_response = 1) {
                struct ref *ref;
                struct object_id old_oid;
                char *name;
                                  PACKET_READ_GENTLE_ON_EOF |
                                  PACKET_READ_CHOMP_NEWLINE);
                if (len < 0)
 -                      die_initial_contact(got_at_least_one_head);
 +                      die_initial_contact(saw_response);
  
                if (!len)
                        break;
                        continue;
                }
  
 +              if (!strcmp(name, "capabilities^{}")) {
 +                      if (saw_response)
 +                              die("protocol error: unexpected capabilities^{}");
 +                      if (got_dummy_ref_with_capabilities_declaration)
 +                              die("protocol error: multiple capabilities^{}");
 +                      got_dummy_ref_with_capabilities_declaration = 1;
 +                      continue;
 +              }
 +
                if (!check_ref(name, flags))
                        continue;
 +
 +              if (got_dummy_ref_with_capabilities_declaration)
 +                      die("protocol error: unexpected ref after capabilities^{}");
 +
                ref = alloc_ref(buffer + GIT_SHA1_HEXSZ + 1);
                oidcpy(&ref->old_oid, &old_oid);
                *list = ref;
                list = &ref->next;
 -              got_at_least_one_head = 1;
        }
  
        annotate_refs_with_symref_info(*orig_list);
@@@ -577,6 -557,11 +577,11 @@@ static struct child_process *git_proxy_
  
        get_host_and_port(&host, &port);
  
+       if (looks_like_command_line_option(host))
+               die("strange hostname '%s' blocked", host);
+       if (looks_like_command_line_option(port))
+               die("strange port '%s' blocked", port);
        proxy = xmalloc(sizeof(*proxy));
        child_process_init(proxy);
        argv_array_push(&proxy->args, git_proxy_command);
@@@ -750,7 -735,7 +755,7 @@@ struct child_process *git_connect(int f
                 * Note: Do not add any other headers here!  Doing so
                 * will cause older git-daemon servers to crash.
                 */
 -              packet_write(fd[1],
 +              packet_write_fmt(fd[1],
                             "%s %s%chost=%s%c",
                             prog, path, 0,
                             target_host, 0);
                conn = xmalloc(sizeof(*conn));
                child_process_init(conn);
  
+               if (looks_like_command_line_option(path))
+                       die("strange pathname '%s' blocked", path);
                strbuf_addstr(&cmd, prog);
                strbuf_addch(&cmd, ' ');
                sq_quote_buf(&cmd, path);
                                return NULL;
                        }
  
+                       if (looks_like_command_line_option(ssh_host))
+                               die("strange hostname '%s' blocked", ssh_host);
                        ssh = get_ssh_command();
                        if (!ssh) {
                                const char *base;
diff --combined path.c
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -6,7 -6,6 +6,7 @@@
  #include "string-list.h"
  #include "dir.h"
  #include "worktree.h"
 +#include "submodule-config.h"
  
  static int get_st_mode_bits(const char *path, int *mode)
  {
@@@ -470,16 -469,12 +470,16 @@@ const char *worktree_git_path(const str
        return pathname->buf;
  }
  
 -static void do_submodule_path(struct strbuf *buf, const char *path,
 -                            const char *fmt, va_list args)
 +/* Returns 0 on success, negative on failure. */
 +#define SUBMODULE_PATH_ERR_NOT_CONFIGURED -1
 +static int do_submodule_path(struct strbuf *buf, const char *path,
 +                           const char *fmt, va_list args)
  {
        const char *git_dir;
        struct strbuf git_submodule_common_dir = STRBUF_INIT;
        struct strbuf git_submodule_dir = STRBUF_INIT;
 +      const struct submodule *sub;
 +      int err = 0;
  
        strbuf_addstr(buf, path);
        strbuf_complete(buf, '/');
                strbuf_reset(buf);
                strbuf_addstr(buf, git_dir);
        }
 +      if (!is_git_directory(buf->buf)) {
 +              gitmodules_config();
 +              sub = submodule_from_path(null_sha1, path);
 +              if (!sub) {
 +                      err = SUBMODULE_PATH_ERR_NOT_CONFIGURED;
 +                      goto cleanup;
 +              }
 +              strbuf_reset(buf);
 +              strbuf_git_path(buf, "%s/%s", "modules", sub->name);
 +      }
 +
        strbuf_addch(buf, '/');
        strbuf_addbuf(&git_submodule_dir, buf);
  
  
        strbuf_cleanup_path(buf);
  
 +cleanup:
        strbuf_release(&git_submodule_dir);
        strbuf_release(&git_submodule_common_dir);
 +
 +      return err;
  }
  
  char *git_pathdup_submodule(const char *path, const char *fmt, ...)
  {
 +      int err;
        va_list args;
        struct strbuf buf = STRBUF_INIT;
        va_start(args, fmt);
 -      do_submodule_path(&buf, path, fmt, args);
 +      err = do_submodule_path(&buf, path, fmt, args);
        va_end(args);
 +      if (err) {
 +              strbuf_release(&buf);
 +              return NULL;
 +      }
        return strbuf_detach(&buf, NULL);
  }
  
 -void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
 -                             const char *fmt, ...)
 +int strbuf_git_path_submodule(struct strbuf *buf, const char *path,
 +                            const char *fmt, ...)
  {
 +      int err;
        va_list args;
        va_start(args, fmt);
 -      do_submodule_path(buf, path, fmt, args);
 +      err = do_submodule_path(buf, path, fmt, args);
        va_end(args);
 +
 +      return err;
  }
  
  static void do_git_common_path(struct strbuf *buf,
@@@ -991,7 -964,7 +991,7 @@@ const char *remove_leading_path(const c
   *
   * Performs the following normalizations on src, storing the result in dst:
   * - Ensures that components are separated by '/' (Windows only)
 - * - Squashes sequences of '/'.
 + * - Squashes sequences of '/' except "//server/share" on Windows
   * - Removes "." components.
   * - Removes ".." components, and the components the precede them.
   * Returns failure (non-zero) if a ".." component appears as first path
  int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
  {
        char *dst0;
 -      int i;
 +      const char *end;
  
 -      for (i = has_dos_drive_prefix(src); i > 0; i--)
 -              *dst++ = *src++;
 +      /*
 +       * Copy initial part of absolute path: "/", "C:/", "//server/share/".
 +       */
 +      end = src + offset_1st_component(src);
 +      while (src < end) {
 +              char c = *src++;
 +              if (is_dir_sep(c))
 +                      c = '/';
 +              *dst++ = c;
 +      }
        dst0 = dst;
  
 -      if (is_dir_sep(*src)) {
 -              *dst++ = '/';
 -              while (is_dir_sep(*src))
 -                      src++;
 -      }
 +      while (is_dir_sep(*src))
 +              src++;
  
        for (;;) {
                char c = *src;
@@@ -1257,6 -1225,11 +1257,11 @@@ int is_ntfs_dotgit(const char *name
                }
  }
  
+ int looks_like_command_line_option(const char *str)
+ {
+       return str && str[0] == '-';
+ }
  char *xdg_config_home(const char *filename)
  {
        const char *home, *config_home;