21
#include "color.h"
22
#include "commit.h"
23
#include "diff.h"
24
+#include "diffcore.h"
25
#include "diff-merges.h"
26
#include "revision.h"
27
#include "log-tree.h"
28
#include "oid-array.h"
29
+#include "oidset.h"
30
#include "tag.h"
31
#include "reflog-walk.h"
32
#include "patch-ids.h"
45
#include "utf8.h"
46
47
#include "commit-reach.h"
48
+#include "promisor-remote.h"
49
#include "range-diff.h"
50
#include "tmp-objdir.h"
51
#include "tree.h"
52
+#include "userdiff.h"
53
#include "write-or-die.h"
54
55
#define MAIL_DEFAULT_WRAP 72
2606
}
2607
}
2608
2609
+/*
2610
+ * Enumerate blob OIDs from a single commit's diff, inserting them into blobs.
2611
+ * Skips files whose userdiff driver explicitly declares binary status
2612
+ * (drv->binary > 0), since patch-ID uses oid_to_hex() for those and
2613
+ * never reads blob content. Use userdiff_find_by_path() since
2614
+ * diff_filespec_load_driver() is static in diff.c.
2615
+ *
2616
+ * Clean up with diff_queue_clear() (from diffcore.h).
2617
+ */
2618
+static void collect_diff_blob_oids(struct commit *commit,
2619
+ struct diff_options *opts,
2620
+ struct oidset *blobs)
2621
+{
2622
+ struct diff_queue_struct *q;
2623
+
2624
+ /*
2625
+ * Merge commits are filtered out by patch_id_defined() in patch-ids.c,
2626
+ * so we'll never be called with one.
2627
+ */
2628
+ assert(!commit->parents || !commit->parents->next);
2629
+
2630
+ if (commit->parents)
2631
+ diff_tree_oid(&commit->parents->item->object.oid,
2632
+ &commit->object.oid, "", opts);
2633
+ else
2634
+ diff_root_tree_oid(&commit->object.oid, "", opts);
2635
+ diffcore_std(opts);
2636
+
2637
+ q = &diff_queued_diff;
2638
+ for (int i = 0; i < q->nr; i++) {
2639
+ struct diff_filepair *p = q->queue[i];
2640
+ struct userdiff_driver *drv;
2641
+
2642
+ /* Skip binary files */
2643
+ drv = userdiff_find_by_path(opts->repo->index, p->one->path);
2644
+ if (drv && drv->binary > 0)
2645
+ continue;
2646
+
2647
+ if (DIFF_FILE_VALID(p->one) &&
2648
+ odb_read_object_info_extended(opts->repo->objects,
2649
+ &p->one->oid, NULL,
2650
+ OBJECT_INFO_FOR_PREFETCH))
2651
+ oidset_insert(blobs, &p->one->oid);
2652
+ if (DIFF_FILE_VALID(p->two) &&
2653
+ odb_read_object_info_extended(opts->repo->objects,
2654
+ &p->two->oid, NULL,
2655
+ OBJECT_INFO_FOR_PREFETCH))
2656
+ oidset_insert(blobs, &p->two->oid);
2657
+ }
2658
+ diff_queue_clear(q);
2659
+}
2660
+
2661
+static int always_match(const void *cmp_data UNUSED,
2662
+ const struct hashmap_entry *entry1 UNUSED,
2663
+ const struct hashmap_entry *entry2 UNUSED,
2664
+ const void *keydata UNUSED)
2665
+{
2666
+ return 0;
2667
+}
2668
+
2669
+/*
2670
+ * Prefetch blobs for git cherry in partial clones.
2671
+ *
2672
+ * Called between the revision walk (which builds the head-side
2673
+ * commit list) and the has_commit_patch_id() comparison loop.
2674
+ *
2675
+ * Uses a cmpfn-swap trick to avoid reading blobs: temporarily
2676
+ * replaces the hashmap's comparison function with a trivial
2677
+ * always-match function, so hashmap_get()/hashmap_get_next() match
2678
+ * any entry with the same oidhash bucket. These are the set of oids
2679
+ * that would trigger patch_id_neq() during normal lookup and cause
2680
+ * blobs to be read on demand, and we want to prefetch them all at
2681
+ * once instead.
2682
+ */
2683
+static void prefetch_cherry_blobs(struct repository *repo,
2684
+ struct commit_list *list,
2685
+ struct patch_ids *ids)
2686
+{
2687
+ struct oidset blobs = OIDSET_INIT;
2688
+ hashmap_cmp_fn original_cmpfn;
2689
+
2690
+ /* Exit if we're not in a partial clone */
2691
+ if (!repo_has_promisor_remote(repo))
2692
+ return;
2693
+
2694
+ /* Save original cmpfn, replace with always_match */
2695
+ original_cmpfn = ids->patches.cmpfn;
2696
+ ids->patches.cmpfn = always_match;
2697
+
2698
+ /* Find header-only collisions, gather blobs from those commits */
2699
+ for (struct commit_list *l = list; l; l = l->next) {
2700
+ struct commit *c = l->item;
2701
+ bool match_found = false;
2702
+ for (struct patch_id *cur = patch_id_iter_first(c, ids);
2703
+ cur;
2704
+ cur = patch_id_iter_next(cur, ids)) {
2705
+ match_found = true;
2706
+ collect_diff_blob_oids(cur->commit, &ids->diffopts,
2707
+ &blobs);
2708
+ }
2709
+ if (match_found)
2710
+ collect_diff_blob_oids(c, &ids->diffopts, &blobs);
2711
+ }
2712
+
2713
+ /* Restore original cmpfn */
2714
+ ids->patches.cmpfn = original_cmpfn;
2715
+
2716
+ /* If we have any blobs to fetch, fetch them */
2717
+ if (oidset_size(&blobs)) {
2718
+ struct oid_array to_fetch = OID_ARRAY_INIT;
2719
+ struct oidset_iter iter;
2720
+ const struct object_id *oid;
2721
+
2722
+ oidset_iter_init(&blobs, &iter);
2723
+ while ((oid = oidset_iter_next(&iter)))
2724
+ oid_array_append(&to_fetch, oid);
2725
+
2726
+ promisor_remote_get_direct(repo, to_fetch.oid, to_fetch.nr);
2727
+
2728
+ oid_array_clear(&to_fetch);
2729
+ }
2730
+
2731
+ oidset_clear(&blobs);
2732
+}
2733
+
2734
int cmd_cherry(int argc,
2735
const char **argv,
2736
const char *prefix,
2802
commit_list_insert(commit, &list);
2803
}
2804
2805
+ prefetch_cherry_blobs(the_repository, list, &ids);
2806
+
2807
for (struct commit_list *l = list; l; l = l->next) {
2808
char sign = '+';
2809