Fossil SCM

Add the "describe" command. This shows the commit hash along with (if applicable) its youngest ancestor with a non-propagating tag and the number of commits since that.

danield 2022-03-30 11:46 trunk
Commit 3f06ed14feddb8e2bb1102510aac168254b261ff0ddcc0f0a6fa4ec0363e585f
1 file changed +173
+173
--- src/info.c
+++ src/info.c
@@ -3678,5 +3678,178 @@
36783678
db_column_text(&q,2),
36793679
db_column_text(&q,3));
36803680
}
36813681
db_finalize(&q);
36823682
}
3683
+
3684
+#if INTERFACE
3685
+typedef struct CommitDescr {
3686
+ char zLastTag[100];
3687
+ int nCommitsSince;
3688
+ char zArtifactHash[65];
3689
+ int isDirty;
3690
+} CommitDescr;
3691
+#endif
3692
+
3693
+int describe_commit(const char *zName, const char *matchGlob,
3694
+ CommitDescr *descr){
3695
+ int rid;
3696
+ const char *zUuid;
3697
+ int nRet = 0;
3698
+ Stmt q;
3699
+
3700
+ rid = symbolic_name_to_rid(zName, "ci"); /* only commits */
3701
+
3702
+ if( rid<=0 ){
3703
+ /* Commit does not exist */
3704
+ *(descr->zLastTag) = 0;
3705
+ descr->nCommitsSince = -1;
3706
+ *(descr->zArtifactHash) = 0;
3707
+ descr->isDirty = -1;
3708
+ return -1;
3709
+ }
3710
+
3711
+ zUuid = rid_to_uuid(rid);
3712
+ memcpy(descr->zArtifactHash, zUuid, strlen(zUuid)+1);
3713
+ descr->isDirty = unsaved_changes(0);
3714
+
3715
+ db_multi_exec(
3716
+ "DROP TABLE IF EXISTS singletonTaggedAncestors;"
3717
+ "CREATE TEMP TABLE singletonTaggedAncestors AS"
3718
+ " WITH RECURSIVE "
3719
+ " singletonTaggedCommits(rid,mtime,shorttag) AS ("
3720
+ " SELECT DISTINCT b.rid,e.mtime,substr(t.tagname,5) AS shorttag"
3721
+ " FROM blob b"
3722
+ " INNER JOIN event e ON e.objid=b.rid"
3723
+ " INNER JOIN tagxref tx ON tx.rid=b.rid"
3724
+ " INNER JOIN tag t ON t.tagid=tx.tagid"
3725
+ " WHERE e.type='ci'"
3726
+ " AND tx.tagtype=1"
3727
+ " AND t.tagname GLOB 'sym-%q'"
3728
+ " ),"
3729
+ " parent(pid,cid,isCP,isPrim) AS ("
3730
+ " SELECT plink.pid, plink.cid, 0, isPrim FROM plink"
3731
+ " UNION ALL"
3732
+ " SELECT parentid, childid, 1, 0 FROM cherrypick WHERE NOT isExclude"
3733
+ " ),"
3734
+ " ancestor(rid, mtime, isCP, isPrim) AS ("
3735
+ " SELECT objid, mtime, 0, 1 FROM event WHERE objid=%d"
3736
+ " UNION"
3737
+ " SELECT parent.pid, event.mtime, parent.isCP, parent.isPrim"
3738
+ " FROM ancestor, parent, event"
3739
+ " WHERE parent.cid=ancestor.rid"
3740
+ " AND event.objid=parent.pid"
3741
+ " AND NOT ancestor.isCP"
3742
+ " AND (event.mtime >= "
3743
+ " (SELECT max(mtime) FROM singletonTaggedCommits"
3744
+ " WHERE mtime<=(SELECT mtime FROM event WHERE objid=%d)))"
3745
+ " ORDER BY mtime DESC"
3746
+ " LIMIT 1000000"
3747
+ " ) "
3748
+ "SELECT rid, mtime, isCP, isPrim, ROW_NUMBER() OVER (ORDER BY mtime DESC) rn"
3749
+ " FROM ancestor",
3750
+ (matchGlob ? matchGlob : "*"), rid, rid
3751
+ );
3752
+
3753
+ db_prepare(&q,
3754
+ "SELECT ta.rid, ta.mtime, ta.rn, b.uuid, substr(t.tagname, 5)"
3755
+ " FROM singletonTaggedAncestors ta"
3756
+ " INNER JOIN blob b ON b.rid=ta.rid"
3757
+ " INNER JOIN tagxref tx ON tx.rid=ta.rid"
3758
+ " INNER JOIN tag t ON tx.tagid=t.tagid"
3759
+ " WHERE tx.tagtype=1 AND t.tagname GLOB 'sym-%q' "
3760
+ " AND rn=(SELECT MAX(rn) FROM singletonTaggedAncestors)"
3761
+ " ORDER BY tx.mtime DESC, t.tagname DESC LIMIT 1",
3762
+ (matchGlob ? matchGlob : "*")
3763
+ );
3764
+
3765
+ if( db_step(&q)==SQLITE_ROW ){
3766
+ const char *lastTag = db_column_text(&q, 4);
3767
+ memcpy(descr->zLastTag, lastTag, strlen(lastTag)+1);
3768
+ descr->nCommitsSince = db_column_int(&q, 2)-1;
3769
+ nRet = 0;
3770
+ }else{
3771
+ /* no ancestor commit with a fitting singleton tag found */
3772
+ *(descr->zLastTag) = 0;
3773
+ descr->nCommitsSince = -1;
3774
+ nRet = -2;
3775
+ }
3776
+
3777
+ db_finalize(&q);
3778
+ return nRet;
3779
+}
3780
+
3781
+/*
3782
+** COMMAND: describe
3783
+**
3784
+** Usage: %fossil describe ?VERSION? ?OPTIONS?
3785
+**
3786
+** Provide a description of the given VERSION by showing a non-propagating
3787
+** tag of the youngest tagged ancestor, followed by the number of commits
3788
+** since that, and the short hash of VERSION. If VERSION and the found
3789
+** ancestor refer to the same commit, the last two components are omitted,
3790
+** unless --long is provided.
3791
+**
3792
+** If no VERSION is provided, describe the current checked-out version. When
3793
+** no fitting tagged ancestor is found, show only the short hash of VERSION.
3794
+**
3795
+** Options:
3796
+**
3797
+** --digits Display so many hex digits of the hash (default 10)
3798
+** -d|--dirty Show whether there are changes to be committed
3799
+** --long Always show all three components
3800
+** --match GLOB Consider only non-propagating tags matching GLOB
3801
+*/
3802
+void describe_cmd(void){
3803
+ const char *zName;
3804
+ const char *zMatchGlob;
3805
+ const char *zDigits;
3806
+ int nDigits;
3807
+ int bDirtyFlag = 0;
3808
+ int bLongFlag = 0;
3809
+ CommitDescr descr;
3810
+
3811
+ db_find_and_open_repository(0,0);
3812
+ bDirtyFlag = find_option("dirty","d",0)!=0;
3813
+ bLongFlag = find_option("long","",0)!=0;
3814
+ zMatchGlob = find_option("match", 0, 1);
3815
+ zDigits = find_option("digits", 0, 1);
3816
+
3817
+ if ( !zDigits || ((nDigits=atoi(zDigits))==0) ){
3818
+ nDigits = 10;
3819
+ }
3820
+
3821
+ /* We should be done with options.. */
3822
+ verify_all_options();
3823
+ if( g.argc<3 ){
3824
+ zName = "current";
3825
+ }else{
3826
+ zName = g.argv[2];
3827
+ }
3828
+
3829
+ if( bDirtyFlag ){
3830
+ if ( g.argc>=3 ) fossil_fatal("cannot use --dirty with specific checkin");
3831
+ }
3832
+
3833
+ switch( describe_commit(zName, zMatchGlob, &descr) ){
3834
+ case -1:
3835
+ fossil_fatal("commit %s does not exist", zName);
3836
+ break;
3837
+ case -2:
3838
+ fossil_print("%.*s%s\n", nDigits, descr.zArtifactHash,
3839
+ bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : "");
3840
+ break;
3841
+ case 0:
3842
+ if( descr.nCommitsSince==0 && !bLongFlag ){
3843
+ fossil_print("%s%s\n", descr.zLastTag,
3844
+ bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : "");
3845
+ }else{
3846
+ fossil_print("%s-%d-%.*s%s\n", descr.zLastTag,
3847
+ descr.nCommitsSince, nDigits, descr.zArtifactHash,
3848
+ bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : "");
3849
+ }
3850
+ break;
3851
+ default:
3852
+ fossil_fatal("cannot describe commit");
3853
+ break;
3854
+ }
3855
+}
36833856
--- src/info.c
+++ src/info.c
@@ -3678,5 +3678,178 @@
3678 db_column_text(&q,2),
3679 db_column_text(&q,3));
3680 }
3681 db_finalize(&q);
3682 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3683
--- src/info.c
+++ src/info.c
@@ -3678,5 +3678,178 @@
3678 db_column_text(&q,2),
3679 db_column_text(&q,3));
3680 }
3681 db_finalize(&q);
3682 }
3683
3684 #if INTERFACE
3685 typedef struct CommitDescr {
3686 char zLastTag[100];
3687 int nCommitsSince;
3688 char zArtifactHash[65];
3689 int isDirty;
3690 } CommitDescr;
3691 #endif
3692
3693 int describe_commit(const char *zName, const char *matchGlob,
3694 CommitDescr *descr){
3695 int rid;
3696 const char *zUuid;
3697 int nRet = 0;
3698 Stmt q;
3699
3700 rid = symbolic_name_to_rid(zName, "ci"); /* only commits */
3701
3702 if( rid<=0 ){
3703 /* Commit does not exist */
3704 *(descr->zLastTag) = 0;
3705 descr->nCommitsSince = -1;
3706 *(descr->zArtifactHash) = 0;
3707 descr->isDirty = -1;
3708 return -1;
3709 }
3710
3711 zUuid = rid_to_uuid(rid);
3712 memcpy(descr->zArtifactHash, zUuid, strlen(zUuid)+1);
3713 descr->isDirty = unsaved_changes(0);
3714
3715 db_multi_exec(
3716 "DROP TABLE IF EXISTS singletonTaggedAncestors;"
3717 "CREATE TEMP TABLE singletonTaggedAncestors AS"
3718 " WITH RECURSIVE "
3719 " singletonTaggedCommits(rid,mtime,shorttag) AS ("
3720 " SELECT DISTINCT b.rid,e.mtime,substr(t.tagname,5) AS shorttag"
3721 " FROM blob b"
3722 " INNER JOIN event e ON e.objid=b.rid"
3723 " INNER JOIN tagxref tx ON tx.rid=b.rid"
3724 " INNER JOIN tag t ON t.tagid=tx.tagid"
3725 " WHERE e.type='ci'"
3726 " AND tx.tagtype=1"
3727 " AND t.tagname GLOB 'sym-%q'"
3728 " ),"
3729 " parent(pid,cid,isCP,isPrim) AS ("
3730 " SELECT plink.pid, plink.cid, 0, isPrim FROM plink"
3731 " UNION ALL"
3732 " SELECT parentid, childid, 1, 0 FROM cherrypick WHERE NOT isExclude"
3733 " ),"
3734 " ancestor(rid, mtime, isCP, isPrim) AS ("
3735 " SELECT objid, mtime, 0, 1 FROM event WHERE objid=%d"
3736 " UNION"
3737 " SELECT parent.pid, event.mtime, parent.isCP, parent.isPrim"
3738 " FROM ancestor, parent, event"
3739 " WHERE parent.cid=ancestor.rid"
3740 " AND event.objid=parent.pid"
3741 " AND NOT ancestor.isCP"
3742 " AND (event.mtime >= "
3743 " (SELECT max(mtime) FROM singletonTaggedCommits"
3744 " WHERE mtime<=(SELECT mtime FROM event WHERE objid=%d)))"
3745 " ORDER BY mtime DESC"
3746 " LIMIT 1000000"
3747 " ) "
3748 "SELECT rid, mtime, isCP, isPrim, ROW_NUMBER() OVER (ORDER BY mtime DESC) rn"
3749 " FROM ancestor",
3750 (matchGlob ? matchGlob : "*"), rid, rid
3751 );
3752
3753 db_prepare(&q,
3754 "SELECT ta.rid, ta.mtime, ta.rn, b.uuid, substr(t.tagname, 5)"
3755 " FROM singletonTaggedAncestors ta"
3756 " INNER JOIN blob b ON b.rid=ta.rid"
3757 " INNER JOIN tagxref tx ON tx.rid=ta.rid"
3758 " INNER JOIN tag t ON tx.tagid=t.tagid"
3759 " WHERE tx.tagtype=1 AND t.tagname GLOB 'sym-%q' "
3760 " AND rn=(SELECT MAX(rn) FROM singletonTaggedAncestors)"
3761 " ORDER BY tx.mtime DESC, t.tagname DESC LIMIT 1",
3762 (matchGlob ? matchGlob : "*")
3763 );
3764
3765 if( db_step(&q)==SQLITE_ROW ){
3766 const char *lastTag = db_column_text(&q, 4);
3767 memcpy(descr->zLastTag, lastTag, strlen(lastTag)+1);
3768 descr->nCommitsSince = db_column_int(&q, 2)-1;
3769 nRet = 0;
3770 }else{
3771 /* no ancestor commit with a fitting singleton tag found */
3772 *(descr->zLastTag) = 0;
3773 descr->nCommitsSince = -1;
3774 nRet = -2;
3775 }
3776
3777 db_finalize(&q);
3778 return nRet;
3779 }
3780
3781 /*
3782 ** COMMAND: describe
3783 **
3784 ** Usage: %fossil describe ?VERSION? ?OPTIONS?
3785 **
3786 ** Provide a description of the given VERSION by showing a non-propagating
3787 ** tag of the youngest tagged ancestor, followed by the number of commits
3788 ** since that, and the short hash of VERSION. If VERSION and the found
3789 ** ancestor refer to the same commit, the last two components are omitted,
3790 ** unless --long is provided.
3791 **
3792 ** If no VERSION is provided, describe the current checked-out version. When
3793 ** no fitting tagged ancestor is found, show only the short hash of VERSION.
3794 **
3795 ** Options:
3796 **
3797 ** --digits Display so many hex digits of the hash (default 10)
3798 ** -d|--dirty Show whether there are changes to be committed
3799 ** --long Always show all three components
3800 ** --match GLOB Consider only non-propagating tags matching GLOB
3801 */
3802 void describe_cmd(void){
3803 const char *zName;
3804 const char *zMatchGlob;
3805 const char *zDigits;
3806 int nDigits;
3807 int bDirtyFlag = 0;
3808 int bLongFlag = 0;
3809 CommitDescr descr;
3810
3811 db_find_and_open_repository(0,0);
3812 bDirtyFlag = find_option("dirty","d",0)!=0;
3813 bLongFlag = find_option("long","",0)!=0;
3814 zMatchGlob = find_option("match", 0, 1);
3815 zDigits = find_option("digits", 0, 1);
3816
3817 if ( !zDigits || ((nDigits=atoi(zDigits))==0) ){
3818 nDigits = 10;
3819 }
3820
3821 /* We should be done with options.. */
3822 verify_all_options();
3823 if( g.argc<3 ){
3824 zName = "current";
3825 }else{
3826 zName = g.argv[2];
3827 }
3828
3829 if( bDirtyFlag ){
3830 if ( g.argc>=3 ) fossil_fatal("cannot use --dirty with specific checkin");
3831 }
3832
3833 switch( describe_commit(zName, zMatchGlob, &descr) ){
3834 case -1:
3835 fossil_fatal("commit %s does not exist", zName);
3836 break;
3837 case -2:
3838 fossil_print("%.*s%s\n", nDigits, descr.zArtifactHash,
3839 bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : "");
3840 break;
3841 case 0:
3842 if( descr.nCommitsSince==0 && !bLongFlag ){
3843 fossil_print("%s%s\n", descr.zLastTag,
3844 bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : "");
3845 }else{
3846 fossil_print("%s-%d-%.*s%s\n", descr.zLastTag,
3847 descr.nCommitsSince, nDigits, descr.zArtifactHash,
3848 bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : "");
3849 }
3850 break;
3851 default:
3852 fossil_fatal("cannot describe commit");
3853 break;
3854 }
3855 }
3856

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button