FIXME: hotpatch for compatibility with latest hg
[git] / git_remote_helpers / fastimport / helpers.py
1 # Copyright (C) 2008 Canonical Ltd
2 #
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17 """Miscellaneous useful stuff."""
18
19 import os
20
21 def single_plural(n, single, plural):
22     """Return a single or plural form of a noun based on number."""
23     if n == 1:
24         return single
25     else:
26         return plural
27
28
29 def invert_dict(d):
30     """Invert a dictionary with keys matching each value turned into a list."""
31     # Based on recipe from ASPN
32     result = {}
33     for k, v in d.iteritems():
34         keys = result.setdefault(v, [])
35         keys.append(k)
36     return result
37
38
39 def invert_dictset(d):
40     """Invert a dictionary with keys matching a set of values, turned into lists."""
41     # Based on recipe from ASPN
42     result = {}
43     for k, c in d.iteritems():
44         for v in c:
45             keys = result.setdefault(v, [])
46             keys.append(k)
47     return result
48
49
50 def _common_path_and_rest(l1, l2, common=[]):
51     # From http://code.activestate.com/recipes/208993/
52     if len(l1) < 1: return (common, l1, l2)
53     if len(l2) < 1: return (common, l1, l2)
54     if l1[0] != l2[0]: return (common, l1, l2)
55     return _common_path_and_rest(l1[1:], l2[1:], common+[l1[0]])
56
57
58 def common_path(path1, path2):
59     """Find the common bit of 2 paths."""
60     return ''.join(_common_path_and_rest(path1, path2)[0])
61
62
63 def common_directory(paths):
64     """Find the deepest common directory of a list of paths.
65     
66     :return: if no paths are provided, None is returned;
67       if there is no common directory, '' is returned;
68       otherwise the common directory with a trailing / is returned.
69     """
70     def get_dir_with_slash(path):
71         if path == '' or path.endswith('/'):
72             return path
73         else:
74             dirname, basename = os.path.split(path)
75             if dirname == '':
76                 return dirname
77             else:
78                 return dirname + '/'
79
80     if not paths:
81         return None
82     elif len(paths) == 1:
83         return get_dir_with_slash(paths[0])
84     else:
85         common = common_path(paths[0], paths[1])
86         for path in paths[2:]:
87             common = common_path(common, path)
88         return get_dir_with_slash(common)