git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH 0/7] unpack-trees: plug memory leak for merges
@ 2013-05-30 11:34 René Scharfe
  2013-05-30 11:34 ` [PATCH 1/7] cache: mark cache_entry pointers const René Scharfe
                   ` (6 more replies)
  0 siblings, 7 replies; 12+ messages in thread
From: René Scharfe @ 2013-05-30 11:34 UTC (permalink / raw)
  To: git; +Cc: Stephen Boyd, Junio C Hamano

This series adds const to cache_entry pointers in a lot of places, in
order to show that we can free them in unpack_nondirectories, which
the last patch finally does.

First three easy patches for adding const and splitting a function in
two:

  cache: mark cache_entry pointers const
  read-cache: mark cache_entry pointers const
  unpack-trees: factor out dup_entry

Then a patch that reduces the side effects of merged_entry:

  unpack-trees: create working copy of merge entry in merged_entry

Another easy const patch:

  diff-lib, read-tree, unpack-trees: mark cache_entry pointers const

And patch that introduces "const struct cache_entry * const *", which
may look a bit scary at first:

  diff-lib, read-tree, unpack-trees: mark cache_entry array paramters const

Final patch that plugs a memory leak in unpack_nondirectories.

  unpack-trees: free cache_entry array members for merges

It's basically the same one that Stephen tested a while ago
(http://thread.gmane.org/gmane.comp.version-control.git/222887).

It's not the only leak that affects cherry-pick; expect more
(independent) patches.

 builtin/read-tree.c |   5 +-
 cache.h             |  10 ++--
 diff-lib.c          |  26 +++++-----
 read-cache.c        |  18 ++++---
 unpack-trees.c      | 146 ++++++++++++++++++++++++++++++++--------------------
 unpack-trees.h      |  14 +++--
 6 files changed, 131 insertions(+), 88 deletions 
1.8.3

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/7] cache: mark cache_entry pointers const
  2013-05-30 11:34 [PATCH 0/7] unpack-trees: plug memory leak for merges René Scharfe
@ 2013-05-30 11:34 ` René Scharfe
  2013-05-30 11:34 ` [PATCH 2/7] read-cache: " René Scharfe
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: René Scharfe @ 2013-05-30 11:34 UTC (permalink / raw)
  To: git; +Cc: Stephen Boyd, Junio C Hamano

Add const for pointers that are only dereferenced for reading by the
inline functions copy_cache_entry and ce_mode_from_stat.  This allows
callers to pass in const pointers.

Signed-off-by: René Scharfe <rene.scharfe@lsrfire.ath.cx>
---
 cache.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/cache.h b/cache.h
index 94ca1ac..43a27e7 100644
--- a/cache.h
+++ b/cache.h
@@ -190,7 +190,8 @@ struct cache_entry {
  * another. But we never change the name, or the hash state!
  */
 #define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
-static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry *src)
+static inline void copy_cache_entry(struct cache_entry *dst,
+				    const struct cache_entry *src)
 {
 	unsigned int state = dst->ce_flags & CE_STATE_MASK;
 
@@ -222,7 +223,8 @@ static inline unsigned int create_ce_mode(unsigned int mode)
 		return S_IFGITLINK;
 	return S_IFREG | ce_permissions(mode);
 }
