diff --git a/all.sas b/all.sas
index 7dbe8cd..064d032 100644
--- a/all.sas
+++ b/all.sas
@@ -4608,8 +4608,7 @@ data &out_ds(compress=no
if did=0 then do;
putlog "NOTE: This directory is empty, or does not exist - &path";
msg=sysmsg();
- put msg;
- put _all_;
+ put (_all_)(=);
stop;
end;
/* attribute is OS-dependent - could be "Directory" or "Directory Name" */
@@ -8168,6 +8167,51 @@ create table &outds as
)
%mend mp_getpk;
+/**
+ @file
+ @brief Stages files in a GIT repo
+ @details Uses the output dataset from mp_gitstatus.sas to determine the files
+ that should be staged.
+
+ If `STAGED ne "TRUE"` then the file is staged.
+
+ Usage:
+
+ %let dir=%sysfunc(pathname(work))/core;
+ %let repo=https://github.com/sasjs/core;
+ %put source clone rc=%sysfunc(GITFN_CLONE(&repo,&dir));
+ %mf_writefile(&dir/somefile.txt,l1=some content)
+ %mf_deletefile(&dir/package.json)
+ %mp_gitstatus(&dir,outds=work.gitstatus)
+
+ %mp_gitadd(&dir,inds=work.gitstatus)
+
+ @param [in] gitdir The directory containing the GIT repository
+ @param [in] inds= (work.mp_gitadd) The input dataset with the list of files
+ to stage. Will accept the output from mp_gitstatus(), else just use a table
+ with the following columns:
+ @li path $1024 - relative path to the file in the repo
+ @li staged $32 - whether the file is staged (TRUE or FALSE)
+ @li status $64 - either new, deleted, or modified
+
+ @param [in] mdebug= (0) Set to 1 to enable DEBUG messages
+
+
Related Files
+ @li mp_gitadd.test.sas
+ @li mp_gitstatus.sas
+
+**/
+
+%macro mp_gitadd(gitdir,inds=work.mp_gitadd,mdebug=0);
+
+data _null_;
+ set &inds;
+ if STAGED ne "TRUE";
+ rc=git_index_add("&gitdir",cats(path),status);
+ if rc ne 0 or &mdebug=1 then put rc=;
+run;
+
+%mend mp_gitadd;
/**
@file
@brief Pulls latest release info from a GIT repository
@@ -8242,6 +8286,73 @@ libname &outlib JSON fileref=&fref;
%end;
%mend mp_gitreleaseinfo;
+/**
+ @file
+ @brief Creates a dataset with the output from `GIT_STATUS()`
+ @details Uses `git_status()` to fetch the number of changed files, then
+ iterates with `git_status_get()`, inserting all attributes into an output
+ dataset.
+
+ Usage:
+
+ %let dir=%sysfunc(pathname(work))/core;
+ %let repo=https://github.com/sasjs/core;
+ %put source clone rc=%sysfunc(GITFN_CLONE(&repo,&dir));
+ %mf_writefile(&dir/somefile.txt,l1=some content)
+ %mf_deletefile(&dir/package.json)
+
+ %mp_gitstatus(&dir,outds=work.gitstatus)
+
+ More info on these functions is in this [helpful paper]
+(https://www.sas.com/content/dam/SAS/support/en/sas-global-forum-proceedings/2019/3057-2019.pdf)
+ by Danny Zimmerman.
+
+ @param [in] gitdir The directory containing the GIT repository
+ @param [out] outds= (work.git_status) The output dataset to create. Vars:
+ @li gitdir $1024 - directory of repo
+ @li path $1024 - relative path to the file in the repo
+ @li staged $32 - whether the file is staged (TRUE or FALSE)
+ @li status $64 - either new, deleted, or modified
+ @li cnt - number of files
+ @li n - the "nth" file in the list from git_status()
+
+ @param [in] mdebug= (0) Set to 1 to enable DEBUG messages
+
+ Related Files
+ @li mp_gitstatus.test.sas
+ @li mp_gitadd.sas
+
+**/
+
+%macro mp_gitstatus(gitdir,outds=work.mp_gitstatus,mdebug=0);
+
+data &outds;
+ LENGTH gitdir path $ 1024 STATUS $ 64 STAGED $ 32;
+ call missing (of _all_);
+ gitdir=symget('gitdir');
+ cnt=git_status(trim(gitdir));
+ if cnt=-1 then do;
+ put "The libgit2 library is unavailable and no Git operations can be used.";
+ put "See: https://stackoverflow.com/questions/74082874";
+ end;
+ else if cnt=-2 then do;
+ put "The libgit2 library is available, but the status function failed.";
+ put "See the log for details.";
+ end;
+ else do n=1 to cnt;
+ rc=GIT_STATUS_GET(n,gitdir,'PATH',path);
+ rc=GIT_STATUS_GET(n,gitdir,'STAGED',staged);
+ rc=GIT_STATUS_GET(n,gitdir,'STATUS',status);
+ output;
+ %if &mdebug=1 %then %do;
+ putlog (_all_)(=);
+ %end;
+ end;
+ rc=git_status_free(gitdir);
+ drop rc cnt;
+run;
+
+%mend mp_gitstatus;
/**
@file
@brief Performs a text substitution on a file
@@ -8729,7 +8840,7 @@ run;
create a hash for each directory also.
This makes use of the new `hashing_file()` and `hashing` functions, available
- since 9.4m6. Interestingly, these can even be used in pure macro, eg:
+ since 9.4m6. Interestingly, those functions can be used in pure macro, eg:
%put %sysfunc(hashing_file(md5,/path/to/file.blob,0));
@@ -8754,8 +8865,9 @@ run;
@li If a folder contains other folders, start from the bottom of the tree -
the folder hashes cascade upwards so you know immediately if there is a
change in a sub/sub directory
- @li If the folder has no content (empty) then it is ignored. No hash created.
+ @li If a subfolder has no content (empty) then it is ignored. No hash created.
@li If the file is empty, it is also ignored / no hash created.
+ @li If the target directory (&inloc) is empty, &outds will also be empty
SAS Macros
@li mp_dirlist.sas
@@ -8796,7 +8908,7 @@ run;
iftrue=%str(1=1)
)/*/STORE SOURCE*/;
-%local curlevel tempds ;
+%local curlevel tempds maxlevel;
%if not(%eval(%unquote(&iftrue))) %then %return;
@@ -8832,6 +8944,7 @@ proc sort data=&outds ;
by descending level directory file_path;
run;
+%let maxlevel=0;
data _null_;
set &outds;
call symputx('maxlevel',level,'l');
diff --git a/base/mp_dirlist.sas b/base/mp_dirlist.sas
index 2af46d2..b22034d 100644
--- a/base/mp_dirlist.sas
+++ b/base/mp_dirlist.sas
@@ -101,8 +101,7 @@ data &out_ds(compress=no
if did=0 then do;
putlog "NOTE: This directory is empty, or does not exist - &path";
msg=sysmsg();
- put msg;
- put _all_;
+ put (_all_)(=);
stop;
end;
/* attribute is OS-dependent - could be "Directory" or "Directory Name" */
diff --git a/base/mp_hashdirectory.sas b/base/mp_hashdirectory.sas
index 01e31af..3aa4e44 100644
--- a/base/mp_hashdirectory.sas
+++ b/base/mp_hashdirectory.sas
@@ -5,7 +5,7 @@
create a hash for each directory also.
This makes use of the new `hashing_file()` and `hashing` functions, available
- since 9.4m6. Interestingly, these can even be used in pure macro, eg:
+ since 9.4m6. Interestingly, those functions can be used in pure macro, eg:
%put %sysfunc(hashing_file(md5,/path/to/file.blob,0));
@@ -30,8 +30,9 @@
@li If a folder contains other folders, start from the bottom of the tree -
the folder hashes cascade upwards so you know immediately if there is a
change in a sub/sub directory
- @li If the folder has no content (empty) then it is ignored. No hash created.
+ @li If a subfolder has no content (empty) then it is ignored. No hash created.
@li If the file is empty, it is also ignored / no hash created.
+ @li If the target directory (&inloc) is empty, &outds will also be empty
SAS Macros
@li mp_dirlist.sas
@@ -72,7 +73,7 @@
iftrue=%str(1=1)
)/*/STORE SOURCE*/;
-%local curlevel tempds ;
+%local curlevel tempds maxlevel;
%if not(%eval(%unquote(&iftrue))) %then %return;
@@ -108,6 +109,7 @@ proc sort data=&outds ;
by descending level directory file_path;
run;
+%let maxlevel=0;
data _null_;
set &outds;
call symputx('maxlevel',level,'l');
diff --git a/sasjs/sasjsconfig.json b/sasjs/sasjsconfig.json
index b420ce8..0705176 100644
--- a/sasjs/sasjsconfig.json
+++ b/sasjs/sasjsconfig.json
@@ -67,7 +67,7 @@
},
{
"name": "server",
- "serverUrl": "https://sas.4gl.io",
+ "serverUrl": "https://sas9.4gl.io",
"serverType": "SASJS",
"httpsAgentOptions": {
"allowInsecureRequests": false
diff --git a/tests/base/mp_hashdirectory.test.sas b/tests/base/mp_hashdirectory.test.sas
index 08a0457..e661846 100644
--- a/tests/base/mp_hashdirectory.test.sas
+++ b/tests/base/mp_hashdirectory.test.sas
@@ -131,3 +131,19 @@ data _null_;
set work.hashes2;
put file_hash file_path;
run;
+
+/* check that it works when the target directory is missing */
+
+%mp_hashdirectory(&fpath/doesnotexist,outds=work.hashes3,maxdepth=MAX)
+
+%mp_assert(
+ iftrue=(&syscc=0),
+ desc=No errors when directory is missing,
+ outds=work.test_results
+)
+
+%mp_assert(
+ iftrue=(%mf_nobs(work.hashes3)=0),
+ desc=no records created when directory is missing,
+ outds=work.test_results
+)
\ No newline at end of file