Fossil

Check-in [36ca266479]
Login

Check-in [36ca266479]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add the new "fossil describe" command.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 36ca26647977e7e96678e11cebc55f31f70713c7004014db07fa9030e83491b0
User & Date: drh 2022-03-30 14:45:15.066
Context
2022-03-30
21:11
Fix the describe_commit() routine so that it is faster and simpler and so that it ignores tags that are applied to more than one check-in. ... (check-in: 41dc5fc000 user: drh tags: trunk)
15:08
Add "--describe" flag to the "version" command to provide context to the most recent major commit tagged with "version*". Run "touch manifest.descr" in the repo root prior to the first build, then build it a second time to get it bootstrapped. ... (check-in: 4329553d51 user: danield tags: version-cmd-describe)
14:45
Add the new "fossil describe" command. ... (check-in: 36ca266479 user: drh tags: trunk)
14:36
Fix segfault and add message for ambiguous checkins. ... (Closed-Leaf check-in: e501d74440 user: danield tags: describe-cmd)
2022-03-28
13:39
Bug fix to the abs() function of pikchr. ... (check-in: b80ae0215e user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/info.c.
3676
3677
3678
3679
3680
3681
3682

































































































































































































      db_column_text(&q,0),
      db_column_text(&q,1),
      db_column_text(&q,2),
      db_column_text(&q,3));
  }
  db_finalize(&q);
}








































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
      db_column_text(&q,0),
      db_column_text(&q,1),
      db_column_text(&q,2),
      db_column_text(&q,3));
  }
  db_finalize(&q);
}

#if INTERFACE
/* 
** Description of a checkin relative to an earlier, tagged checkin.
*/
typedef struct CommitDescr {
  char *zRelTagname;        /* Tag name on the relative checkin */
  int nCommitsSince;        /* Number of commits since then */
  char *zCommitHash;        /* Hash of the described checkin */
  int isDirty;              /* Working directory has uncommitted changes */
} CommitDescr;
#endif

/*
** Describe the checkin given by 'zName', and possibly matching 'matchGlob',
** relative to an earlier, tagged checkin. Use 'descr' for the output.
**
** Finds the closest ancestor (ignoring merge-ins) that has a non-propagating
** label tag and the number of steps backwards that we had to search in
** order to find that tag.
**
** Return values:
**       0: ok
**      -1: zName does not resolve to a commit
**      -2: zName resolves to more than a commit
**      -3: no ancestor commit with a fitting non-propagating tag found
*/
int describe_commit(const char *zName, const char *matchGlob,
                    CommitDescr *descr){
  int rid;             /* rid for zName */
  const char *zUuid;   /* Hash of rid */
  int nRet = 0;        /* Value to be returned */
  Stmt q;              /* Query for tagged ancestors */

  rid = symbolic_name_to_rid(zName, "ci"); /* only commits */

  if( rid<=0 ){
    /* Commit does not exist or is ambiguous */
    descr->zRelTagname = mprintf("");
    descr->nCommitsSince = -1;
    descr->zCommitHash = mprintf("");
    descr->isDirty = -1;
    return (rid-1);
  }

  zUuid = rid_to_uuid(rid);
  descr->zCommitHash = mprintf("%s", zUuid);
  descr->isDirty = unsaved_changes(0);

  db_multi_exec(
    "DROP TABLE IF EXISTS singletonTaggedAncestors;"
    "CREATE TEMP TABLE singletonTaggedAncestors AS"
    "  WITH RECURSIVE "
    "  singletonTaggedCommits(rid,mtime,shorttag) AS ("
    "    SELECT DISTINCT b.rid,e.mtime,substr(t.tagname,5) AS shorttag"
    "          FROM blob b"
    "    INNER JOIN event e ON e.objid=b.rid"
    "    INNER JOIN tagxref tx ON tx.rid=b.rid"
    "    INNER JOIN tag t ON t.tagid=tx.tagid"
    "         WHERE e.type='ci'"
    "           AND tx.tagtype=1"
    "           AND t.tagname GLOB 'sym-%q'"
    "  ),"
    "  parent(pid,cid,isCP,isPrim) AS ("
    "    SELECT plink.pid, plink.cid, 0, isPrim FROM plink"
    "    UNION ALL"
    "    SELECT parentid, childid, 1, 0 FROM cherrypick WHERE NOT isExclude"
    "  ),"
    "  ancestor(rid, mtime, isCP, isPrim) AS ("
    "    SELECT objid, mtime, 0, 1 FROM event WHERE objid=%d"
    "    UNION"
    "    SELECT parent.pid, event.mtime, parent.isCP, parent.isPrim"
    "      FROM ancestor, parent, event"
    "     WHERE parent.cid=ancestor.rid"
    "       AND event.objid=parent.pid"
    "       AND NOT ancestor.isCP"
    "       AND (event.mtime >= "
    "              (SELECT max(mtime) FROM singletonTaggedCommits"
    "                 WHERE mtime<=(SELECT mtime FROM event WHERE objid=%d)))"
    "     ORDER BY mtime DESC"
    "     LIMIT 1000000"
    "  ) "
    "SELECT rid, mtime, isCP, isPrim, ROW_NUMBER() OVER (ORDER BY mtime DESC) rn"
    "  FROM ancestor",
    (matchGlob ? matchGlob : "*"), rid, rid
  );

  db_prepare(&q,
    "SELECT ta.rid, ta.mtime, ta.rn, b.uuid, substr(t.tagname, 5)"
    "        FROM singletonTaggedAncestors ta"
    "  INNER JOIN blob b ON b.rid=ta.rid"
    "  INNER JOIN tagxref tx ON tx.rid=ta.rid"
    "  INNER JOIN tag t ON tx.tagid=t.tagid"
    "       WHERE tx.tagtype=1 AND t.tagname GLOB 'sym-%q' "
    "         AND rn=(SELECT MAX(rn) FROM singletonTaggedAncestors)"
    "    ORDER BY tx.mtime DESC, t.tagname DESC LIMIT 1",
    (matchGlob ? matchGlob : "*")     
  );

  if( db_step(&q)==SQLITE_ROW ){
    const char *lastTag = db_column_text(&q, 4);
    descr->zRelTagname = mprintf("%s", lastTag);
    descr->nCommitsSince = db_column_int(&q, 2)-1;
    nRet = 0;
  }else{
    /* no ancestor commit with a fitting singleton tag found */
    descr->zRelTagname = mprintf("");
    descr->nCommitsSince = -1;
    nRet = -3;
  }

  db_finalize(&q);
  return nRet;
}