-static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
+static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce,
+					     unsigned int mode)
 {
 	extern int trust_executable_bit, has_symlinks;
 	if (!has_symlinks && S_ISREG(mode) &&
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/7] read-cache: mark cache_entry pointers const
  2013-05-30 11:34 [PATCH 0/7] unpack-trees: plug memory leak for merges René Scharfe
  2013-05-30 11:34 ` [PATCH 1/7] cache: mark cache_entry pointers const René Scharfe
@ 2013-05-30 11:34 ` René Scharfe
  2013-05-30 11:34 ` [PATCH 3/7] unpack-trees: factor out dup_entry René Scharfe
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: René Scharfe @ 2013-05-30 11:34 UTC (permalink / raw)
  To: git; +Cc: Stephen Boyd, Junio C Hamano

ie_match_stat and ie_modified only derefence their struct cache_entry
pointers for reading.  Add const to the parameter declaration here and
do the same for the static helper function used by them, as it's the
same there as well.  This allows callers to pass in const pointers.

Signed-off-by: René Scharfe <rene.scharfe@lsrfire.ath.cx>
---
 cache.h      |  4 ++--
 read-cache.c | 18 ++++++++++--------
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/cache.h b/cache.h
index 43a27e7..01e8760 100644
--- a/cache.h
+++ b/cache.h
@@ -482,8 +482,8 @@ extern void *read_blob_data_from_index(struct index_state *, const char *, unsig
 #define CE_MATCH_RACY_IS_DIRTY		02
 /* do stat comparison even if CE_SKIP_WORKTREE is true */
 #define CE_MATCH_IGNORE_SKIP_WORKTREE	04
-extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
-extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 
 #define PATHSPEC_ONESTAR 1	/* the pathspec pattern sastisfies GFNM_ONESTAR */
 
diff --git a/read-cache.c b/read-cache.c
index 04ed561..e6e0466 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -91,7 +91,7 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 		ce_mark_uptodate(ce);
 }
 
-static int ce_compare_data(struct cache_entry *ce, struct stat *st)
+static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
 {
 	int match = -1;
 	int fd = open(ce->name, O_RDONLY);
@@ -105,7 +105,7 @@ static int ce_compare_data(struct cache_entry *ce, struct stat *st)
 	return match;
 }
 
-static int ce_compare_link(struct cache_entry *ce, size_t expected_size)
+static int ce_compare_link(const struct cache_entry *ce, size_t expected_size)
 {
 	int match = -1;
 	void *buffer;
@@ -126,7 +126,7 @@ static int ce_compare_link(struct cache_entry *ce, size_t expected_size)
 	return match;
 }
 
-static int ce_compare_gitlink(struct cache_entry *ce)
+static int ce_compare_gitlink(const struct cache_entry *ce)
 {
 	unsigned char sha1[20];
 
@@ -143,7 +143,7 @@ static int ce_compare_gitlink(struct cache_entry *ce)
 	return hashcmp(sha1, ce->sha1);
 }
 
-static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
+static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
 {
 	switch (st->st_mode & S_IFMT) {
 	case S_IFREG:
@@ -163,7 +163,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
 	return 0;
 }
 
-static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
+static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
 {
 	unsigned int changed = 0;
 
@@ -239,7 +239,8 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 	return changed;
 }
 
-static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
+static int is_racy_timestamp(const struct index_state *istate,
+			     const struct cache_entry *ce)
 {
 	return (!S_ISGITLINK(ce->ce_mode) &&
 		istate->timestamp.sec &&
@@ -255,7 +256,7 @@ static int is_racy_timestamp(const struct index_state *istate, struct cache_entr
 }
 
 int ie_match_stat(const struct index_state *istate,
-		  struct cache_entry *ce, struct stat *st,
+		  const struct cache_entry *ce, struct stat *st,
 		  unsigned int options)
 {
 	unsigned int changed;
@@ -311,7 +312,8 @@ int ie_match_stat(const struct index_state *istate,
 }
 
 int ie_modified(const struct index_state *istate,
-		struct cache_entry *ce, struct stat *st, unsigned int options)
+		const struct cache_entry *ce,
+		struct stat *st, unsigned int options)
 {
 	int changed, changed_fs;
 
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 3/7] unpack-trees: factor out dup_entry
  2013-05-30 11:34 [PATCH 0/7] unpack-trees: plug memory leak for merges René Scharfe
  2013-05-30 11:34 ` [PATCH 1/7] cache: mark cache_entry pointers const René Scharfe
  2013-05-30 11:34 ` [PATCH 2/7] read-cache: " René Scharfe
@ 2013-05-30 11:34 ` René Scharfe
  2013-06-04 15:06   ` Peter Krefting
  2013-05-30 11:34 ` [PATCH 4/7] unpack-trees: create working copy of merge entry in merged_entry René Scharfe
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: René Scharfe @ 2013-05-30 11:34 UTC (permalink / raw)
  To: git; +Cc: Stephen Boyd, Junio C Hamano

While we're add it, mark the struct cache_entry pointer of add_entry
const because we only read from it and this allows callers to pass in
const pointers.

Signed-off-by: René Scharfe <rene.scharfe@lsrfire.ath.cx>
---
 unpack-trees.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/unpack-trees.c b/unpack-trees.c
index ede4299..e8b4cc1 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -116,14 +116,20 @@ static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 			ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 }
 
-static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
-	unsigned int set, unsigned int clear)
+static struct cache_entry *dup_entry(const struct cache_entry *ce)
 {
 	unsigned int size = ce_size(ce);
 	struct cache_entry *new = xmalloc(size);
 
 	memcpy(new, ce, size);
-	do_add_entry(o, new, set, clear);
+	return new;
+}
+
+static void add_entry(struct unpack_trees_options *o,
+		      const struct cache_entry *ce,
+		      unsigned int set, unsigned int clear)
+{
+	do_add_entry(o, dup_entry(ce), set, clear);
 }
 
 /*
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 4/7] unpack-trees: create working copy of merge entry in merged_entry
  2013-05-30 11:34 [PATCH 0/7] unpack-trees: plug memory leak for merges René Scharfe
                   ` (2 preceding siblings ...)
  2013-05-30 11:34 ` [PATCH 3/7] unpack-trees: factor out dup_entry René Scharfe
@ 2013-05-30 11:34 ` René Scharfe
  2013-05-30 11:34 ` [PATCH 5/7] diff-lib, read-tree, unpack-trees: mark cache_entry pointers const René Scharfe
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: René Scharfe @ 2013-05-30 11:34 UTC (permalink / raw)
  To: git; +Cc: Stephen Boyd, Junio C Hamano

Duplicate the merge entry right away and work with that instead of
modifying the entry we got and duplicating it only at the end of
the function.  Then mark that pointer const to document that we
don't modify the referenced cache_entry.

This change is safe because all existing merge functions call
merged_entry just before returning (or not at all), i.e. they don't
care about changes to the referenced cache_entry after the call.
unpack_nondirectories and unpack_index_entry, which call the merge
functions through call_unpack_fn, aren't interested in such changes
neither.

The change complicates merged_entry a bit because we have to free the
copy if we error out, but allows callers to pass a const pointer.

Signed-off-by: René Scharfe <rene.scharfe@lsrfire.ath.cx>
---
 unpack-trees.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/unpack-trees.c b/unpack-trees.c
index e8b4cc1..2fecef8 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1466,10 +1466,12 @@ static int verify_absent_sparse(struct cache_entry *ce,
 	return verify_absent_1(ce, orphaned_error, o);
 }
 
-static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
-		struct unpack_trees_options *o)
+static int merged_entry(const struct cache_entry *ce,
+			struct cache_entry *old,
+			struct unpack_trees_options *o)
 {
 	int update = CE_UPDATE;
+	struct cache_entry *merge = dup_entry(ce);
 
 	if (!old) {
 		/*
@@ -1487,8 +1489,11 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
 		update |= CE_ADDED;
 		merge->ce_flags |= CE_NEW_SKIP_WORKTREE;
 
-		if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
+		if (verify_absent(merge,
+				  ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
+			free(merge);
 			return -1;
+		}
 		invalidate_ce_path(merge, o);
 	} else if (!(old->ce_flags & CE_CONFLICTED)) {
 		/*
@@ -1502,8 +1507,10 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
 			copy_cache_entry(merge, old);
 			update = 0;
 		} else {
-			if (verify_uptodate(old, o))
+			if (verify_uptodate(old, o)) {
+				free(merge);
 				return -1;
+			}
 			/* Migrate old flags over */
 			update |= old->ce_flags & (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
 			invalidate_ce_path(old, o);
@@ -1516,7 +1523,7 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
 		invalidate_ce_path(old, o);
 	}
 
