|  | @@ -132,7 +132,7 @@ def git_glob(glob):
 | 
	
		
			
				|  |  |    global gg_cache
 | 
	
		
			
				|  |  |    if glob in gg_cache: return gg_cache[glob]
 | 
	
		
			
				|  |  |    r = set(subprocess
 | 
	
		
			
				|  |  | -      .check_output(['git', 'ls-files', glob])
 | 
	
		
			
				|  |  | +      .check_output(['git', 'ls-files', os.path.join(git_root, glob)])
 | 
	
		
			
				|  |  |        .decode('utf-8')
 | 
	
		
			
				|  |  |        .strip()
 | 
	
		
			
				|  |  |        .splitlines())
 | 
	
	
		
			
				|  | @@ -150,7 +150,7 @@ def expand_directives(root, directives):
 | 
	
		
			
				|  |  |          globs[glob].append(directive.who)
 | 
	
		
			
				|  |  |    # expand owners for intersecting globs
 | 
	
		
			
				|  |  |    sorted_globs = sorted(globs.keys(),
 | 
	
		
			
				|  |  | -                        key=lambda g: len(git_glob(os.path.join(root, g))),
 | 
	
		
			
				|  |  | +                        key=lambda g: len(git_glob(full_dir(root, g))),
 | 
	
		
			
				|  |  |                          reverse=True)
 | 
	
		
			
				|  |  |    out_globs = collections.OrderedDict()
 | 
	
		
			
				|  |  |    for glob_add in sorted_globs:
 | 
	
	
		
			
				|  | @@ -162,8 +162,9 @@ def expand_directives(root, directives):
 | 
	
		
			
				|  |  |        files_have = git_glob(full_dir(root, glob_have))
 | 
	
		
			
				|  |  |        intersect = files_have.intersection(files_add)
 | 
	
		
			
				|  |  |        if intersect:
 | 
	
		
			
				|  |  | -        for f in files_add:
 | 
	
		
			
				|  |  | +        for f in sorted(files_add): # sorted to ensure merge stability
 | 
	
		
			
				|  |  |            if f not in intersect:
 | 
	
		
			
				|  |  | +            print("X", root, glob_add, glob_have)
 | 
	
		
			
				|  |  |              out_globs[os.path.relpath(f, start=root)] = who_add
 | 
	
		
			
				|  |  |          for who in who_have:
 | 
	
		
			
				|  |  |            if who not in out_globs[glob_add]:
 | 
	
	
		
			
				|  | @@ -182,8 +183,9 @@ def add_parent_to_globs(parent, globs, globs_dir):
 | 
	
		
			
				|  |  |            intersect = files_parent.intersection(files_child)
 | 
	
		
			
				|  |  |            gglob_who_orig = gglob_who.copy()
 | 
	
		
			
				|  |  |            if intersect:
 | 
	
		
			
				|  |  | -            for f in files_child:
 | 
	
		
			
				|  |  | +            for f in sorted(files_child): # sorted to ensure merge stability
 | 
	
		
			
				|  |  |                if f not in intersect:
 | 
	
		
			
				|  |  | +                print("Y", full_dir(owners.dir, oglob), full_dir(globs_dir, gglob))
 | 
	
		
			
				|  |  |                  who = gglob_who_orig.copy()
 | 
	
		
			
				|  |  |                  globs[os.path.relpath(f, start=globs_dir)] = who
 | 
	
		
			
				|  |  |              for who in oglob_who:
 | 
	
	
		
			
				|  | @@ -199,6 +201,7 @@ with open(args.out, 'w') as out:
 | 
	
		
			
				|  |  |    out.write('# Auto-generated by the tools/mkowners/mkowners.py tool\n')
 | 
	
		
			
				|  |  |    out.write('# Uses OWNERS files in different modules throughout the\n')
 | 
	
		
			
				|  |  |    out.write('# repository as the source of truth for module ownership.\n')
 | 
	
		
			
				|  |  | +  written_globs = []
 | 
	
		
			
				|  |  |    while todo:
 | 
	
		
			
				|  |  |      head, *todo = todo
 | 
	
		
			
				|  |  |      if head.parent and not head.parent in done:
 | 
	
	
		
			
				|  | @@ -207,6 +210,21 @@ with open(args.out, 'w') as out:
 | 
	
		
			
				|  |  |      globs = expand_directives(head.dir, head.directives)
 | 
	
		
			
				|  |  |      add_parent_to_globs(head.parent, globs, head.dir)
 | 
	
		
			
				|  |  |      for glob, owners in globs.items():
 | 
	
		
			
				|  |  | -      out.write('/%s %s\n' % (
 | 
	
		
			
				|  |  | -          full_dir(head.dir, glob), ' '.join(owners)))
 | 
	
		
			
				|  |  | +      skip = False
 | 
	
		
			
				|  |  | +      for glob1, owners1, dir1 in reversed(written_globs):
 | 
	
		
			
				|  |  | +        files = git_glob(full_dir(head.dir, glob))
 | 
	
		
			
				|  |  | +        files1 = git_glob(full_dir(dir1, glob1))
 | 
	
		
			
				|  |  | +        intersect = files.intersection(files1)
 | 
	
		
			
				|  |  | +        if files == intersect:
 | 
	
		
			
				|  |  | +          if sorted(owners) == sorted(owners1):
 | 
	
		
			
				|  |  | +            skip = True # nothing new in this rule
 | 
	
		
			
				|  |  | +            break
 | 
	
		
			
				|  |  | +        elif intersect:
 | 
	
		
			
				|  |  | +          # continuing would cause a semantic change since some files are
 | 
	
		
			
				|  |  | +          # affected differently by this rule and CODEOWNERS is order dependent
 | 
	
		
			
				|  |  | +          break
 | 
	
		
			
				|  |  | +      if not skip:
 | 
	
		
			
				|  |  | +        out.write('/%s %s\n' % (
 | 
	
		
			
				|  |  | +            full_dir(head.dir, glob), ' '.join(owners)))
 | 
	
		
			
				|  |  | +        written_globs.append((glob, owners, head.dir))
 | 
	
		
			
				|  |  |      done.add(head.dir)
 |