521
}
522
523
struct directory_versions {
524
+ /*
525
+ * versions: list of (basename -> version_info)
526
+ *
527
+ * The basenames are in reverse lexicographic order of full pathnames,
528
+ * as processed in process_entries(). This puts all entries within
529
+ * a directory together, and covers the directory itself after
530
+ * everything within it, allowing us to write subtrees before needing
531
+ * to record information for the tree itself.
532
+ */
533
struct string_list versions;
534
+
535
+ /*
536
+ * offsets: list of (full relative path directories -> integer offsets)
537
+ *
538
+ * Since versions contains basenames from files in multiple different
539
+ * directories, we need to know which entries in versions correspond
540
+ * to which directories. Values of e.g.
541
+ * "" 0
542
+ * src 2
543
+ * src/moduleA 5
544
+ * Would mean that entries 0-1 of versions are files in the toplevel
545
+ * directory, entries 2-4 are files under src/, and the remaining
546
+ * entries starting at index 5 are files under src/moduleA/.
547
+ */
548
+ struct string_list offsets;
549
+
550
+ /*
551
+ * last_directory: directory that previously processed file found in
552
+ *
553
+ * last_directory starts NULL, but records the directory in which the
554
+ * previous file was found within. As soon as
555
+ * directory(current_file) != last_directory
556
+ * then we need to start updating accounting in versions & offsets.
557
+ * Note that last_directory is always the last path in "offsets" (or
558
+ * NULL if "offsets" is empty) so this exists just for quick access.
559
+ */
560
+ const char *last_directory;
561
+
562
+ /* last_directory_len: cached computation of strlen(last_directory) */
563
+ unsigned last_directory_len;
564
};
565
566
static int tree_entry_order(const void *a_, const void *b_)
635
basename)->util = &mi->result;
636
}
637
638
+static void write_completed_directory(struct merge_options *opt,
639
+ const char *new_directory_name,
640
+ struct directory_versions *info)
641
+{
642
+ const char *prev_dir;
643
+ struct merged_info *dir_info = NULL;
644
+ unsigned int offset;
645
+
646
+ /*
647
+ * Some explanation of info->versions and info->offsets...
648
+ *
649
+ * process_entries() iterates over all relevant files AND
650
+ * directories in reverse lexicographic order, and calls this
651
+ * function. Thus, an example of the paths that process_entries()
652
+ * could operate on (along with the directories for those paths
653
+ * being shown) is:
654
+ *
655
+ * xtract.c ""
656
+ * tokens.txt ""
657
+ * src/moduleB/umm.c src/moduleB
658
+ * src/moduleB/stuff.h src/moduleB
659
+ * src/moduleB/baz.c src/moduleB
660
+ * src/moduleB src
661
+ * src/moduleA/foo.c src/moduleA
662
+ * src/moduleA/bar.c src/moduleA
663
+ * src/moduleA src
664
+ * src ""
665
+ * Makefile ""
666
+ *
667
+ * info->versions:
668
+ *
669
+ * always contains the unprocessed entries and their
670
+ * version_info information. For example, after the first five
671
+ * entries above, info->versions would be:
672
+ *
673
+ * xtract.c <xtract.c's version_info>
674
+ * token.txt <token.txt's version_info>
675
+ * umm.c <src/moduleB/umm.c's version_info>
676
+ * stuff.h <src/moduleB/stuff.h's version_info>
677
+ * baz.c <src/moduleB/baz.c's version_info>
678
+ *
679
+ * Once a subdirectory is completed we remove the entries in
680
+ * that subdirectory from info->versions, writing it as a tree
681
+ * (write_tree()). Thus, as soon as we get to src/moduleB,
682
+ * info->versions would be updated to
683
+ *
684
+ * xtract.c <xtract.c's version_info>
685
+ * token.txt <token.txt's version_info>
686
+ * moduleB <src/moduleB's version_info>
687
+ *
688
+ * info->offsets:
689
+ *
690
+ * helps us track which entries in info->versions correspond to
691
+ * which directories. When we are N directories deep (e.g. 4
692
+ * for src/modA/submod/subdir/), we have up to N+1 unprocessed
693
+ * directories (+1 because of toplevel dir). Corresponding to
694
+ * the info->versions example above, after processing five entries
695
+ * info->offsets will be:
696
+ *
697
+ * "" 0
698
+ * src/moduleB 2
699
+ *
700
+ * which is used to know that xtract.c & token.txt are from the
701
+ * toplevel dirctory, while umm.c & stuff.h & baz.c are from the
702
+ * src/moduleB directory. Again, following the example above,
703
+ * once we need to process src/moduleB, then info->offsets is
704
+ * updated to
705
+ *
706
+ * "" 0
707
+ * src 2
708
+ *
709
+ * which says that moduleB (and only moduleB so far) is in the
710
+ * src directory.
711
+ *
712
+ * One unique thing to note about info->offsets here is that
713
+ * "src" was not added to info->offsets until there was a path
714
+ * (a file OR directory) immediately below src/ that got
715
+ * processed.
716
+ *
717
+ * Since process_entry() just appends new entries to info->versions,
718
+ * write_completed_directory() only needs to do work if the next path
719
+ * is in a directory that is different than the last directory found
720
+ * in info->offsets.
721
+ */
722
+
723
+ /*
724
+ * If we are working with the same directory as the last entry, there
725
+ * is no work to do. (See comments above the directory_name member of
726
+ * struct merged_info for why we can use pointer comparison instead of
727
+ * strcmp here.)
728
+ */
729
+ if (new_directory_name == info->last_directory)
730
+ return;
731
+
732
+ /*
733
+ * If we are just starting (last_directory is NULL), or last_directory
734
+ * is a prefix of the current directory, then we can just update
735
+ * info->offsets to record the offset where we started this directory
736
+ * and update last_directory to have quick access to it.
737
+ */
738
+ if (info->last_directory == NULL ||
739
+ !strncmp(new_directory_name, info->last_directory,
740
+ info->last_directory_len)) {
741
+ uintptr_t offset = info->versions.nr;
742
+
743
+ info->last_directory = new_directory_name;
744
+ info->last_directory_len = strlen(info->last_directory);
745
+ /*
746
+ * Record the offset into info->versions where we will
747
+ * start recording basenames of paths found within
748
+ * new_directory_name.
749
+ */
750
+ string_list_append(&info->offsets,
751
+ info->last_directory)->util = (void*)offset;
752
+ return;
753
+ }
754
+
755
+ /*
756
+ * The next entry that will be processed will be within
757
+ * new_directory_name. Since at this point we know that
758
+ * new_directory_name is within a different directory than
759
+ * info->last_directory, we have all entries for info->last_directory
760
+ * in info->versions and we need to create a tree object for them.
761
+ */
762
+ dir_info = strmap_get(&opt->priv->paths, info->last_directory);
763
+ assert(dir_info);
764
+ offset = (uintptr_t)info->offsets.items[info->offsets.nr-1].util;
765
+ if (offset == info->versions.nr) {
766
+ /*
767
+ * Actually, we don't need to create a tree object in this
768
+ * case. Whenever all files within a directory disappear
769
+ * during the merge (e.g. unmodified on one side and
770
+ * deleted on the other, or files were renamed elsewhere),
771
+ * then we get here and the directory itself needs to be
772
+ * omitted from its parent tree as well.
773
+ */
774
+ dir_info->is_null = 1;
775
+ } else {
776
+ /*
777
+ * Write out the tree to the git object directory, and also
778
+ * record the mode and oid in dir_info->result.
779
+ */
780
+ dir_info->is_null = 0;
781
+ dir_info->result.mode = S_IFDIR;
782
+ write_tree(&dir_info->result.oid, &info->versions, offset,
783
+ opt->repo->hash_algo->rawsz);
784
+ }
785
+
786
+ /*
787
+ * We've now used several entries from info->versions and one entry
788
+ * from info->offsets, so we get rid of those values.
789
+ */
790
+ info->offsets.nr--;
791
+ info->versions.nr = offset;
792
+
793
+ /*
794
+ * Now we've taken care of the completed directory, but we need to
795
+ * prepare things since future entries will be in
796
+ * new_directory_name. (In particular, process_entry() will be
797
+ * appending new entries to info->versions.) So, we need to make
798
+ * sure new_directory_name is the last entry in info->offsets.
799
+ */
800
+ prev_dir = info->offsets.nr == 0 ? NULL :
801
+ info->offsets.items[info->offsets.nr-1].string;
802
+ if (new_directory_name != prev_dir) {
803
+ uintptr_t c = info->versions.nr;
804
+ string_list_append(&info->offsets,
805
+ new_directory_name)->util = (void*)c;
806
+ }
807
+
808
+ /* And, of course, we need to update last_directory to match. */
809
+ info->last_directory = new_directory_name;
810
+ info->last_directory_len = strlen(info->last_directory);
811
+}
812
+
813
/* Per entry merge function */
814
static void process_entry(struct merge_options *opt,
815
const char *path,
908
struct strmap_entry *e;
909
struct string_list plist = STRING_LIST_INIT_NODUP;
910
struct string_list_item *entry;
697
- struct directory_versions dir_metadata = { STRING_LIST_INIT_NODUP };
911
+ struct directory_versions dir_metadata = { STRING_LIST_INIT_NODUP,
912
+ STRING_LIST_INIT_NODUP,
913
+ NULL, 0 };
914
915
if (strmap_empty(&opt->priv->paths)) {
916
oidcpy(result_oid, opt->repo->hash_algo->empty_tree);
930
/*
931
* Iterate over the items in reverse order, so we can handle paths
932
* below a directory before needing to handle the directory itself.
933
+ *
934
+ * This allows us to write subtrees before we need to write trees,
935
+ * and it also enables sane handling of directory/file conflicts
936
+ * (because it allows us to know whether the directory is still in
937
+ * the way when it is time to process the file at the same path).
938
*/
939
for (entry = &plist.items[plist.nr-1]; entry >= plist.items; --entry) {
940
char *path = entry->string;
945
*/
946
struct merged_info *mi = entry->util;
947
948
+ write_completed_directory(opt, mi->directory_name,
949
+ &dir_metadata);
950
if (mi->clean)
951
record_entry_for_tree(&dir_metadata, path, mi);
952
else {
955
}
956
}
957
735
- /*
736
- * TODO: We can't actually write a tree yet, because dir_metadata just
737
- * contains all basenames of all files throughout the tree with their
738
- * mode and hash. Not only is that a nonsensical tree, it will have
739
- * lots of duplicates for paths such as "Makefile" or ".gitignore".
740
- */
741
- die("Not yet implemented; need to process subtrees separately");
958
+ if (dir_metadata.offsets.nr != 1 ||
959
+ (uintptr_t)dir_metadata.offsets.items[0].util != 0) {
960
+ printf("dir_metadata.offsets.nr = %d (should be 1)\n",
961
+ dir_metadata.offsets.nr);
962
+ printf("dir_metadata.offsets.items[0].util = %u (should be 0)\n",
963
+ (unsigned)(uintptr_t)dir_metadata.offsets.items[0].util);
964
+ fflush(stdout);
965
+ BUG("dir_metadata accounting completely off; shouldn't happen");
966
+ }
967
write_tree(result_oid, &dir_metadata.versions, 0,
968
opt->repo->hash_algo->rawsz);
969
string_list_clear(&plist, 0);
970
string_list_clear(&dir_metadata.versions, 0);
971
+ string_list_clear(&dir_metadata.offsets, 0);
972
}
973
974
void merge_switch_to_result(struct merge_options *opt,