-	add_entry(o, merge, update, CE_STAGEMASK);
+	do_add_entry(o, merge, update, CE_STAGEMASK);
 	return 1;
 }
 
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 5/7] diff-lib, read-tree, unpack-trees: mark cache_entry pointers const
  2013-05-30 11:34 [PATCH 0/7] unpack-trees: plug memory leak for merges René Scharfe
                   ` (3 preceding siblings ...)
  2013-05-30 11:34 ` [PATCH 4/7] unpack-trees: create working copy of merge entry in merged_entry René Scharfe
@ 2013-05-30 11:34 ` René Scharfe
  2013-05-30 11:34 ` [PATCH 6/7] diff-lib, read-tree, unpack-trees: mark cache_entry array paramters const René Scharfe
  2013-05-30 11:34 ` [PATCH 7/7] unpack-trees: free cache_entry array members for merges René Scharfe
  6 siblings, 0 replies; 12+ messages in thread
From: René Scharfe @ 2013-05-30 11:34 UTC (permalink / raw)
  To: git; +Cc: Stephen Boyd, Junio C Hamano

Add const to struct cache_entry pointers throughout the tree which are
only used for reading.  This allows callers to pass in const pointers.

Signed-off-by: René Scharfe <rene.scharfe@lsrfire.ath.cx>
---
 builtin/read-tree.c |  2 +-
 diff-lib.c          | 23 +++++++-------
 unpack-trees.c      | 91 +++++++++++++++++++++++++++++------------------------
 3 files changed, 63 insertions(+), 53 deletions(-)

diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 042ac1b..b847486 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -66,7 +66,7 @@ static int exclude_per_directory_cb(const struct option *opt, const char *arg,
 	return 0;
 }
 
-static void debug_stage(const char *label, struct cache_entry *ce,
+static void debug_stage(const char *label, const struct cache_entry *ce,
 			struct unpack_trees_options *o)
 {
 	printf("%s ", label);
diff --git a/diff-lib.c b/diff-lib.c
index f35de0f..83d0cb8 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -64,8 +64,9 @@ static int check_removed(const struct cache_entry *ce, struct stat *st)
  * commits, untracked content and/or modified content).
  */
 static int match_stat_with_submodule(struct diff_options *diffopt,
-				      struct cache_entry *ce, struct stat *st,
-				      unsigned ce_option, unsigned *dirty_submodule)
+				     const struct cache_entry *ce,
+				     struct stat *st, unsigned ce_option,
+				     unsigned *dirty_submodule)
 {
 	int changed = ce_match_stat(ce, st, ce_option);
 	if (S_ISGITLINK(ce->ce_mode)) {
@@ -237,7 +238,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
 /* A file entry went away or appeared */
 static void diff_index_show_file(struct rev_info *revs,
 				 const char *prefix,
-				 struct cache_entry *ce,
+				 const struct cache_entry *ce,
 				 const unsigned char *sha1, int sha1_valid,
 				 unsigned int mode,
 				 unsigned dirty_submodule)
@@ -246,7 +247,7 @@ static void diff_index_show_file(struct rev_info *revs,
 		       sha1, sha1_valid, ce->name, dirty_submodule);
 }
 
-static int get_stat_data(struct cache_entry *ce,
+static int get_stat_data(const struct cache_entry *ce,
 			 const unsigned char **sha1p,
 			 unsigned int *modep,
 			 int cached, int match_missing,
@@ -283,7 +284,7 @@ static int get_stat_data(struct cache_entry *ce,
 }
 
 static void show_new_file(struct rev_info *revs,
-			  struct cache_entry *new,
+			  const struct cache_entry *new,
 			  int cached, int match_missing)
 {
 	const unsigned char *sha1;
@@ -302,8 +303,8 @@ static void show_new_file(struct rev_info *revs,
 }
 
 static int show_modified(struct rev_info *revs,
-			 struct cache_entry *old,
-			 struct cache_entry *new,
+			 const struct cache_entry *old,
+			 const struct cache_entry *new,
 			 int report_missing,
 			 int cached, int match_missing)
 {
@@ -362,8 +363,8 @@ static int show_modified(struct rev_info *revs,
  * give you the position and number of entries in the index).
  */
 static void do_oneway_diff(struct unpack_trees_options *o,
-	struct cache_entry *idx,
-	struct cache_entry *tree)
+			   const struct cache_entry *idx,
+			   const struct cache_entry *tree)
 {
 	struct rev_info *revs = o->unpack_data;
 	int match_missing, cached;
@@ -425,8 +426,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,
  */
 static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
 {
-	struct cache_entry *idx = src[0];
-	struct cache_entry *tree = src[1];
+	const struct cache_entry *idx = src[0];
+	const struct cache_entry *tree = src[1];
 	struct rev_info *revs = o->unpack_data;
 
 	/*
diff --git a/unpack-trees.c b/unpack-trees.c
index 2fecef8..c5a40df 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -241,8 +241,11 @@ static int check_updates(struct unpack_trees_options *o)
 	return errs != 0;
 }
 
-static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);
-static int verify_absent_sparse(struct cache_entry *ce, enum unpack_trees_error_types, struct unpack_trees_options *o);
+static int verify_uptodate_sparse(const struct cache_entry *ce,
+				  struct unpack_trees_options *o);
+static int verify_absent_sparse(const struct cache_entry *ce,
+				enum unpack_trees_error_types,
+				struct unpack_trees_options *o);
 
 static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)
 {
@@ -326,7 +329,7 @@ static void mark_all_ce_unused(struct index_state *index)
 		index->cache[i]->ce_flags &= ~(CE_UNPACKED | CE_ADDED | CE_NEW_SKIP_WORKTREE);
 }
 
-static int locate_in_src_index(struct cache_entry *ce,
+static int locate_in_src_index(const struct cache_entry *ce,
 			       struct unpack_trees_options *o)
 {
 	struct index_state *index = o->src_index;
@@ -1001,7 +1004,9 @@ static void mark_new_skip_worktree(struct exclude_list *el,
 		       select_flag, skip_wt_flag, el);
 }
 
-static int verify_absent(struct cache_entry *, enum unpack_trees_error_types, struct unpack_trees_options *);
+static int verify_absent(const struct cache_entry *,
+			 enum unpack_trees_error_types,
+			 struct unpack_trees_options *);
 /*
  * N-way merge "len" trees.  Returns 0 on success, -1 on failure to manipulate the
  * resulting index, -2 on failure to reflect the changes to the work tree.
@@ -1171,12 +1176,13 @@ return_failed:
 
 /* Here come the merge functions */
 
-static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)
+static int reject_merge(const struct cache_entry *ce,
+			struct unpack_trees_options *o)
 {
 	return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
 }
 
-static int same(struct cache_entry *a, struct cache_entry *b)
+static int same(const struct cache_entry *a, const struct cache_entry *b)
 {
 	if (!!a != !!b)
 		return 0;
@@ -1193,9 +1199,9 @@ static int same(struct cache_entry *a, struct cache_entry *b)
  * When a CE gets turned into an unmerged entry, we
  * want it to be up-to-date
  */
-static int verify_uptodate_1(struct cache_entry *ce,
-				   struct unpack_trees_options *o,
-				   enum unpack_trees_error_types error_type)
+static int verify_uptodate_1(const struct cache_entry *ce,
+			     struct unpack_trees_options *o,
+			     enum unpack_trees_error_types error_type)
 {
 	struct stat st;
 
@@ -1234,7 +1240,7 @@ static int verify_uptodate_1(struct cache_entry *ce,
 		add_rejected_path(o, error_type, ce->name);
 }
 
-static int verify_uptodate(struct cache_entry *ce,
+static int verify_uptodate(const struct cache_entry *ce,
 			   struct unpack_trees_options *o)
 {
 	if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
@@ -1242,13 +1248,14 @@ static int verify_uptodate(struct cache_entry *ce,
 	return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);
 }
 
-static int verify_uptodate_sparse(struct cache_entry *ce,
+static int verify_uptodate_sparse(const struct cache_entry *ce,
 				  struct unpack_trees_options *o)
 {
 	return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
 }
 
-static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
+static void invalidate_ce_path(const struct cache_entry *ce,
+			       struct unpack_trees_options *o)
 {
 	if (ce)
 		cache_tree_invalidate_path(o->src_index->cache_tree, ce->name);
@@ -1261,16 +1268,16 @@ static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_optio
  * Currently, git does not checkout subprojects during a superproject
  * checkout, so it is not going to overwrite anything.
  */
-static int verify_clean_submodule(struct cache_entry *ce,
-				      enum unpack_trees_error_types error_type,
-				      struct unpack_trees_options *o)
+static int verify_clean_submodule(const struct cache_entry *ce,
+				  enum unpack_trees_error_types error_type,
+				  struct unpack_trees_options *o)
 {
 	return 0;
 }
 
-static int verify_clean_subdirectory(struct cache_entry *ce,
-				      enum unpack_trees_error_types error_type,
-				      struct unpack_trees_options *o)
+static int verify_clean_subdirectory(const struct cache_entry *ce,
+				     enum unpack_trees_error_types error_type,
+				     struct unpack_trees_options *o)
 {
 	/*
 	 * we are about to extract "ce->name"; we would not want to lose
@@ -1356,7 +1363,7 @@ static int icase_exists(struct unpack_trees_options *o, const char *name, int le
 }
 
 static int check_ok_to_remove(const char *name, int len, int dtype,
-			      struct cache_entry *ce, struct stat *st,
+			      const struct cache_entry *ce, struct stat *st,
 			      enum unpack_trees_error_types error_type,
 			      struct unpack_trees_options *o)
 {
@@ -1411,9 +1418,9 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
  * We do not want to remove or overwrite a working tree file that
  * is not tracked, unless it is ignored.
  */
-static int verify_absent_1(struct cache_entry *ce,
-				 enum unpack_trees_error_types error_type,
-				 struct unpack_trees_options *o)
+static int verify_absent_1(const struct cache_entry *ce,
+			   enum unpack_trees_error_types error_type,
+			   struct unpack_trees_options *o)
 {
 	int len;
 	struct stat st;
@@ -1446,7 +1453,7 @@ static int verify_absent_1(struct cache_entry *ce,
 	}
 }
 
-static int verify_absent(struct cache_entry *ce,
+static int verify_absent(const struct cache_entry *ce,
 			 enum unpack_trees_error_types error_type,
 			 struct unpack_trees_options *o)
 {
@@ -1455,9 +1462,9 @@ static int verify_absent(struct cache_entry *ce,
 	return verify_absent_1(ce, error_type, o);
 }
 
-static int verify_absent_sparse(struct cache_entry *ce,
-			 enum unpack_trees_error_types error_type,
-			 struct unpack_trees_options *o)
+static int verify_absent_sparse(const struct cache_entry *ce,
+				enum unpack_trees_error_types error_type,
+				struct unpack_trees_options *o)
 {
 	enum unpack_trees_error_types orphaned_error = error_type;
 	if (orphaned_error == ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN)
@@ -1467,7 +1474,7 @@ static int verify_absent_sparse(struct cache_entry *ce,
 }
 
 static int merged_entry(const struct cache_entry *ce,
-			struct cache_entry *old,
+			const struct cache_entry *old,
 			struct unpack_trees_options *o)
 {
 	int update = CE_UPDATE;
@@ -1527,8 +1534,9 @@ static int merged_entry(const struct cache_entry *ce,
 	return 1;
 }
 
-static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
-		struct unpack_trees_options *o)
+static int deleted_entry(const struct cache_entry *ce,
+			 const struct cache_entry *old,
+			 struct unpack_trees_options *o)
 {
 	/* Did it exist in the index? */
 	if (!old) {
@@ -1543,7 +1551,8 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
 	return 1;
 }
 
-static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o)
+static int keep_entry(const struct cache_entry *ce,
+		      struct unpack_trees_options *o)
 {
 	add_entry(o, ce, 0, 0);
 	return 1;
@@ -1567,9 +1576,9 @@ static void show_stage_entry(FILE *o,
 
 int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
 {
-	struct cache_entry *index;
-	struct cache_entry *head;
-	struct cache_entry *remote = stages[o->head_idx + 1];
+	const struct cache_entry *index;
+	const struct cache_entry *head;
+	const struct cache_entry *remote = stages[o->head_idx + 1];
 	int count;
 	int head_match = 0;
 	int remote_match = 0;
@@ -1654,7 +1663,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
 	if (o->aggressive) {
 		int head_deleted = !head;
 		int remote_deleted = !remote;
-		struct cache_entry *ce = NULL;
+		const struct cache_entry *ce = NULL;
 
 		if (index)
 			ce = index;
@@ -1739,9 +1748,9 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
  */
 int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 {
-	struct cache_entry *current = src[0];
-	struct cache_entry *oldtree = src[1];
-	struct cache_entry *newtree = src[2];
+	const struct cache_entry *current = src[0];
+	const struct cache_entry *oldtree = src[1];
+	const struct cache_entry *newtree = src[2];
 
 	if (o->merge_size != 2)
 		return error("Cannot do a twoway merge of %d trees",
@@ -1806,8 +1815,8 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 int bind_merge(struct cache_entry **src,
 		struct unpack_trees_options *o)
 {
-	struct cache_entry *old = src[0];
-	struct cache_entry *a = src[1];
+	const struct cache_entry *old = src[0];
+	const struct cache_entry *a = src[1];
 
 	if (o->merge_size != 1)
 		return error("Cannot do a bind merge of %d trees",
@@ -1829,8 +1838,8 @@ int bind_merge(struct cache_entry **src,
  */
 int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 {
-	struct cache_entry *old = src[0];
-	struct cache_entry *a = src[1];
+	const struct cache_entry *old = src[0];
+	const struct cache_entry *a = src[1];
 
 	if (o->merge_size != 1)
 		return error("Cannot do a oneway merge of %d trees",
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 6/7] diff-lib, read-tree, unpack-trees: mark cache_entry array paramters const
  2013-05-30 11:34 [PATCH 0/7] unpack-trees: plug memory leak for merges René Scharfe
                   ` (4 preceding siblings ...)
  2013-05-30 11:34 ` [PATCH 5/7] diff-lib, read-tree, unpack-trees: mark cache_entry pointers const René Scharfe
@ 2013-05-30 11:34 ` René Scharfe
  2013-05-30 11:34 ` [PATCH 7/7] unpack-trees: free cache_entry array members for merges René Scharfe
  6 siblings, 0 replies; 12+ messages in thread
From: René Scharfe @ 2013-05-30 11:34 UTC (permalink / raw)
  To: git; +Cc: Stephen Boyd, Junio C Hamano

Change the type merge_fn_t to accept the array of cache_entry pointers
as const pointers to const pointers.  This documents the fact that the
merge functions don't modify the cache_entry contents or replace any of
the pointers in the array.

Only a single cast is necessary in unpack_nondirectories because adding
two const modifiers at once is not allowed in C.  The cast is safe in
that it doesn't mask any modfication; call_unpack_fn only needs the
array for reading.

Signed-off-by: René Scharfe <rene.scharfe@lsrfire.ath.cx>
---
 builtin/read-tree.c |  3 ++-
 diff-lib.c          |  3 ++-
 unpack-trees.c      | 21 +++++++++++++--------
 unpack-trees.h      | 14 +++++++++-----
 4 files changed, 26 insertions(+), 15 deletions(-)

diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index b847486..0f5d7fe 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -80,7 +80,8 @@ static void debug_stage(const char *label, const struct cache_entry *ce,
 		       sha1_to_hex(ce->sha1));
 }
 
-static int debug_merge(struct cache_entry **stages, struct unpack_trees_options *o)
+static int debug_merge(const struct cache_entry * const *stages,
+		       struct unpack_trees_options *o)
 {
 	int i;
 
diff --git a/diff-lib.c b/diff-lib.c
index 83d0cb8..b6f4b21 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -424,7 +424,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,
  * the fairly complex unpack_trees() semantic requirements, including
  * the skipping, the path matching, the type conflict cases etc.
  */
-static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
+static int oneway_diff(const struct cache_entry * const *src,
+		       struct unpack_trees_options *o)
 {
 	const struct cache_entry *idx = src[0];
 	const struct cache_entry *tree = src[1];
diff --git a/unpack-trees.c b/unpack-trees.c
index c5a40df..2dbc05d 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -300,7 +300,8 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
 	return 0;
 }
 
-static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)
+static inline int call_unpack_fn(const struct cache_entry * const *src,
+				 struct unpack_trees_options *o)
 {
 	int ret = o->fn(src, o);
 	if (ret > 0)
@@ -397,7 +398,7 @@ static void add_same_unmerged(struct cache_entry *ce,
 static int unpack_index_entry(struct cache_entry *ce,
 			      struct unpack_trees_options *o)
 {
-	struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
+	const struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
 	int ret;
 
 	src[0] = ce;
@@ -600,7 +601,8 @@ static int unpack_nondirectories(int n, unsigned long mask,
 	}
 
 	if (o->merge)
-		return call_unpack_fn(src, o);
+		return call_unpack_fn((const struct cache_entry * const *)src,
+				      o);
 
 	for (i = 0; i < n; i++)
 		if (src[i] && src[i] != o->df_conflict_entry)
@@ -1574,7 +1576,8 @@ static void show_stage_entry(FILE *o,
 }
 #endif
 
-int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
+int threeway_merge(const struct cache_entry * const *stages,
+		   struct unpack_trees_options *o)
 {
 	const struct cache_entry *index;
 	const struct cache_entry *head;
@@ -1746,7 +1749,8 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
  * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
  *
  */
-int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
+int twoway_merge(const struct cache_entry * const *src,
+		 struct unpack_trees_options *o)
 {
 	const struct cache_entry *current = src[0];
 	const struct cache_entry *oldtree = src[1];
@@ -1812,8 +1816,8 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
  * Keep the index entries at stage0, collapse stage1 but make sure
  * stage0 does not have anything there.
  */
-int bind_merge(struct cache_entry **src,
-		struct unpack_trees_options *o)
+int bind_merge(const struct cache_entry * const *src,
+	       struct unpack_trees_options *o)
 {
 	const struct cache_entry *old = src[0];
 	const struct cache_entry *a = src[1];
@@ -1836,7 +1840,8 @@ int bind_merge(struct cache_entry **src,
  * The rule is:
  * - take the stat information from stage0, take the data from stage1
  */
-int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
+int oneway_merge(const struct cache_entry * const *src,
+		 struct unpack_trees_options *o)
 {
 	const struct cache_entry *old = src[0];
 	const struct cache_entry *a = src[1];
diff --git a/unpack-trees.h b/unpack-trees.h
index 5e432f5..36a73a6 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -8,7 +8,7 @@
 struct unpack_trees_options;
 struct exclude_list;
 
-typedef int (*merge_fn_t)(struct cache_entry **src,
+typedef int (*merge_fn_t)(const struct cache_entry * const *src,
 		struct unpack_trees_options *options);
 
 enum unpack_trees_error_types {
@@ -77,9 +77,13 @@ struct unpack_trees_options {
 extern int unpack_trees(unsigned n, struct tree_desc *t,
 		struct unpack_trees_options *options);
 
-int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);
-int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
-int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
-int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int threeway_merge(const struct cache_entry * const *stages,
+		   struct unpack_trees_options *o);
+int twoway_merge(const struct cache_entry * const *src,
+		 struct unpack_trees_options *o);
+int bind_merge(const struct cache_entry * const *src,
+	       struct unpack_trees_options *o);
+int oneway_merge(const struct cache_entry * const *src,
+		 struct unpack_trees_options *o);
 
 #endif
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 7/7] unpack-trees: free cache_entry array members for merges
  2013-05-30 11:34 [PATCH 0/7] unpack-trees: plug memory leak for merges René Scharfe
                   ` (5 preceding siblings ...)
  2013-05-30 11:34 ` [PATCH 6/7] diff-lib, read-tree, unpack-trees: mark cache_entry array paramters const René Scharfe
@ 2013-05-30 11:34 ` René Scharfe
  2013-05-30 12:04   ` Felipe Contreras
  6 siblings, 1 reply; 12+ messages in thread
From: René Scharfe @ 2013-05-30 11:34 UTC (permalink / raw)
  To: git; +Cc: Stephen Boyd, Junio C Hamano

The merge functions duplicate entries as needed and they don't free
them.  Release them in unpack_nondirectories, the same function
where they were allocated, after we're done.

Signed-off-by: René Scharfe <rene.scharfe@lsrfire.ath.cx>
---
 unpack-trees.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/unpack-trees.c b/unpack-trees.c
index 2dbc05d..fc0dd5a 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -600,9 +600,14 @@ static int unpack_nondirectories(int n, unsigned long mask,
 		src[i + o->merge] = create_ce_entry(info, names + i, stage);
 	}
 
-	if (o->merge)
-		return call_unpack_fn((const struct cache_entry * const *)src,
-				      o);
+	if (o->merge) {
+		int rc = call_unpack_fn((const struct cache_entry * const *)src,
+					o);
+		for (i = 1; i <= n; i++)
+			if (src[i] && src[i] != o->df_conflict_entry)
+				free(src[i]);
+		return rc;
+	}
 
 	for (i = 0; i < n; i++)
 		if (src[i] && src[i] != o->df_conflict_entry)
-- 
1.8.3

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 7/7] unpack-trees: free cache_entry array members for merges
  2013-05-30 11:34 ` [PATCH 7/7] unpack-trees: free cache_entry array members for merges René Scharfe