/*
** COMMAND: describe
**
** Usage: %fossil describe ?VERSION? ?OPTIONS?
**
** Provide a description of the given VERSION by showing a non-propagating
** tag of the youngest tagged ancestor, followed by the number of commits
** since that, and the short hash of VERSION.  If VERSION and the found 
** ancestor refer to the same commit, the last two components are omitted,
** unless --long is provided.
**
** If no VERSION is provided, describe the current checked-out version.  When
** no fitting tagged ancestor is found, show only the short hash of VERSION.
**
** Options:
**
**    --digits           Display so many hex digits of the hash (default 10)
**    -d|--dirty         Show whether there are changes to be committed
**    --long             Always show all three components
**    --match GLOB       Consider only non-propagating tags matching GLOB
*/
void describe_cmd(void){
  const char *zName;
  const char *zMatchGlob;
  const char *zDigits;
  int nDigits;
  int bDirtyFlag = 0;
  int bLongFlag = 0;
  CommitDescr descr;

  db_find_and_open_repository(0,0);
  bDirtyFlag = find_option("dirty","d",0)!=0;
  bLongFlag = find_option("long","",0)!=0;
  zMatchGlob = find_option("match", 0, 1);
  zDigits = find_option("digits", 0, 1);

  if ( !zDigits || ((nDigits=atoi(zDigits))==0) ){
    nDigits = 10;
  }

  /* We should be done with options.. */
  verify_all_options();
  if( g.argc<3 ){
    zName = "current";
  }else{
    zName = g.argv[2];
  }

  if( bDirtyFlag ){
    if ( g.argc>=3 ) fossil_fatal("cannot use --dirty with specific checkin");
  }

  switch( describe_commit(zName, zMatchGlob, &descr) ){
    case -1:
      fossil_fatal("commit %s does not exist", zName);
      break;
    case -2:
      fossil_fatal("commit %s is ambiguous", zName);
      break;
    case -3:
      fossil_print("%.*s%s\n", nDigits, descr.zCommitHash,
                  bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : "");
      break;
    case 0:
      if( descr.nCommitsSince==0 && !bLongFlag ){
        fossil_print("%s%s\n", descr.zRelTagname,
                    bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : "");
      }else{
        fossil_print("%s-%d-%.*s%s\n", descr.zRelTagname,
                    descr.nCommitsSince, nDigits, descr.zCommitHash,
                    bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : "");
      }
      break;
    default:
      fossil_fatal("cannot describe commit");
      break;
  }
}