Merge branch 'bc/object-id'
authorJunio C Hamano <gitster@pobox.com>
Wed, 6 May 2015 04:00:23 +0000 (21:00 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 6 May 2015 04:00:23 +0000 (21:00 -0700)
Identify parts of the code that knows that we use SHA-1 hash to
name our objects too much, and use (1) symbolic constants instead
of hardcoded 20 as byte count and/or (2) use struct object_id
instead of unsigned char [20] for object names.

* bc/object-id:
apply: convert threeway_stage to object_id
patch-id: convert to use struct object_id
commit: convert parts to struct object_id
diff: convert struct combine_diff_path to object_id
bulk-checkin.c: convert to use struct object_id
zip: use GIT_SHA1_HEXSZ for trailers
archive.c: convert to use struct object_id
bisect.c: convert leaf functions to use struct object_id
define utility functions for object IDs
define a structure for object IDs

1  2 
archive-zip.c
archive.c
builtin/apply.c
bulk-checkin.c
cache.h
commit.h
diff-lib.c
log-tree.c
send-pack.c
shallow.c
upload-pack.c
diff --combined archive-zip.c
index ffb3535e93dca2135724d998657b80c2bc4d6f20,b669e506ee45e653bd3bb858e8b8742a237b475b..ae3d67f9d310e2e34c6d2c5919eff467b951783f
@@@ -5,8 -5,6 +5,8 @@@
  #include "archive.h"
  #include "streaming.h"
  #include "utf8.h"
 +#include "userdiff.h"
 +#include "xdiff-interface.h"
  
  static int zip_date;
  static int zip_time;
@@@ -122,6 -120,7 +122,6 @@@ static void *zlib_deflate_raw(void *dat
        void *buffer;
        int result;
  
 -      memset(&stream, 0, sizeof(stream));
        git_deflate_init_raw(&stream, compression_level);
        maxsize = git_deflate_bound(&stream, size);
        buffer = xmalloc(maxsize);
@@@ -190,16 -189,6 +190,16 @@@ static int has_only_ascii(const char *s
        }
  }
  
 +static int entry_is_binary(const char *path, const void *buffer, size_t size)
 +{
 +      struct userdiff_driver *driver = userdiff_find_by_path(path);
 +      if (!driver)
 +              driver = userdiff_find_by_name("default");
 +      if (driver->binary != -1)
 +              return driver->binary;
 +      return buffer_is_binary(buffer, size);
 +}
 +
  #define STREAM_BUFFER_SIZE (1024 * 16)
  
  static int write_zip_entry(struct archiver_args *args,
        struct git_istream *stream = NULL;
        unsigned long flags = 0;
        unsigned long size;
 +      int is_binary = -1;
 +      const char *path_without_prefix = path + args->baselen;
  
        crc = crc32(0, NULL, 0);
  
                                return error("cannot read %s",
                                             sha1_to_hex(sha1));
                        crc = crc32(crc, buffer, size);
 +                      is_binary = entry_is_binary(path_without_prefix,
 +                                                  buffer, size);
                        out = buffer;
                }
                compressed_size = (method == 0) ? size : 0;
        copy_le16(dirent.extra_length, ZIP_EXTRA_MTIME_SIZE);
        copy_le16(dirent.comment_length, 0);
        copy_le16(dirent.disk, 0);
 -      copy_le16(dirent.attr1, 0);
        copy_le32(dirent.attr2, attr2);
        copy_le32(dirent.offset, zip_offset);
  
                        if (readlen <= 0)
                                break;
                        crc = crc32(crc, buf, readlen);
 +                      if (is_binary == -1)
 +                              is_binary = entry_is_binary(path_without_prefix,
 +                                                          buf, readlen);
                        write_or_die(1, buf, readlen);
                }
                close_istream(stream);
                size_t out_len;
                unsigned char compressed[STREAM_BUFFER_SIZE * 2];
  
 -              memset(&zstream, 0, sizeof(zstream));
                git_deflate_init_raw(&zstream, args->compression_level);
  
                compressed_size = 0;
                        if (readlen <= 0)
                                break;
                        crc = crc32(crc, buf, readlen);
 +                      if (is_binary == -1)
 +                              is_binary = entry_is_binary(path_without_prefix,
 +                                                          buf, readlen);
  
                        zstream.next_in = buf;
                        zstream.avail_in = readlen;
        free(deflated);
        free(buffer);
  
 +      copy_le16(dirent.attr1, !is_binary);
 +
        memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE);
        zip_dir_offset += ZIP_DIR_HEADER_SIZE;
        memcpy(zip_dir + zip_dir_offset, path, pathlen);
@@@ -448,12 -427,12 +448,12 @@@ static void write_zip_trailer(const uns
        copy_le16(trailer.entries, zip_dir_entries);
        copy_le32(trailer.size, zip_dir_offset);
        copy_le32(trailer.offset, zip_offset);
-       copy_le16(trailer.comment_length, sha1 ? 40 : 0);
+       copy_le16(trailer.comment_length, sha1 ? GIT_SHA1_HEXSZ : 0);
  
        write_or_die(1, zip_dir, zip_dir_offset);
        write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE);
        if (sha1)
-               write_or_die(1, sha1_to_hex(sha1), 40);
+               write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
  }
  
  static void dos_time(time_t *time, int *dos_date, int *dos_time)