@ 2013-05-30 12:04   ` Felipe Contreras
  2013-05-30 14:40     ` René Scharfe
  0 siblings, 1 reply; 12+ messages in thread
From: Felipe Contreras @ 2013-05-30 12:04 UTC (permalink / raw)
  To: René Scharfe; +Cc: git, Stephen Boyd, Junio C Hamano

On Thu, May 30, 2013 at 6:34 AM, René Scharfe
<rene.scharfe@lsrfire.ath.cx> wrote:
> The merge functions duplicate entries as needed and they don't free
> them.  Release them in unpack_nondirectories, the same function
> where they were allocated, after we're done.

Ah, you beat me to this change, but..

> @@ -600,9 +600,14 @@ static int unpack_nondirectories(int n, unsigned long mask,
>                 src[i + o->merge] = create_ce_entry(info, names + i, stage);
>         }
>
> -       if (o->merge)
> -               return call_unpack_fn((const struct cache_entry * const *)src,
> -                                     o);
> +       if (o->merge) {
> +               int rc = call_unpack_fn((const struct cache_entry * const *)src,
> +                                       o);
> +               for (i = 1; i <= n; i++)
> +                       if (src[i] && src[i] != o->df_conflict_entry)
> +                               free(src[i]);

Doesn't it make more sense to follow the code above and do src[i + o->merge]?

-- 
Felipe Contreras

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 7/7] unpack-trees: free cache_entry array members for merges
  2013-05-30 12:04   ` Felipe Contreras
@ 2013-05-30 14:40     ` René Scharfe
  2013-05-30 15:20       ` Felipe Contreras
  0 siblings, 1 reply; 12+ messages in thread
From: René Scharfe @ 2013-05-30 14:40 UTC (permalink / raw)
  To: Felipe Contreras; +Cc: git, Stephen Boyd, Junio C Hamano

Am 30.05.2013 14:04, schrieb Felipe Contreras:
> On Thu, May 30, 2013 at 6:34 AM, René Scharfe
> <rene.scharfe@lsrfire.ath.cx> wrote:
>> The merge functions duplicate entries as needed and they don't free
>> them.  Release them in unpack_nondirectories, the same function
>> where they were allocated, after we're done.
>
> Ah, you beat me to this change, but..
>
>> @@ -600,9 +600,14 @@ static int unpack_nondirectories(int n, unsigned long mask,
>>                  src[i + o->merge] = create_ce_entry(info, names + i, stage);
>>          }
>>
>> -       if (o->merge)
>> -               return call_unpack_fn((const struct cache_entry * const *)src,
>> -                                     o);
>> +       if (o->merge) {
>> +               int rc = call_unpack_fn((const struct cache_entry * const *)src,
>> +                                       o);
>> +               for (i = 1; i <= n; i++)
>> +                       if (src[i] && src[i] != o->df_conflict_entry)
>> +                               free(src[i]);
>
> Doesn't it make more sense to follow the code above and do src[i + o->merge]?

Not sure I understand.  Is the goal to avoid confusion for code readers 
by using the same indexing method for allocation and release?  Or are 
you worried about o->merge having a different value than 1 in that loop?

We'd have to add 1 (== o->merge) to each index variable usage with a 
zero-based loop.  A one-based loop avoids that, and while it's not 
pretty it's also not too complicated, I think.

René

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 7/7] unpack-trees: free cache_entry array members for merges
  2013-05-30 14:40     ` René Scharfe
