2570
referent, update->old_target);
2571
return -1;
2572
}
2573
+
2574
+struct migration_data {
2575
+ struct ref_store *old_refs;
2576
+ struct ref_transaction *transaction;
2577
+ struct strbuf *errbuf;
2578
+};
2579
+
2580
+static int migrate_one_ref(const char *refname, const struct object_id *oid,
2581
+ int flags, void *cb_data)
2582
+{
2583
+ struct migration_data *data = cb_data;
2584
+ struct strbuf symref_target = STRBUF_INIT;
2585
+ int ret;
2586
+
2587
+ if (flags & REF_ISSYMREF) {
2588
+ ret = refs_read_symbolic_ref(data->old_refs, refname, &symref_target);
2589
+ if (ret < 0)
2590
+ goto done;
2591
+
2592
+ ret = ref_transaction_update(data->transaction, refname, NULL, null_oid(),
2593
+ symref_target.buf, NULL,
2594
+ REF_SKIP_CREATE_REFLOG | REF_NO_DEREF, NULL, data->errbuf);
2595
+ if (ret < 0)
2596
+ goto done;
2597
+ } else {
2598
+ ret = ref_transaction_create(data->transaction, refname, oid,
2599
+ REF_SKIP_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION,
2600
+ NULL, data->errbuf);
2601
+ if (ret < 0)
2602
+ goto done;
2603
+ }
2604
+
2605
+done:
2606
+ strbuf_release(&symref_target);
2607
+ return ret;
2608
+}
2609
+
2610
+static int move_files(const char *from_path, const char *to_path, struct strbuf *errbuf)
2611
+{
2612
+ struct strbuf from_buf = STRBUF_INIT, to_buf = STRBUF_INIT;
2613
+ size_t from_len, to_len;
2614
+ DIR *from_dir;
2615
+ int ret;
2616
+
2617
+ from_dir = opendir(from_path);
2618
+ if (!from_dir) {
2619
+ strbuf_addf(errbuf, "could not open source directory '%s': %s",
2620
+ from_path, strerror(errno));
2621
+ ret = -1;
2622
+ goto done;
2623
+ }
2624
+
2625
+ strbuf_addstr(&from_buf, from_path);
2626
+ strbuf_complete(&from_buf, '/');
2627
+ from_len = from_buf.len;
2628
+
2629
+ strbuf_addstr(&to_buf, to_path);
2630
+ strbuf_complete(&to_buf, '/');
2631
+ to_len = to_buf.len;
2632
+
2633
+ while (1) {
2634
+ struct dirent *ent;
2635
+
2636
+ errno = 0;
2637
+ ent = readdir(from_dir);
2638
+ if (!ent)
2639
+ break;
2640
+
2641
+ if (!strcmp(ent->d_name, ".") ||
2642
+ !strcmp(ent->d_name, ".."))
2643
+ continue;
2644
+
2645
+ strbuf_setlen(&from_buf, from_len);
2646
+ strbuf_addstr(&from_buf, ent->d_name);
2647
+
2648
+ strbuf_setlen(&to_buf, to_len);
2649
+ strbuf_addstr(&to_buf, ent->d_name);
2650
+
2651
+ ret = rename(from_buf.buf, to_buf.buf);
2652
+ if (ret < 0) {
2653
+ strbuf_addf(errbuf, "could not link file '%s' to '%s': %s",
2654
+ from_buf.buf, to_buf.buf, strerror(errno));
2655
+ goto done;
2656
+ }
2657
+ }
2658
+
2659
+ if (errno) {
2660
+ strbuf_addf(errbuf, "could not read entry from directory '%s': %s",
2661
+ from_path, strerror(errno));
2662
+ ret = -1;
2663
+ goto done;
2664
+ }
2665
+
2666
+ ret = 0;
2667
+
2668
+done:
2669
+ strbuf_release(&from_buf);
2670
+ strbuf_release(&to_buf);
2671
+ if (from_dir)
2672
+ closedir(from_dir);
2673
+ return ret;
2674
+}
2675
+
2676
+static int count_reflogs(const char *reflog UNUSED, void *payload)
2677
+{
2678
+ size_t *reflog_count = payload;
2679
+ (*reflog_count)++;
2680
+ return 0;
2681
+}
2682
+
2683
+static int has_worktrees(void)
2684
+{
2685
+ struct worktree **worktrees = get_worktrees();
2686
+ int ret = 0;
2687
+ size_t i;
2688
+
2689
+ for (i = 0; worktrees[i]; i++) {
2690
+ if (is_main_worktree(worktrees[i]))
2691
+ continue;
2692
+ ret = 1;
2693
+ }
2694
+
2695
+ free_worktrees(worktrees);
2696
+ return ret;
2697
+}
2698
+
2699
+int repo_migrate_ref_storage_format(struct repository *repo,
2700
+ enum ref_storage_format format,
2701
+ unsigned int flags,
2702
+ struct strbuf *errbuf)
2703
+{
2704
+ struct ref_store *old_refs = NULL, *new_refs = NULL;
2705
+ struct ref_transaction *transaction = NULL;
2706
+ struct strbuf new_gitdir = STRBUF_INIT;
2707
+ struct migration_data data;
2708
+ size_t reflog_count = 0;
2709
+ int did_migrate_refs = 0;
2710
+ int ret;
2711
+
2712
+ if (repo->ref_storage_format == format) {
2713
+ strbuf_addstr(errbuf, "current and new ref storage format are equal");
2714
+ ret = -1;
2715
+ goto done;
2716
+ }
2717
+
2718
+ old_refs = get_main_ref_store(repo);
2719
+
2720
+ /*
2721
+ * We do not have any interfaces that would allow us to write many
2722
+ * reflog entries. Once we have them we can remove this restriction.
2723
+ */
2724
+ if (refs_for_each_reflog(old_refs, count_reflogs, &reflog_count) < 0) {
2725
+ strbuf_addstr(errbuf, "cannot count reflogs");
2726
+ ret = -1;
2727
+ goto done;
2728
+ }
2729
+ if (reflog_count) {
2730
+ strbuf_addstr(errbuf, "migrating reflogs is not supported yet");
2731
+ ret = -1;
2732
+ goto done;
2733
+ }
2734
+
2735
+ /*
2736
+ * Worktrees complicate the migration because every worktree has a
2737
+ * separate ref storage. While it should be feasible to implement, this
2738
+ * is pushed out to a future iteration.
2739
+ *
2740
+ * TODO: we should really be passing the caller-provided repository to
2741
+ * `has_worktrees()`, but our worktree subsystem doesn't yet support
2742
+ * that.
2743
+ */
2744
+ if (has_worktrees()) {
2745
+ strbuf_addstr(errbuf, "migrating repositories with worktrees is not supported yet");
2746
+ ret = -1;
2747
+ goto done;
2748
+ }
2749
+
2750
+ /*
2751
+ * The overall logic looks like this:
2752
+ *
2753
+ * 1. Set up a new temporary directory and initialize it with the new
2754
+ * format. This is where all refs will be migrated into.
2755
+ *
2756
+ * 2. Enumerate all refs and write them into the new ref storage.
2757
+ * This operation is safe as we do not yet modify the main
2758
+ * repository.
2759
+ *
2760
+ * 3. If we're in dry-run mode then we are done and can hand over the
2761
+ * directory to the caller for inspection. If not, we now start
2762
+ * with the destructive part.
2763
+ *
2764
+ * 4. Delete the old ref storage from disk. As we have a copy of refs
2765
+ * in the new ref storage it's okay(ish) if we now get interrupted
2766
+ * as there is an equivalent copy of all refs available.
2767
+ *
2768
+ * 5. Move the new ref storage files into place.
2769
+ *
2770
+ * 6. Change the repository format to the new ref format.
2771
+ */
2772
+ strbuf_addf(&new_gitdir, "%s/%s", old_refs->gitdir, "ref_migration.XXXXXX");
2773
+ if (!mkdtemp(new_gitdir.buf)) {
2774
+ strbuf_addf(errbuf, "cannot create migration directory: %s",
2775
+ strerror(errno));
2776
+ ret = -1;
2777
+ goto done;
2778
+ }
2779
+
2780
+ new_refs = ref_store_init(repo, format, new_gitdir.buf,
2781
+ REF_STORE_ALL_CAPS);
2782
+ ret = ref_store_create_on_disk(new_refs, 0, errbuf);
2783
+ if (ret < 0)
2784
+ goto done;
2785
+
2786
+ transaction = ref_store_transaction_begin(new_refs, errbuf);
2787
+ if (!transaction)
2788
+ goto done;
2789
+
2790
+ data.old_refs = old_refs;
2791
+ data.transaction = transaction;
2792
+ data.errbuf = errbuf;
2793
+
2794
+ /*
2795
+ * We need to use the internal `do_for_each_ref()` here so that we can
2796
+ * also include broken refs and symrefs. These would otherwise be
2797
+ * skipped silently.
2798
+ *
2799
+ * Ideally, we would do this call while locking the old ref storage
2800
+ * such that there cannot be any concurrent modifications. We do not
2801
+ * have the infra for that though, and the "files" backend does not
2802
+ * allow for a central lock due to its design. It's thus on the user to
2803
+ * ensure that there are no concurrent writes.
2804
+ */
2805
+ ret = do_for_each_ref(old_refs, "", NULL, migrate_one_ref, 0,
2806
+ DO_FOR_EACH_INCLUDE_ROOT_REFS | DO_FOR_EACH_INCLUDE_BROKEN,
2807
+ &data);
2808
+ if (ret < 0)
2809
+ goto done;
2810
+
2811
+ /*
2812
+ * TODO: we might want to migrate to `initial_ref_transaction_commit()`
2813
+ * here, which is more efficient for the files backend because it would
2814
+ * write new refs into the packed-refs file directly. At this point,
2815
+ * the files backend doesn't handle pseudo-refs and symrefs correctly
2816
+ * though, so this requires some more work.
2817
+ */
2818
+ ret = ref_transaction_commit(transaction, errbuf);
2819
+ if (ret < 0)
2820
+ goto done;
2821
+ did_migrate_refs = 1;
2822
+
2823
+ if (flags & REPO_MIGRATE_REF_STORAGE_FORMAT_DRYRUN) {
2824
+ printf(_("Finished dry-run migration of refs, "
2825
+ "the result can be found at '%s'\n"), new_gitdir.buf);
2826
+ ret = 0;
2827
+ goto done;
2828
+ }
2829
+
2830
+ /*
2831
+ * Until now we were in the non-destructive phase, where we only
2832
+ * populated the new ref store. From hereon though we are about
2833
+ * to get hands by deleting the old ref store and then moving
2834
+ * the new one into place.
2835
+ *
2836
+ * Assuming that there were no concurrent writes, the new ref
2837
+ * store should have all information. So if we fail from hereon
2838
+ * we may be in an in-between state, but it would still be able
2839
+ * to recover by manually moving remaining files from the
2840
+ * temporary migration directory into place.
2841
+ */
2842
+ ret = ref_store_remove_on_disk(old_refs, errbuf);
2843
+ if (ret < 0)
2844
+ goto done;
2845
+
2846
+ ret = move_files(new_gitdir.buf, old_refs->gitdir, errbuf);
2847
+ if (ret < 0)
2848
+ goto done;
2849
+
2850
+ if (rmdir(new_gitdir.buf) < 0)
2851
+ warning_errno(_("could not remove temporary migration directory '%s'"),
2852
+ new_gitdir.buf);
2853
+
2854
+ /*
2855
+ * We have migrated the repository, so we now need to adjust the
2856
+ * repository format so that clients will use the new ref store.
2857
+ * We also need to swap out the repository's main ref store.
2858
+ */
2859
+ initialize_repository_version(hash_algo_by_ptr(repo->hash_algo), format, 1);
2860
+
2861
+ free(new_refs->gitdir);
2862
+ new_refs->gitdir = xstrdup(old_refs->gitdir);
2863
+ repo->refs_private = new_refs;
2864
+ ref_store_release(old_refs);
2865
+
2866
+ ret = 0;
2867
+
2868
+done:
2869
+ if (ret && did_migrate_refs) {
2870
+ strbuf_complete(errbuf, '\n');
2871
+ strbuf_addf(errbuf, _("migrated refs can be found at '%s'"),
2872
+ new_gitdir.buf);
2873
+ }
2874
+
2875
+ if (ret && new_refs)
2876
+ ref_store_release(new_refs);
2877
+ ref_transaction_free(transaction);
2878
+ strbuf_release(&new_gitdir);
2879
+ return ret;
2880
+}