diff --combined archive.c
index 96057ed830e521a5f12b0a73b4a04a6b5e581e87,2015f574c0b0028b84c1a0561cd8faf80a434eff..d37c41daf29b1163ac7743e5ce17cdafc9b2e8a3
+++ b/archive.c
@@@ -8,9 -8,9 +8,9 @@@
  #include "dir.h"
  
  static char const * const archive_usage[] = {
 -      N_("git archive [options] <tree-ish> [<path>...]"),
 +      N_("git archive [<options>] <tree-ish> [<path>...]"),
        N_("git archive --list"),
 -      N_("git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [<path>...]"),
 +      N_("git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> [<path>...]"),
        N_("git archive --remote <repo> [--exec <cmd>] --list"),
        NULL
  };
@@@ -101,7 -101,7 +101,7 @@@ static void setup_archive_check(struct 
  
  struct directory {
        struct directory *up;
-       unsigned char sha1[20];
+       struct object_id oid;
        int baselen, len;
        unsigned mode;
        int stage;
@@@ -177,7 -177,7 +177,7 @@@ static void queue_directory(const unsig
        d->stage   = stage;
        c->bottom  = d;
        d->len = sprintf(d->path, "%.*s%s/", (int)base->len, base->buf, filename);
-       hashcpy(d->sha1, sha1);
+       hashcpy(d->oid.hash, sha1);
  }
  
  static int write_directory(struct archiver_context *c)
        d->path[d->len - 1] = '\0'; /* no trailing slash */
        ret =
                write_directory(c) ||
-               write_archive_entry(d->sha1, d->path, d->baselen,
+               write_archive_entry(d->oid.hash, d->path, d->baselen,
                                    d->path + d->baselen, d->mode,
                                    d->stage, c) != READ_TREE_RECURSIVE;
        free(d);
@@@ -354,7 -354,7 +354,7 @@@ static void parse_treeish_arg(const cha
        time_t archive_time;
        struct tree *tree;
        const struct commit *commit;
-       unsigned char sha1[20];
+       struct object_id oid;
  
        /* Remotes are only allowed to fetch actual refs */
        if (remote && !remote_allow_unreachable) {
                const char *colon = strchrnul(name, ':');
                int refnamelen = colon - name;
  
-               if (!dwim_ref(name, refnamelen, sha1, &ref))
+               if (!dwim_ref(name, refnamelen, oid.hash, &ref))
                        die("no such ref: %.*s", refnamelen, name);
                free(ref);
        }
  
-       if (get_sha1(name, sha1))
+       if (get_sha1(name, oid.hash))
                die("Not a valid object name");
  
-       commit = lookup_commit_reference_gently(sha1, 1);
+       commit = lookup_commit_reference_gently(oid.hash, 1);
        if (commit) {
                commit_sha1 = commit->object.sha1;
                archive_time = commit->date;
                archive_time = time(NULL);
        }
  
-       tree = parse_tree_indirect(sha1);
+       tree = parse_tree_indirect(oid.hash);
        if (tree == NULL)
                die("not a tree object");
  
        if (prefix) {
-               unsigned char tree_sha1[20];
+               struct object_id tree_oid;
                unsigned int mode;
                int err;
  
                err = get_tree_entry(tree->object.sha1, prefix,
-                                    tree_sha1, &mode);
+                                    tree_oid.hash, &mode);
                if (err || !S_ISDIR(mode))
                        die("current working directory is untracked");
  
-               tree = parse_tree_indirect(tree_sha1);
+               tree = parse_tree_indirect(tree_oid.hash);
        }
        ar_args->tree = tree;
        ar_args->commit_sha1 = commit_sha1;
diff --combined builtin/apply.c
index 0769b09287b2bcf6b3f85ff503a396fa245c31a0,9ffdce7e4bcd0cc1b63e7d79c1fc69266bed5ad6..146be97a1a879242aa12eb066c5c69cd6af92409
@@@ -51,12 -51,11 +51,12 @@@ static int apply_verbosely
  static int allow_overlap;
  static int no_add;
  static int threeway;
 +static int unsafe_paths;
  static const char *fake_ancestor;
  static int line_termination = '\n';
  static unsigned int p_context = UINT_MAX;
  static const char * const apply_usage[] = {
 -      N_("git apply [options] [<patch>...]"),
 +      N_("git apply [<options>] [<patch>...]"),
        NULL
  };
  
@@@ -208,7 -207,7 +208,7 @@@ struct patch 
        struct patch *next;
  
        /* three-way fallback result */
-       unsigned char threeway_stage[3][20];
+       struct object_id threeway_stage[3];
  };
  
  static void free_fragment_list(struct fragment *list)
@@@ -1601,9 -1600,6 +1601,9 @@@ static int parse_fragment(const char *l
                        if (!deleted && !added)
                                leading++;
                        trailing++;
 +                      if (!apply_in_reverse &&
 +                          ws_error_action == correct_ws_error)
 +                              check_whitespace(line, len, patch->ws_rule);
                        break;
                case '-':
                        if (apply_in_reverse &&
@@@ -2776,8 -2772,7 +2776,8 @@@ static int apply_one_fragment(struct im
                default:
                        if (apply_verbosely)
                                error(_("invalid start of line: '%c'"), first);
 -                      return -1;
 +                      applied_pos = -1;
 +                      goto out;
                }
                if (added_blank_line) {
                        if (!new_blank_lines_at_end)
                              (int)(old - oldlines), oldlines);
        }
  
 +out:
        free(oldlines);
        strbuf_release(&newlines);
        free(preimage.line_allocated);
@@@ -3227,7 -3221,7 +3227,7 @@@ static int load_patch_target(struct str
                             const char *name,
                             unsigned expected_mode)
  {
 -      if (cached) {
 +      if (cached || check_index) {
                if (read_file_or_gitlink(ce, buf))
                        return error(_("read of %s failed"), name);
        } else if (name) {
                                return read_file_or_gitlink(ce, buf);
                        else
                                return SUBMODULE_PATCH_WITHOUT_INDEX;
 +              } else if (has_symlink_leading_path(name, strlen(name))) {
 +                      return error(_("reading from '%s' beyond a symbolic link"), name);
                } else {
                        if (read_old_data(st, name, buf))
                                return error(_("read of %s failed"), name);
@@@ -3426,11 -3418,11 +3426,11 @@@ static int try_threeway(struct image *i
        if (status) {
                patch->conflicted_threeway = 1;
                if (patch->is_new)
-                       hashclr(patch->threeway_stage[0]);
+                       oidclr(&patch->threeway_stage[0]);
                else
-                       hashcpy(patch->threeway_stage[0], pre_sha1);
-               hashcpy(patch->threeway_stage[1], our_sha1);
-               hashcpy(patch->threeway_stage[2], post_sha1);
+                       hashcpy(patch->threeway_stage[0].hash, pre_sha1);
+               hashcpy(patch->threeway_stage[1].hash, our_sha1);
+               hashcpy(patch->threeway_stage[2].hash, post_sha1);
                fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name);
        } else {
                fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name);
@@@ -3577,121 -3569,6 +3577,121 @@@ static int check_to_create(const char *
        return 0;
  }
  
 +/*
 + * We need to keep track of how symlinks in the preimage are
 + * manipulated by the patches.  A patch to add a/b/c where a/b
 + * is a symlink should not be allowed to affect the directory
 + * the symlink points at, but if the same patch removes a/b,
 + * it is perfectly fine, as the patch removes a/b to make room
 + * to create a directory a/b so that a/b/c can be created.
 + */
 +static struct string_list symlink_changes;
 +#define SYMLINK_GOES_AWAY 01
 +#define SYMLINK_IN_RESULT 02
 +
 +static uintptr_t register_symlink_changes(const char *path, uintptr_t what)
 +{
 +      struct string_list_item *ent;
 +
 +      ent = string_list_lookup(&symlink_changes, path);
 +      if (!ent) {
 +              ent = string_list_insert(&symlink_changes, path);
 +              ent->util = (void *)0;
 +      }
 +      ent->util = (void *)(what | ((uintptr_t)ent->util));
 +      return (uintptr_t)ent->util;
 +}
 +
 +static uintptr_t check_symlink_changes(const char *path)
 +{
 +      struct string_list_item *ent;
 +
 +      ent = string_list_lookup(&symlink_changes, path);
 +      if (!ent)
 +              return 0;
 +      return (uintptr_t)ent->util;
 +}
 +
 +static void prepare_symlink_changes(struct patch *patch)
 +{
 +      for ( ; patch; patch = patch->next) {
 +              if ((patch->old_name && S_ISLNK(patch->old_mode)) &&
 +                  (patch->is_rename || patch->is_delete))
 +                      /* the symlink at patch->old_name is removed */
 +                      register_symlink_changes(patch->old_name, SYMLINK_GOES_AWAY);
 +
 +              if (patch->new_name && S_ISLNK(patch->new_mode))
 +                      /* the symlink at patch->new_name is created or remains */
 +                      register_symlink_changes(patch->new_name, SYMLINK_IN_RESULT);
 +      }
 +}
 +
 +static int path_is_beyond_symlink_1(struct strbuf *name)
 +{
 +      do {
 +              unsigned int change;
 +
 +              while (--name->len && name->buf[name->len] != '/')
 +                      ; /* scan backwards */
 +              if (!name->len)
 +                      break;
 +              name->buf[name->len] = '\0';
 +              change = check_symlink_changes(name->buf);
 +              if (change & SYMLINK_IN_RESULT)
 +                      return 1;
 +              if (change & SYMLINK_GOES_AWAY)
 +                      /*
 +                       * This cannot be "return 0", because we may
 +                       * see a new one created at a higher level.
 +                       */
 +                      continue;
 +
 +              /* otherwise, check the preimage */
 +              if (check_index) {
 +                      struct cache_entry *ce;
 +
 +                      ce = cache_file_exists(name->buf, name->len, ignore_case);
 +                      if (ce && S_ISLNK(ce->ce_mode))
 +                              return 1;
 +              } else {
 +                      struct stat st;
 +                      if (!lstat(name->buf, &st) && S_ISLNK(st.st_mode))
 +                              return 1;
 +              }
 +      } while (1);
 +      return 0;
 +}
 +
 +static int path_is_beyond_symlink(const char *name_)
 +{
 +      int ret;
 +      struct strbuf name = STRBUF_INIT;
 +
 +      assert(*name_ != '\0');
 +      strbuf_addstr(&name, name_);
 +      ret = path_is_beyond_symlink_1(&name);
 +      strbuf_release(&name);
 +
 +      return ret;
 +}
 +
 +static void die_on_unsafe_path(struct patch *patch)
 +{
 +      const char *old_name = NULL;
 +      const char *new_name = NULL;
 +      if (patch->is_delete)
 +              old_name = patch->old_name;
 +      else if (!patch->is_new && !patch->is_copy)
 +              old_name = patch->old_name;
 +      if (!patch->is_delete)
 +              new_name = patch->new_name;
 +
 +      if (old_name && !verify_path(old_name))
 +              die(_("invalid path '%s'"), old_name);
 +      if (new_name && !verify_path(new_name))
 +              die(_("invalid path '%s'"), new_name);
 +}
 +
  /*
   * Check and apply the patch in-core; leave the result in patch->result
   * for the caller to write it out to the final destination.
@@@ -3779,22 -3656,6 +3779,22 @@@ static int check_patch(struct patch *pa
                }
        }
  
 +      if (!unsafe_paths)
 +              die_on_unsafe_path(patch);
 +
 +      /*
 +       * An attempt to read from or delete a path that is beyond a
 +       * symbolic link will be prevented by load_patch_target() that
 +       * is called at the beginning of apply_data() so we do not
 +       * have to worry about a patch marked with "is_delete" bit
 +       * here.  We however need to make sure that the patch result
 +       * is not deposited to a path that is beyond a symbolic link
 +       * here.
 +       */
 +      if (!patch->is_delete && path_is_beyond_symlink(patch->new_name))
 +              return error(_("affected file '%s' is beyond a symbolic link"),
 +                           patch->new_name);
 +
        if (apply_data(patch, &st, ce) < 0)
                return error(_("%s: patch does not apply"), name);
        patch->rejected = 0;
@@@ -3805,7 -3666,6 +3805,7 @@@ static int check_patch_list(struct patc
  {
        int err = 0;
  
 +      prepare_symlink_changes(patch);
        prepare_fn_table(patch);
        while (patch) {
                if (apply_verbosely)
@@@ -4186,14 -4046,14 +4186,14 @@@ static void add_conflicted_stages_file(
  
        remove_file_from_cache(patch->new_name);
        for (stage = 1; stage < 4; stage++) {
-               if (is_null_sha1(patch->threeway_stage[stage - 1]))
+               if (is_null_oid(&patch->threeway_stage[stage - 1]))
                        continue;
                ce = xcalloc(1, ce_size);
                memcpy(ce->name, patch->new_name, namelen);
                ce->ce_mode = create_ce_mode(mode);
                ce->ce_flags = create_ce_flags(stage);
                ce->ce_namelen = namelen;
-               hashcpy(ce->sha1, patch->threeway_stage[stage - 1]);
+               hashcpy(ce->sha1, patch->threeway_stage[stage - 1].hash);
                if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
                        die(_("unable to add cache entry for %s"), patch->new_name);
        }
@@@ -4544,8 -4404,6 +4544,8 @@@ int cmd_apply(int argc, const char **ar
                        N_("make sure the patch is applicable to the current index")),
                OPT_BOOL(0, "cached", &cached,
                        N_("apply a patch without touching the working tree")),
 +              OPT_BOOL(0, "unsafe-paths", &unsafe_paths,
 +                      N_("accept a patch that touches outside the working area")),
                OPT_BOOL(0, "apply", &force_apply,
                        N_("also apply the patch (use with --stat/--summary/--check)")),
                OPT_BOOL('3', "3way", &threeway,
                        die(_("--cached outside a repository"));
                check_index = 1;
        }
 +      if (check_index)
 +              unsafe_paths = 0;
 +
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
                int fd;
diff --combined bulk-checkin.c
index 8d157eba455f15a00cc7bf9b970f1862167ee1bb,c80e503728333fede3d043e4a7e60e02eec4d452..7cffc3a579e4a02c1422284092cef18cd4b7d059
@@@ -24,7 -24,7 +24,7 @@@ static struct bulk_checkin_state 
  
  static void finish_bulk_checkin(struct bulk_checkin_state *state)
  {
-       unsigned char sha1[20];
+       struct object_id oid;
        struct strbuf packname = STRBUF_INIT;
        int i;
  
                unlink(state->pack_tmp_name);
                goto clear_exit;
        } else if (state->nr_written == 1) {
-               sha1close(state->f, sha1, CSUM_FSYNC);
+               sha1close(state->f, oid.hash, CSUM_FSYNC);
        } else {
-               int fd = sha1close(state->f, sha1, 0);
-               fixup_pack_header_footer(fd, sha1, state->pack_tmp_name,
-                                        state->nr_written, sha1,
+               int fd = sha1close(state->f, oid.hash, 0);
+               fixup_pack_header_footer(fd, oid.hash, state->pack_tmp_name,
+                                        state->nr_written, oid.hash,
                                         state->offset);
                close(fd);
        }
@@@ -48,7 -48,7 +48,7 @@@
        strbuf_addf(&packname, "%s/pack/pack-", get_object_directory());
        finish_tmp_packfile(&packname, state->pack_tmp_name,
                            state->written, state->nr_written,
-                           &state->pack_idx_opts, sha1);
+                           &state->pack_idx_opts, oid.hash);
        for (i = 0; i < state->nr_written; i++)
                free(state->written[i]);
  
@@@ -105,6 -105,7 +105,6 @@@ static int stream_to_pack(struct bulk_c
        int write_object = (flags & HASH_WRITE_OBJECT);
        off_t offset = 0;
  
 -      memset(&s, 0, sizeof(s));
        git_deflate_init(&s, pack_compression_level);
  
        hdrlen = encode_in_pack_object_header(type, size, obuf);
diff --combined cache.h
index 3d3244ba647db66c4d3ed7071bcca3de77c97cf8,04cfb1f8aa17014096cd7f92db6799602b8d8e69..b320b1a94e1f7aa8bb786886c8585379ec46dfde
+++ b/cache.h
@@@ -43,6 -43,14 +43,14 @@@ int git_deflate_end_gently(git_zstream 
  int git_deflate(git_zstream *, int flush);
  unsigned long git_deflate_bound(git_zstream *, unsigned long);
  
+ /* The length in bytes and in hex digits of an object name (SHA-1 value). */
+ #define GIT_SHA1_RAWSZ 20
+ #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
+ struct object_id {
+       unsigned char hash[GIT_SHA1_RAWSZ];
+ };
  #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
  #define DTYPE(de)     ((de)->d_type)
  #else
@@@ -568,7 -576,7 +576,7 @@@ extern void update_index_if_able(struc
  extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
 -extern int delete_ref(const char *, const unsigned char *sha1, int delopt);
 +extern int delete_ref(const char *, const unsigned char *sha1, unsigned int flags);
  
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
@@@ -613,14 -621,6 +621,14 @@@ extern int precomposed_unicode
  extern int protect_hfs;
  extern int protect_ntfs;
  
 +/*
 + * Include broken refs in all ref iterations, which will
 + * generally choke dangerous operations rather than letting
 + * them silently proceed without taking the broken ref into
 + * account.
 + */
 +extern int ref_paranoia;
 +
  /*
   * The character that begins a commented line in user-editable file
   * that is subject to stripspace.
@@@ -718,13 -718,13 +726,13 @@@ extern char *sha1_pack_name(const unsig
  extern char *sha1_pack_index_name(const unsigned char *sha1);
  
  extern const char *find_unique_abbrev(const unsigned char *sha1, int);
- extern const unsigned char null_sha1[20];
+ extern const unsigned char null_sha1[GIT_SHA1_RAWSZ];
  
  static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
  {
        int i;
  
-       for (i = 0; i < 20; i++, sha1++, sha2++) {
+       for (i = 0; i < GIT_SHA1_RAWSZ; i++, sha1++, sha2++) {
                if (*sha1 != *sha2)
                        return *sha1 - *sha2;
        }
        return 0;
  }
  
+ static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
+ {
+       return hashcmp(oid1->hash, oid2->hash);
+ }
  static inline int is_null_sha1(const unsigned char *sha1)
  {
        return !hashcmp(sha1, null_sha1);
  }
  
+ static inline int is_null_oid(const struct object_id *oid)
+ {
+       return !hashcmp(oid->hash, null_sha1);
+ }
  static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
  {
-       memcpy(sha_dst, sha_src, 20);
+       memcpy(sha_dst, sha_src, GIT_SHA1_RAWSZ);
  }
+ static inline void oidcpy(struct object_id *dst, const struct object_id *src)
+ {
+       hashcpy(dst->hash, src->hash);
+ }
  static inline void hashclr(unsigned char *hash)
  {
-       memset(hash, 0, 20);
+       memset(hash, 0, GIT_SHA1_RAWSZ);
  }
  
+ static inline void oidclr(struct object_id *oid)
+ {
+       hashclr(oid->hash);
+ }
  #define EMPTY_TREE_SHA1_HEX \
        "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
  #define EMPTY_TREE_SHA1_BIN_LITERAL \
@@@ -952,8 -974,10 +982,10 @@@ extern int for_each_abbrev(const char *
   * null-terminated string.
   */
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
+ extern int get_oid_hex(const char *hex, struct object_id *sha1);
  
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
+ extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
  extern int read_ref_full(const char *refname, int resolve_flags,
                         unsigned char *sha1, int *flags);
  extern int read_ref(const char *refname, unsigned char *sha1);
@@@ -1577,6 -1601,7 +1609,6 @@@ extern int ws_blank_line(const char *li
  #define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
 -int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix);
  void overlay_tree_on_cache(const char *tree_name, const char *prefix);
  
  char *alias_lookup(const char *alias);
diff --combined commit.h
index 9f189cb054266cd8f8c084853afbb678ca56c9e9,896272abcfcc99aaf26b4d4d7d832d7e653b654c..ed3a1d59a553b498b20d28f4999effde78e51c3c
+++ b/commit.h
@@@ -226,9 -226,9 +226,9 @@@ enum rev_sort_order 
  void sort_in_topological_order(struct commit_list **, enum rev_sort_order);
  
  struct commit_graft {
-       unsigned char sha1[20];
+       struct object_id oid;
        int nr_parent; /* < 0 if shallow commit */
-       unsigned char parent[FLEX_ARRAY][20]; /* more */
+       struct object_id parent[FLEX_ARRAY]; /* more */
  };
  typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
  
@@@ -254,6 -254,7 +254,6 @@@ extern int for_each_commit_graft(each_c
  extern int is_repository_shallow(void);
  extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
 -extern void check_shallow_file_for_update(void);
  extern void set_alternate_shallow_file(const char *path, int override);
  extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
                                 const struct sha1_array *extra);
diff --combined diff-lib.c
index a85c4971ac8ece6397c05d0674bbac8c75cf219f,470dc7851fc031630254b6e3a857c73cdf520dc1..241a8435eb1f7ddb88664343a5d9c42a1951886f
@@@ -101,7 -101,6 +101,7 @@@ int run_diff_files(struct rev_info *rev
                struct cache_entry *ce = active_cache[i];
                int changed;
                unsigned dirty_submodule = 0;
 +              const unsigned char *old_sha1, *new_sha1;
  
                if (diff_can_quit_early(&revs->diffopt))
                        break;
                        dpath->next = NULL;
                        memcpy(dpath->path, ce->name, path_len);
                        dpath->path[path_len] = '\0';
-                       hashclr(dpath->sha1);
+                       oidclr(&dpath->oid);
                        memset(&(dpath->parent[0]), 0,
                               sizeof(struct combine_diff_parent)*5);
  
                                if (2 <= stage) {
                                        int mode = nce->ce_mode;
                                        num_compare_stages++;
-                                       hashcpy(dpath->parent[stage-2].sha1, nce->sha1);
+                                       hashcpy(dpath->parent[stage-2].oid.hash, nce->sha1);
                                        dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode);
                                        dpath->parent[stage-2].status =
                                                DIFF_STATUS_MODIFIED;
                                continue;
                }
                oldmode = ce->ce_mode;
 +              old_sha1 = ce->sha1;
 +              new_sha1 = changed ? null_sha1 : ce->sha1;
                diff_change(&revs->diffopt, oldmode, newmode,
 -                          ce->sha1, (changed ? null_sha1 : ce->sha1),
 -                          !is_null_sha1(ce->sha1), (changed ? 0 : !is_null_sha1(ce->sha1)),
 +                          old_sha1, new_sha1,
 +                          !is_null_sha1(old_sha1),
 +                          !is_null_sha1(new_sha1),
                            ce->name, 0, dirty_submodule);
  
        }
@@@ -339,14 -335,14 +339,14 @@@ static int show_modified(struct rev_inf
                memcpy(p->path, new->name, pathlen);
                p->path[pathlen] = 0;
                p->mode = mode;
-               hashclr(p->sha1);
+               oidclr(&p->oid);
                memset(p->parent, 0, 2 * sizeof(struct combine_diff_parent));
                p->parent[0].status = DIFF_STATUS_MODIFIED;
                p->parent[0].mode = new->ce_mode;
-               hashcpy(p->parent[0].sha1, new->sha1);
+               hashcpy(p->parent[0].oid.hash, new->sha1);
                p->parent[1].status = DIFF_STATUS_MODIFIED;
                p->parent[1].mode = old->ce_mode;
-               hashcpy(p->parent[1].sha1, old->sha1);
+               hashcpy(p->parent[1].oid.hash, old->sha1);
                show_combined_diff(p, 2, revs->dense_combined_merges, revs);
                free(p);
                return 0;
diff --combined log-tree.c
index 2c1ed0fa90170438e00a2eadbf74e15d89531613,51cc695ae5dfe284c486069c25a624840a2ce490..cf4646b0f4d78dfb9229287859d48022afc21b65
@@@ -137,7 -137,7 +137,7 @@@ static int add_ref_decoration(const cha
  
  static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
  {
-       struct commit *commit = lookup_commit(graft->sha1);
+       struct commit *commit = lookup_commit(graft->oid.hash);
        if (!commit)
                return 0;
        add_name_decoration(DECORATION_GRAFTED, "grafted", &commit->object);
@@@ -172,43 -172,6 +172,43 @@@ static void show_children(struct rev_in
        }
  }
  
 +/*
 + * Do we have HEAD in the output, and also the branch it points at?
 + * If so, find that decoration entry for that current branch.
 + */
 +static const struct name_decoration *current_pointed_by_HEAD(const struct name_decoration *decoration)
 +{
 +      const struct name_decoration *list, *head = NULL;
 +      const char *branch_name = NULL;
 +      unsigned char unused[20];
 +      int rru_flags;
 +
 +      /* First find HEAD */
 +      for (list = decoration; list; list = list->next)
 +              if (list->type == DECORATION_REF_HEAD) {
 +                      head = list;
 +                      break;
 +              }
 +      if (!head)
 +              return NULL;
 +
 +      /* Now resolve and find the matching current branch */
 +      branch_name = resolve_ref_unsafe("HEAD", 0, unused, &rru_flags);
 +      if (!(rru_flags & REF_ISSYMREF))
 +              return NULL;
 +      if (!skip_prefix(branch_name, "refs/heads/", &branch_name))
 +              return NULL;
 +
 +      /* OK, do we have that ref in the list? */
 +      for (list = decoration; list; list = list->next)
 +              if ((list->type == DECORATION_REF_LOCAL) &&
 +                  !strcmp(branch_name, list->name)) {
 +                      return list;
 +              }
 +
 +      return NULL;
 +}
 +
  /*
   * The caller makes sure there is no funny color before calling.
   * format_decorations_extended makes sure the same after return.
@@@ -221,7 -184,6 +221,7 @@@ void format_decorations_extended(struc
                        const char *suffix)
  {
        const struct name_decoration *decoration;
 +      const struct name_decoration *current_and_HEAD;
        const char *color_commit =
                diff_get_color(use_color, DIFF_COMMIT);
        const char *color_reset =
        decoration = get_name_decoration(&commit->object);
        if (!decoration)
                return;
 +
 +      current_and_HEAD = current_pointed_by_HEAD(decoration);
        while (decoration) {
 -              strbuf_addstr(sb, color_commit);
 -              strbuf_addstr(sb, prefix);
 -              strbuf_addstr(sb, decorate_get_color(use_color, decoration->type));
 -              if (decoration->type == DECORATION_REF_TAG)
 -                      strbuf_addstr(sb, "tag: ");
 -              strbuf_addstr(sb, decoration->name);
 -              strbuf_addstr(sb, color_reset);
 -              prefix = separator;
 +              /*
 +               * When both current and HEAD are there, only
 +               * show HEAD->current where HEAD would have
 +               * appeared, skipping the entry for current.
 +               */
 +              if (decoration != current_and_HEAD) {
 +                      strbuf_addstr(sb, color_commit);
 +                      strbuf_addstr(sb, prefix);
 +                      strbuf_addstr(sb, color_reset);
 +                      strbuf_addstr(sb, decorate_get_color(use_color, decoration->type));
 +                      if (decoration->type == DECORATION_REF_TAG)
 +                              strbuf_addstr(sb, "tag: ");
 +
 +                      strbuf_addstr(sb, decoration->name);
 +
 +                      if (current_and_HEAD &&
 +                          decoration->type == DECORATION_REF_HEAD) {
 +                              strbuf_addstr(sb, color_reset);
 +                              strbuf_addstr(sb, color_commit);
 +                              strbuf_addstr(sb, " -> ");
 +                              strbuf_addstr(sb, color_reset);
 +                              strbuf_addstr(sb, decorate_get_color(use_color, current_and_HEAD->type));
 +                              strbuf_addstr(sb, current_and_HEAD->name);
 +                      }
 +                      strbuf_addstr(sb, color_reset);
 +
 +                      prefix = separator;
 +              }
                decoration = decoration->next;
        }
        strbuf_addstr(sb, color_commit);
diff --combined send-pack.c
index 2e07ac3339bce870b12e0023dc985929a277ebef,fa7701b8ff9f95c3101480a677a91d0aedfbd76e..2a64fec949ea9495b5b9512399cec7979319de1c
@@@ -182,7 -182,7 +182,7 @@@ static int advertise_shallow_grafts_cb(
  {
        struct strbuf *sb = cb;
        if (graft->nr_parent == -1)
-               packet_buf_write(sb, "shallow %s\n", sha1_to_hex(graft->sha1));
+               packet_buf_write(sb, "shallow %s\n", oid_to_hex(&graft->oid));
        return 0;
  }
  
@@@ -193,13 -193,10 +193,13 @@@ static void advertise_shallow_grafts_bu
        for_each_commit_graft(advertise_shallow_grafts_cb, sb);
  }
  
 -static int ref_update_to_be_sent(const struct ref *ref, const struct send_pack_args *args)
 +#define CHECK_REF_NO_PUSH -1
 +#define CHECK_REF_STATUS_REJECTED -2
 +#define CHECK_REF_UPTODATE -3
 +static int check_to_send_update(const struct ref *ref, const struct send_pack_args *args)
  {
        if (!ref->peer_ref && !args->send_mirror)
 -              return 0;
 +              return CHECK_REF_NO_PUSH;
  
        /* Check for statuses set by set_ref_status_for_push() */
        switch (ref->status) {
        case REF_STATUS_REJECT_NEEDS_FORCE:
        case REF_STATUS_REJECT_STALE:
        case REF_STATUS_REJECT_NODELETE:
 +              return CHECK_REF_STATUS_REJECTED;
        case REF_STATUS_UPTODATE:
 -              return 0;
 +              return CHECK_REF_UPTODATE;
        default:
 -              return 1;
 +              return 0;
        }
  }
  
@@@ -257,7 -253,7 +257,7 @@@ static int generate_push_cert(struct st
        strbuf_addstr(&cert, "\n");
  
        for (ref = remote_refs; ref; ref = ref->next) {
 -              if (!ref_update_to_be_sent(ref, args))
 +              if (check_to_send_update(ref, args) < 0)
                        continue;
                update_seen = 1;
                strbuf_addf(&cert, "%s %s %s\n",
@@@ -285,51 -281,6 +285,51 @@@ free_return
        return update_seen;
  }
  
 +
 +static int atomic_push_failure(struct send_pack_args *args,
 +                             struct ref *remote_refs,
 +                             struct ref *failing_ref)
 +{
 +      struct ref *ref;
 +      /* Mark other refs as failed */
 +      for (ref = remote_refs; ref; ref = ref->next) {
 +              if (!ref->peer_ref && !args->send_mirror)
 +                      continue;
 +
 +              switch (ref->status) {
 +              case REF_STATUS_EXPECTING_REPORT:
 +                      ref->status = REF_STATUS_ATOMIC_PUSH_FAILED;
 +                      continue;
 +              default:
 +                      break; /* do nothing */
 +              }
 +      }
 +      return error("atomic push failed for ref %s. status: %d\n",
 +                   failing_ref->name, failing_ref->status);
 +}
 +
 +#define NONCE_LEN_LIMIT 256
 +
 +static void reject_invalid_nonce(const char *nonce, int len)
 +{
 +      int i = 0;
 +
 +      if (NONCE_LEN_LIMIT <= len)
 +              die("the receiving end asked to sign an invalid nonce <%.*s>",
 +                  len, nonce);
 +
 +      for (i = 0; i < len; i++) {
 +              int ch = nonce[i] & 0xFF;
 +              if (isalnum(ch) ||
 +                  ch == '-' || ch == '.' ||
 +                  ch == '/' || ch == '+' ||
 +                  ch == '=' || ch == '_')
 +                      continue;
 +              die("the receiving end asked to sign an invalid nonce <%.*s>",
 +                  len, nonce);
 +      }
 +}
 +
  int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
              struct ref *remote_refs,
        int use_sideband = 0;
        int quiet_supported = 0;
        int agent_supported = 0;
 +      int use_atomic = 0;
 +      int atomic_supported = 0;
        unsigned cmds_sent = 0;
        int ret;
        struct async demux;
                agent_supported = 1;
        if (server_supports("no-thin"))
                args->use_thin_pack = 0;
 +      if (server_supports("atomic"))
 +              atomic_supported = 1;
        if (args->push_cert) {
                int len;
  
                push_cert_nonce = server_feature_value("push-cert", &len);
                if (!push_cert_nonce)
                        die(_("the receiving end does not support --signed push"));
 +              reject_invalid_nonce(push_cert_nonce, len);
                push_cert_nonce = xmemdupz(push_cert_nonce, len);
        }
  
                        "Perhaps you should specify a branch such as 'master'.\n");
                return 0;
        }
 +      if (args->atomic && !atomic_supported)
 +              die(_("the receiving end does not support --atomic push"));
 +
 +      use_atomic = atomic_supported && args->atomic;
  
        if (status_report)
                strbuf_addstr(&cap_buf, " report-status");
                strbuf_addstr(&cap_buf, " side-band-64k");
        if (quiet_supported && (args->quiet || !args->progress))
                strbuf_addstr(&cap_buf, " quiet");
 +      if (use_atomic)
 +              strbuf_addstr(&cap_buf, " atomic");
        if (agent_supported)
                strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());
  
         * the pack data.
         */
        for (ref = remote_refs; ref; ref = ref->next) {
 -              if (!ref_update_to_be_sent(ref, args))
 +              switch (check_to_send_update(ref, args)) {
 +              case 0: /* no error */
 +                      break;
 +              case CHECK_REF_STATUS_REJECTED:
 +                      /*
 +                       * When we know the server would reject a ref update if
 +                       * we were to send it and we're trying to send the refs
 +                       * atomically, abort the whole operation.
 +                       */
 +                      if (use_atomic)
 +                              return atomic_push_failure(args, remote_refs, ref);
 +                      /* Fallthrough for non atomic case. */
 +              default:
                        continue;
 -
 +              }
                if (!ref->deletion)
                        need_pack_data = 1;
  
                if (args->dry_run || args->push_cert)
                        continue;
  
 -              if (!ref_update_to_be_sent(ref, args))
 +              if (check_to_send_update(ref, args) < 0)
                        continue;
  
                old_hex = sha1_to_hex(ref->old_sha1);
diff --combined shallow.c
index d8bf40ad4bed3bc846bb71945f1c2bfce76a202d,24872034ef5b104049a96f485851fa49d0deefc9..d08d264dd2e567e1e467c48d03e579fb21677a29
+++ b/shallow.c
@@@ -31,7 -31,7 +31,7 @@@ int register_shallow(const unsigned cha
                xmalloc(sizeof(struct commit_graft));
        struct commit *commit = lookup_commit(sha1);
  
-       hashcpy(graft->sha1, sha1);
+       hashcpy(graft->oid.hash, sha1);
        graft->nr_parent = -1;
        if (commit && commit->object.parsed)
                commit->parents = NULL;
@@@ -137,7 -137,7 +137,7 @@@ struct commit_list *get_shallow_commits
        return result;
  }
  
 -void check_shallow_file_for_update(void)
 +static void check_shallow_file_for_update(void)
  {
        if (is_shallow == -1)
                die("BUG: shallow must be initialized by now");
@@@ -159,11 -159,11 +159,11 @@@ struct write_shallow_data 
  static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
  {
        struct write_shallow_data *data = cb_data;
-       const char *hex = sha1_to_hex(graft->sha1);
+       const char *hex = oid_to_hex(&graft->oid);
        if (graft->nr_parent != -1)
                return 0;
        if (data->flags & SEEN_ONLY) {
-               struct commit *c = lookup_commit(graft->sha1);
+               struct commit *c = lookup_commit(graft->oid.hash);
                if (!c || !(c->object.flags & SEEN)) {
                        if (data->flags & VERBOSE)
                                printf("Removing %s from .git/shallow\n",
@@@ -282,7 -282,7 +282,7 @@@ static int advertise_shallow_grafts_cb(
  {
        int fd = *(int *)cb;
        if (graft->nr_parent == -1)
-               packet_write(fd, "shallow %s\n", sha1_to_hex(graft->sha1));
+               packet_write(fd, "shallow %s\n", oid_to_hex(&graft->oid));
        return 0;
  }
  
diff --combined upload-pack.c
index aa845765009ac7fb9105851306c3288b3f602369,0566ce04b933dd5cb631646fb47f9844da43e94f..745fda8515313fa0629489f36aee0ba49fb7d822
@@@ -74,7 -74,7 +74,7 @@@ static int write_one_shallow(const stru
  {
        FILE *fp = cb_data;
        if (graft->nr_parent == -1)
-               fprintf(fp, "--shallow %s\n", sha1_to_hex(graft->sha1));
+               fprintf(fp, "--shallow %s\n", oid_to_hex(&graft->oid));
        return 0;
  }
  
@@@ -681,7 -681,7 +681,7 @@@ static void receive_needs(void
  }
  
  /* return non-zero if the ref is hidden, otherwise 0 */
 -static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 +static int mark_our_ref(const char *refname, const unsigned char *sha1)
  {
        struct object *o = lookup_unknown_object(sha1);
  
                o->flags |= HIDDEN_REF;
                return 1;
        }
 -      if (!o)
 -              die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
        o->flags |= OUR_REF;
        return 0;
  }
  
 +static int check_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 +{
 +      mark_our_ref(refname, sha1);
 +      return 0;
 +}
 +
  static void format_symref_info(struct strbuf *buf, struct string_list *symref)
  {
        struct string_list_item *item;
@@@ -717,7 -713,7 +717,7 @@@ static int send_ref(const char *refname
        const char *refname_nons = strip_namespace(refname);
        unsigned char peeled[20];
  
 -      if (mark_our_ref(refname, sha1, flag, NULL))
 +      if (mark_our_ref(refname, sha1))
                return 0;
  
        if (capabilities) {
@@@ -771,8 -767,8 +771,8 @@@ static void upload_pack(void
                advertise_shallow_grafts(1);
                packet_flush(1);
        } else {
 -              head_ref_namespaced(mark_our_ref, NULL);
 -              for_each_namespaced_ref(mark_our_ref, NULL);
 +              head_ref_namespaced(check_ref, NULL);
 +              for_each_namespaced_ref(check_ref, NULL);
        }
        string_list_clear(&symref, 1);
        if (advertise_refs)