@ 2013-05-30 15:20       ` Felipe Contreras
  0 siblings, 0 replies; 12+ messages in thread
From: Felipe Contreras @ 2013-05-30 15:20 UTC (permalink / raw)
  To: René Scharfe; +Cc: git, Stephen Boyd, Junio C Hamano

On Thu, May 30, 2013 at 9:40 AM, René Scharfe
<rene.scharfe@lsrfire.ath.cx> wrote:
> Am 30.05.2013 14:04, schrieb Felipe Contreras:
>
>> On Thu, May 30, 2013 at 6:34 AM, René Scharfe
>> <rene.scharfe@lsrfire.ath.cx> wrote:
>>>
>>> The merge functions duplicate entries as needed and they don't free
>>> them.  Release them in unpack_nondirectories, the same function
>>> where they were allocated, after we're done.
>>
>>
>> Ah, you beat me to this change, but..
>>
>>> @@ -600,9 +600,14 @@ static int unpack_nondirectories(int n, unsigned
>>> long mask,
>>>                  src[i + o->merge] = create_ce_entry(info, names + i,
>>> stage);
>>>          }
>>>
>>> -       if (o->merge)
>>> -               return call_unpack_fn((const struct cache_entry * const
>>> *)src,
>>> -                                     o);
>>> +       if (o->merge) {
>>> +               int rc = call_unpack_fn((const struct cache_entry * const
>>> *)src,
>>> +                                       o);
>>> +               for (i = 1; i <= n; i++)
>>> +                       if (src[i] && src[i] != o->df_conflict_entry)
>>> +                               free(src[i]);
>>
>>
>> Doesn't it make more sense to follow the code above and do src[i +
>> o->merge]?
>
>
> Not sure I understand.  Is the goal to avoid confusion for code readers by
> using the same indexing method for allocation and release?  Or are you
> worried about o->merge having a different value than 1 in that loop?

Both. In particular I'm eyeballing the code you can even see in this patch:

 src[i + o->merge] = create_ce_entry(info, names + i, stage);

If you think it's better to use src[i], then I think the code above
should do the same.

-- 
Felipe Contreras

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 3/7] unpack-trees: factor out dup_entry
  2013-05-30 11:34 ` [PATCH 3/7] unpack-trees: factor out dup_entry René Scharfe
