From: Kevin Willford <hidden> Date: 2017-08-28 20:29:44
Code was using two string_lists, one for the directories and
one for the files. The code never checks the lists independently
so we should be able to only use one list. The string_list also
is a O(log n) for lookup and insertion. Switching this to use a
hashmap will give O(1) which will save some time when there are
millions of paths that will be checked.
Also cleaned up a memory leak and method where the return was not
being used.
Kevin Willford (3):
merge-recursive: fix memory leak
merge-recursive: remove return value from get_files_dirs
merge-recursive: change current file dir string_lists to hashmap
merge-recursive.c | 68 +++++++++++++++++++++++++++++++++++++++----------------
merge-recursive.h | 3 +--
2 files changed, 49 insertions(+), 22 deletions(-)
--
2.14.1.329.g6edf0add19
From: Kevin Willford <hidden> Date: 2017-08-28 20:29:47
In merge_trees if process_renames or process_entry returns less
than zero, the method will just return and not free re_merge,
re_head, or entries.
This change cleans up the allocated variables before returning
to the caller.
Signed-off-by: Kevin Willford <redacted>
---
merge-recursive.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
From: Ben Peart <hidden> Date: 2017-08-28 22:43:06
On 8/28/2017 4:28 PM, Kevin Willford wrote:
In merge_trees if process_renames or process_entry returns less
than zero, the method will just return and not free re_merge,
re_head, or entries.
This change cleans up the allocated variables before returning
to the caller.
Signed-off-by: Kevin Willford <redacted>
From: Jeff King <hidden> Date: 2017-08-29 08:12:13
On Mon, Aug 28, 2017 at 02:28:27PM -0600, Kevin Willford wrote:
In merge_trees if process_renames or process_entry returns less
than zero, the method will just return and not free re_merge,
re_head, or entries.
This change cleans up the allocated variables before returning
to the caller.
Good catch. I suspect this function could stand to be refactored a bit.
For instance, pulling those inner bits of the conditional into a helper
would let us do:
re_merge = get_renames(...);
... other setup ...
clean = our_new_helper(re_merge, ...);
string_clear(re_merge);
... other cleanup ...
if (clean < 0)
return clean;
without having to resort to a goto. But certainly I don't mind this much
more minimal change, which fixes the actual functionality problem.
-Peff
From: Kevin Willford <hidden> Date: 2017-08-28 20:29:51
The return value of the get_files_dirs call is never being used.
Looking at the history of the file and it was originally only
being used for debug output statements. Also when
read_tree_recursive return value is non zero it is changed to
zero. This leads me to believe that it doesn't matter if
read_tree_recursive gets an error.
Since the debug output has been removed and the caller isn't
checking the return value there is no reason to keep calulating
and returning a value.
Signed-off-by: Kevin Willford <redacted>
---
merge-recursive.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
From: Ben Peart <hidden> Date: 2017-08-28 22:45:34
On 8/28/2017 4:28 PM, Kevin Willford wrote:
The return value of the get_files_dirs call is never being used.
Looking at the history of the file and it was originally only
being used for debug output statements. Also when
read_tree_recursive return value is non zero it is changed to
zero. This leads me to believe that it doesn't matter if
read_tree_recursive gets an error.
Since the debug output has been removed and the caller isn't
checking the return value there is no reason to keep calulating
and returning a value.
Did a quick search and you're right, nothing is using the return value.
No reason to spend time calculating it. Looks good.
From: Jeff King <hidden> Date: 2017-08-29 08:19:29
On Mon, Aug 28, 2017 at 06:45:29PM -0400, Ben Peart wrote:
quoted
Since the debug output has been removed and the caller isn't
checking the return value there is no reason to keep calulating
and returning a value.
Did a quick search and you're right, nothing is using the return value. No
reason to spend time calculating it. Looks good.
This is actually one we can reasonably trust the compiler to check for
us. Though of course it doesn't hurt to look at the callers and make
sure that it's sane that they do not care about this return value (I
don't know merge-recursive.c very well, but the current return value
does seem kind of useless).
-Peff
From: Jeff King <hidden> Date: 2017-08-29 08:18:01
On Mon, Aug 28, 2017 at 02:28:28PM -0600, Kevin Willford wrote:
The return value of the get_files_dirs call is never being used.
Looking at the history of the file and it was originally only
being used for debug output statements. Also when
read_tree_recursive return value is non zero it is changed to
zero. This leads me to believe that it doesn't matter if
read_tree_recursive gets an error.
Or that the function is buggy. :)
I'm tempted to say that we should probably die() when
read_tree_recursive fails. This should only happen if we fail to parse
the tree, or if our callback (save_files_dirs here) returns failure, and
the latter looks like it never happens.
Since the debug output has been removed and the caller isn't
checking the return value there is no reason to keep calulating
and returning a value.
Agreed, and I'm happy to see dead code go.
Minor nit: s/calulating/calculating/ in your commit message.
-Peff
From: Kevin Willford <hidden> Date: 2017-08-29 15:58:07
On Mon, Aug 28, 2017 at 02:28:28PM -0600, Kevin Willford wrote:
quoted
The return value of the get_files_dirs call is never being used.
Looking at the history of the file and it was originally only
being used for debug output statements. Also when
read_tree_recursive return value is non zero it is changed to
zero. This leads me to believe that it doesn't matter if
read_tree_recursive gets an error.
Or that the function is buggy. :)
That was one of my questions as well. Should read_tree_recursive
be propagating a -1 and merge_trees be checking for that and bail
when the call to get_files_dirs return is < 0? I made a commit with
this change and ran the tests and they all still passed so either this
return really doesn't matter or there are not sufficient tests covering
it.
I went with this change because it was not changing any of the
current functionality and if we find a case where it matters that
read_tree_recursive fails due to bad tree or something else we
can address it then.
I'm tempted to say that we should probably die() when
read_tree_recursive fails. This should only happen if we fail to parse
the tree, or if our callback (save_files_dirs here) returns failure, and
the latter looks like it never happens.
quoted
Since the debug output has been removed and the caller isn't
checking the return value there is no reason to keep calulating
and returning a value.
Agreed, and I'm happy to see dead code go.
Minor nit: s/calulating/calculating/ in your commit message.
When will that spell checker for git messages be ready? ;)
From: Jeff King <hidden> Date: 2017-08-29 16:50:47
On Tue, Aug 29, 2017 at 03:58:00PM +0000, Kevin Willford wrote:
quoted
quoted
The return value of the get_files_dirs call is never being used.
Looking at the history of the file and it was originally only
being used for debug output statements. Also when
read_tree_recursive return value is non zero it is changed to
zero. This leads me to believe that it doesn't matter if
read_tree_recursive gets an error.
Or that the function is buggy. :)
That was one of my questions as well. Should read_tree_recursive
be propagating a -1 and merge_trees be checking for that and bail
when the call to get_files_dirs return is < 0? I made a commit with
this change and ran the tests and they all still passed so either this
return really doesn't matter or there are not sufficient tests covering
it.
Right, in a normal flow I'd say that it should propagate the -1, etc.
But since the only possible error is parse_tree() failing, which happens
in a corrupt repository, I think it would be OK to just die(). That
saves having to deal with error propagation. It's not very graceful,
perhaps, but the important thing is that we don't quietly ignore the
error.
I went with this change because it was not changing any of the
current functionality and if we find a case where it matters that
read_tree_recursive fails due to bad tree or something else we
can address it then.
I think it's worth doing while we're thinking about it now, but it
certainly can be a separate patch.
-Peff
From: Stefan Beller <hidden> Date: 2017-08-31 18:12:07
On Tue, Aug 29, 2017 at 8:58 AM, Kevin Willford [off-list ref] wrote:
quoted
On Mon, Aug 28, 2017 at 02:28:28PM -0600, Kevin Willford wrote:
quoted
The return value of the get_files_dirs call is never being used.
Looking at the history of the file and it was originally only
being used for debug output statements. Also when
read_tree_recursive return value is non zero it is changed to
zero. This leads me to believe that it doesn't matter if
read_tree_recursive gets an error.
Or that the function is buggy. :)
That was one of my questions as well. Should read_tree_recursive
be propagating a -1 and merge_trees be checking for that and bail
when the call to get_files_dirs return is < 0? I made a commit with
this change and ran the tests and they all still passed so either this
return really doesn't matter or there are not sufficient tests covering
it.
I went with this change because it was not changing any of the
current functionality and if we find a case where it matters that
read_tree_recursive fails due to bad tree or something else we
can address it then.
quoted
I'm tempted to say that we should probably die() when
read_tree_recursive fails. This should only happen if we fail to parse
the tree, or if our callback (save_files_dirs here) returns failure, and
the latter looks like it never happens.
quoted
Since the debug output has been removed and the caller isn't
checking the return value there is no reason to keep calulating
and returning a value.
Agreed, and I'm happy to see dead code go.
Minor nit: s/calulating/calculating/ in your commit message.
When will that spell checker for git messages be ready? ;)
Different workflows need different setups apparently.
(me, as a heavy user of git-gui, likes the builtin spell checker,
though I need to find a way to train it to accept git lingo, such
as "submodule", or "gitlink")
Maybe:
https://tarasalenin.wordpress.com/2010/09/11/configure-git-gui-spell-checker-on-windows/
If you do not use git-gui... you are at the merci of your $EDITOR
($GIT_EDITOR, or core.editor) to have spell checking.
From: Kevin Willford <hidden> Date: 2017-08-28 20:29:57
The code was using two string_lists, one for the directories and
one for the files. The code never checks the lists independently
so we should be able to only use one list. The string_list also
is a O(log n) for lookup and insertion. Switching this to use a
hashmap will give O(1) which will save some time when there are
millions of paths that will be checked.
Signed-off-by: Kevin Willford <redacted>
---
merge-recursive.c | 48 +++++++++++++++++++++++++++++++++++++-----------
merge-recursive.h | 3 +--
2 files changed, 38 insertions(+), 13 deletions(-)
From: Ben Peart <hidden> Date: 2017-08-28 23:06:14
On 8/28/2017 4:28 PM, Kevin Willford wrote:
The code was using two string_lists, one for the directories and
one for the files. The code never checks the lists independently
so we should be able to only use one list. The string_list also
is a O(log n) for lookup and insertion. Switching this to use a
hashmap will give O(1) which will save some time when there are
millions of paths that will be checked.
Good observation that the two string lists were never used
independently. Perhaps the intent was to help the O(log n) issue by
splitting them up? A single hashmap is much faster. Looks good.
From: Jeff King <hidden> Date: 2017-08-29 08:42:25
On Mon, Aug 28, 2017 at 02:28:29PM -0600, Kevin Willford wrote:
The code was using two string_lists, one for the directories and
one for the files. The code never checks the lists independently
so we should be able to only use one list. The string_list also
is a O(log n) for lookup and insertion. Switching this to use a
hashmap will give O(1) which will save some time when there are
millions of paths that will be checked.
Makes sense. I think when this code was written we didn't have the
generalized hashmap.c, which is why it relies on string lists.
+static unsigned int (*pathhash)(const char *path);
+static int (*pathcmp)(const char *a, const char *b);
These global function pointers are sufficiently out of the ordinary that
it might be worth adding a comment about how they're intended to be
set and used.
I also suspect they could be stuffed into the merge_options struct along
with the hashmap itself, and then passed via the cmp_data pointer (which
is relatively new-ish; I won't be surprised if you wrote this patch
before that existed and forward-ported it).
I think you can use hashmap_get_from_hash() here to avoid dealing with
"dummy" yourself.
quoted hunk
@@ -1941,8 +1966,7 @@ int merge_trees(struct merge_options *o, if (unmerged_cache()) { struct string_list *entries, *re_head, *re_merge; int i;- string_list_clear(&o->current_file_set, 1);- string_list_clear(&o->current_directory_set, 1);+ hashmap_init(&o->current_file_dir_set, path_hashmap_cmp, NULL, 512);
This hunk seems funny. string_list_clear() assumes the input is an
already-initialized string list, and we drop all of its entries. But
hashmap_init assumes we have an _uninitialized_ chunk of memory, and
throws away whatever was there.
Do we ever hit this code path when there's actually something in the
hashmap?
I'd think we would want to hashmap_init() at the same place we used to
string_list_init(). I.e., in init_merge_options.
Ah, I think this answers my question above. We only care about the
contents during this one bit of code, so we do an init/free pair. And
since there's no "clear" function for a hashmap that drops the entries
without resetting things like the comparison function, we _have_ to
call hashmap_init in the earlier hunk.
That does make me wonder if it would be sane to just declare the hashmap
local to this block, which would make its lifetime much more clear. I
guess having it in merge_options is convenient for passing it around,
though.
The rest of the patch looks like a straight-forward conversion. With the
understanding I have above, I think everything here is correct, but
there are a few minor bits it might be worth addressing to make the code
more clear.
-Peff
From: Junio C Hamano <hidden> Date: 2017-09-06 03:35:31
I needed this squashed into before I could even compile the
resulting code. Perhaps my compiler is stale?
merge-recursive.c:28:22: error: declaration does not declare anything [-Werror]
struct hashmap_entry;
^
merge-recursive.c:29:7: error: flexible array member in otherwise empty struct
char path[FLEX_ARRAY];
^
---
merge-recursive.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)