@ 2013-06-04 15:06   ` Peter Krefting
  0 siblings, 0 replies; 12+ messages in thread
From: Peter Krefting @ 2013-06-04 15:06 UTC (permalink / raw)
  To: René Scharfe; +Cc: Git Mailing List

René Scharfe:

> While we're add it,

  "add" → "at"

-- 
\\// Peter - http://www.softwolves.pp.se/

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2013-06-04 15:06 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-05-30 11:34 [PATCH 0/7] unpack-trees: plug memory leak for merges René Scharfe
2013-05-30 11:34 ` [PATCH 1/7] cache: mark cache_entry pointers const René Scharfe
2013-05-30 11:34 ` [PATCH 2/7] read-cache: " René Scharfe
2013-05-30 11:34 ` [PATCH 3/7] unpack-trees: factor out dup_entry René Scharfe
2013-06-04 15:06   ` Peter Krefting
2013-05-30 11:34 ` [PATCH 4/7] unpack-trees: create working copy of merge entry in merged_entry René Scharfe
2013-05-30 11:34 ` [PATCH 5/7] diff-lib, read-tree, unpack-trees: mark cache_entry pointers const René Scharfe
2013-05-30 11:34 ` [PATCH 6/7] diff-lib, read-tree, unpack-trees: mark cache_entry array paramters const René Scharfe
2013-05-30 11:34 ` [PATCH 7/7] unpack-trees: free cache_entry array members for merges René Scharfe
2013-05-30 12:04   ` Felipe Contreras
2013-05-30 14:40     ` René Scharfe
2013-05-30 15:20       ` Felipe Contreras

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).