    001package org.vishia.gitGui;
    002
    003import java.io.File;
    004import java.io.FileNotFoundException;
    005import java.io.IOException;
    006import java.text.SimpleDateFormat;
    007import java.util.Date;
    008import java.util.LinkedList;
    009import java.util.List;
    010import java.util.Map;
    011
    012import org.vishia.cmd.CmdExecuter;
    013import org.vishia.cmd.JZtxtcmdFilepath;
    014import org.vishia.gral.base.GralButton;
    015import org.vishia.gral.base.GralGraphicTimeOrder;
    016import org.vishia.gral.base.GralTable;
    017import org.vishia.gral.base.GralTextBox;
    018import org.vishia.gral.base.GralTextField;
    019import org.vishia.gral.base.GralWindow;
    020import org.vishia.gral.ifc.GralTableLine_ifc;
    021import org.vishia.gral.ifc.GralUserAction;
    022import org.vishia.gral.ifc.GralWindow_ifc;
    023import org.vishia.util.DataAccess;
    024import org.vishia.util.Debugutil;
    025import org.vishia.util.FileList;
    026import org.vishia.util.FilePath;
    027import org.vishia.util.FileSystem;
    028import org.vishia.util.KeyCode;
    029import org.vishia.util.StringPartAppend;
    030
    031
    032/**A Graphical User Interface for basic working in git with remote repository and preserving time stamp of the files.
    033 * <ul>
    034 * <li>Show a working tree with remote repository (option -git-dir on any git command call).
    035 * <li>Lists all changed files from working tree and between any revisions.
    036 * <li>supports commit: via a text file for the commit text (edit with any standard editor) and commit button.
    037 *   On any commit creates a new "_filelist.lst" via {@link FileList} which contains the time stamp of all files.
    038 * <li>restore any file from any older version: select version, select file, right mouse "restore". (git checkout)
    039 *   with restoring the original time stamp.
    040 * </ul>
    041 * @author Hartmut Schorrig
    042 *
    043 */
    044public class GitGui
    045{
    046
    047
    048
    049  /**Version, history and license
    050   * <ul>
    051   * <li>2018-10-28 Hartmut new textfield for cmd, shows the last automatically cmd, enables assembled cmd for git and common cmd.
    052   * <li>2018-10-27 Hartmut new {@link #openNewFileSelector(String, RevisionEntry)} not ready yet.
    053   * <li>2018-10-27 Hartmut chg uses <code>diff --name-status</diff> instead <code>diff --name-only</diff>
    054   *   to build the input for the #wdgTableFiles to show different files. The table has an additional left row for the kind of difference
    055   *   adequate the output of <code>diff --name-status</diff>.
    056   * <li>2018-10-10 Hartmut new {@link #guiRepository(JZtxtcmdFilepath)} as start operation. It checks whether "name.gitRepository" is given
    057   *  and copies "name.gitignore" to ".gitignore" and uses "name.filelist" as filelist. It supports more as one component on one working dir.
    058   * <li>2018-10-10 Hartmut new {@link #getFilePathRepository(File)}. It is invoked in jzTc to get the opened repository by this GUI for add command.
    059   * <li>2017-05-10 Hartmut bugfix {@link CmdExecuter#setCharsetForOutput(String)} to UTF-8 because git outputs cmd output in UTF-8
    060   * <li>2016-12-02 Hartmut chg GitGui some improvements.
    061   * <li>2016-09-23 Hartmut GitGui: ContextMenu in file table
    062   * <li>2016-08-18 Hartmut this version is able to use to view the repository versions, the changed files per version, the changed file to the working tree.
    063   *   It supports view diff with invocation of an external tool. It is the first productive version. But yet with some specific settings yet now.
    064   *   TODO: read a config. Document it. Show the git command line for any action.
    065   * <li>2016-08-24 Hartmut created
    066   * </ul>
    067   *
    068   *
    069   * <b>Copyright/Copyleft</b>:
    070   * For this source the LGPL Lesser General Public License,
    071   * published by the Free Software Foundation is valid.
    072   * It means:
    073   * <ol>
    074   * <li> You can use this source without any restriction for any desired purpose.
    075   * <li> You can redistribute copies of this source to everybody.
    076   * <li> Every user of this source, also the user of redistribute copies
    077   *    with or without payment, must accept this license for further using.
    078   * <li> But the LPGL is not appropriate for a whole software product,
    079   *    if this source is only a part of them. It means, the user
    080   *    must publish this part of source,
    081   *    but don't need to publish the whole source of the own product.
    082   * <li> You can study and modify (improve) this source
    083   *    for own using or for redistribution, but you have to license the
    084   *    modified sources likewise under this LGPL Lesser General Public License.
    085   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
    086   * </ol>
    087   * If you are intent to use this sources without publishing its usage, you can get
    088   * a second license subscribing a special contract with the author.
    089   *
    090   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
    091   *
    092   *
    093   */
    094  public final String sVersion = "2018-10-28";
    095
    096
    097  static class RevisionEntry
    098  {
    099    final String revisionHash;
    100    String treeHash;
    101    String parentHash;
    102    String author;
    103    Date dateAuthor;
    104    String committer;
    105    Date dateCommit;
    106    String commitTitle;
    107    StringBuilder commitText = new StringBuilder(200);
    108
    109    RevisionEntry(String hash) { revisionHash = hash; }
    110
    111  }
    112
    113
    114
    115  static class Settings
    116  {
    117
    118    File dirTemp1 = new File("t:/git_tmp1");
    119    File dirTemp2 = new File("t:/git_tmp2");
    120  }
    121
    122
    123  /**Action for open the commit text.
    124   *
    125   */
    126  GralUserAction actionOpenCommitText = new GralUserAction("actionTableOpenCommitText")
    127  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    128      String[] args ={"cmd.exe", "/C", "edit", ".gitCommit"};
    129      wdgCmd.setText("cmd.exe /C edit .gitCommit");
    130      gitCmd.addCmd(args, null, listOut, null, workingDir, null);
    131      wdgCommit.setText("do commit");
    132      return true;
    133  } };
    134
    135
    136  /**This code snippet is executed after the 'git diff' command for 2 revisions are executed.
    137   * It is used as last argument of {@link CmdExecuter#execute(String[], boolean, String, List, List, org.vishia.cmd.CmdExecuter.ExecuteAfterFinish)}
    138   * and prepares the {@link #wdgTableFiles}.
    139   */
    140  CmdExecuter.ExecuteAfterFinish exec_CommitDone = new CmdExecuter.ExecuteAfterFinish()
    141  { @Override
    142    public void exec(int errorcode, Appendable out, Appendable err)
    143    { if(errorcode ==0) {
    144        FileSystem.writeFile("\n", sWorkingDir + "/.gitcommit");
    145        wdgCommit.setText("commit done");
    146      } else {
    147        wdgCommit.setText("commit error");
    148      }
    149    }
    150  };
    151
    152
    153
    154  /**Action for do commi.
    155   *
    156   */
    157  GralUserAction actionCommit = new GralUserAction("actionCommit")
    158  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    159      File gitCommit = new File(sWorkingDir, ".gitCommit");
    160      if(gitCommit.length() > 3) {
    161        try{ FileList.list(sWorkingDir, "*", GitGui.this.sFileList);
    162        } catch(Exception exc){
    163          wdgInfo.setText("_filelist.lst problem: " + exc.getMessage());
    164        }
    165        String sGitCmd = "git";
    166        if(! sGitDir.startsWith(sWorkingDir)) {
    167          sGitCmd += " '--git-dir=" + sGitDir + "'";
    168        }
    169        sGitCmd += " commit -a -F .gitcommit";
    170        wdgCmd.setText(sGitCmd);
    171        String[] args ={exepath.gitsh_exe, "-x", "-c", sGitCmd};
    172        gitCmd.addCmd(args, null, listOut, null, workingDir, exec_CommitDone);
    173      } else {
    174        wdgCommit.setText("do commit text?");
    175      }
    176      return true;
    177  } };
    178
    179
    180  /**Action for show the version table for the given file.
    181   *
    182   */
    183  GralUserAction actionRefresh = new GralUserAction("actionRefresh")
    184  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    185      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    186        startLog(null); return true;
    187      } //if;
    188      return false;
    189  } };
    190
    191
    192
    193
    194  GralUserAction actionTableLineVersion = new GralUserAction("actionTablelineVersion")
    195  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    196      if(actionCode == KeyCode.userSelect) {
    197        @SuppressWarnings("unchecked")
    198        GralTable<RevisionEntry>.TableLineData line = (GralTable<RevisionEntry>.TableLineData) params[0];
    199
    200        showLog_startRevisionDiff4FileTable(line);
    201      }
    202      return true;
    203    }
    204  };
    205
    206
    207  /**This code snippet is executed after the 'git diff' command for 2 revisions are executed.
    208   * It is used as last argument of {@link CmdExecuter#execute(String[], boolean, String, List, List, org.vishia.cmd.CmdExecuter.ExecuteAfterFinish)}
    209   * and prepares the {@link #wdgTableFiles}.
    210   */
    211  CmdExecuter.ExecuteAfterFinish exec_fillRevisionTable = new CmdExecuter.ExecuteAfterFinish()
    212  { @Override
    213    public void exec(int errorcode, Appendable out, Appendable err)
    214    { fillRevisionTable();
    215    }
    216  };
    217
    218
    219  /**This code snippet is executed after the 'git diff' command for 2 revisions are executed.
    220   * It is used as last argument of {@link CmdExecuter#execute(String[], boolean, String, List, List, org.vishia.cmd.CmdExecuter.ExecuteAfterFinish)}
    221   * and prepares the {@link #wdgTableFiles}.
    222   */
    223  CmdExecuter.ExecuteAfterFinish exec_fillFileTable4Revision = new CmdExecuter.ExecuteAfterFinish()
    224  { @Override
    225    public void exec(int errorcode, Appendable out, Appendable err)
    226    { fillFileTable4Revision();
    227    }
    228  };
    229
    230
    231  /**This code snippet is executed after the 'git diff' command for 2 revisions are executed.
    232   * It is used as last argument of {@link CmdExecuter#execute(String[], boolean, String, List, List, org.vishia.cmd.CmdExecuter.ExecuteAfterFinish)}
    233   * and prepares the {@link #wdgTableFiles}.
    234   */
    235  CmdExecuter.ExecuteAfterFinish exec_ShowStatus = new CmdExecuter.ExecuteAfterFinish()
    236  { @Override
    237    public void exec(int errorcode, Appendable out, Appendable err)
    238    { wdgInfo.setText(gitOut);  //The status info
    239      gitOut.buffer().setLength(0);  //prepare for the next command.
    240      gitOut.assign(gitOut.buffer());   //to reset positions to the changed gitOut.buffer()
    241    }
    242  };
    243
    244
    245  /**Action for mouse double to start view diff.
    246   *
    247   */
    248  GralUserAction actionTableFile = new GralUserAction("actionTableFile")
    249  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    250      @SuppressWarnings("unchecked")
    251      GralTable<RevisionEntry>.TableLineData line = (GralTable<RevisionEntry>.TableLineData)params[0];  //it is the table line.
    252
    253      String sFile = line.getKey(); //"org/vishia/inspcPC/InspCmd.java";
    254      switch(actionCode) {
    255      case (KeyCode.ctrl | KeyCode.enter):
    256      case KeyCode.mouse1Double: startDiffView(sFile, GitGui.this.currentEntry, GitGui.this.cmpEntry); return true;
    257      case (KeyCode.ctrl | 's'): startLog(sFile); return true;
    258      default: return false;
    259      } //switch;
    260
    261  } };
    262
    263
    264  /**Action for mouse double to start view diff.
    265   *
    266   */
    267  GralUserAction actionRestore = new GralUserAction("actionRestore")
    268  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    269      @SuppressWarnings("unchecked")
    270      GralTable<RevisionEntry> table = (GralTable<RevisionEntry>)widgd;  //it is the table line.
    271      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    272        GralTable<RevisionEntry>.TableLineData line = table.getLineMousePressed(); //(GralTable<RevisionEntry>.TableLineData)params[0];  //it is the table line.
    273        String sFile = line.getKey();
    274        restoreFile(sFile);
    275        return true;
    276      } //if;
    277      return false;
    278  } };
    279
    280
    281  /**Action for mouse double to start view diff.
    282   *
    283   */
    284  GralUserAction actionTableFileDiffView = new GralUserAction("actionTableFileDiffView")
    285  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    286      @SuppressWarnings("unchecked")
    287      GralTable<RevisionEntry> table = (GralTable<RevisionEntry>)widgd;  //it is the table line.
    288
    289      GralTable<RevisionEntry>.TableLineData lineCurr = table.getCurrentLine(); //(GralTable<RevisionEntry>.TableLineData)params[0];  //it is the table line.
    290      GralTable<RevisionEntry>.TableLineData line = table.getLineMousePressed(); //(GralTable<RevisionEntry>.TableLineData)params[0];  //it is the table line.
    291      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    292        String sFile = line.getKey();
    293        startDiffView(sFile, GitGui.this.currentEntry, GitGui.this.cmpEntry); return true;
    294      } //if;
    295      return false;
    296  } };
    297
    298
    299  GralUserAction actionTableFileRenMove = new GralUserAction("actionTableFileRenMove")
    300  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    301      @SuppressWarnings("unchecked")
    302      GralTable<RevisionEntry> table = (GralTable<RevisionEntry>)widgd;  //it is the table line.
    303
    304      GralTable<RevisionEntry>.TableLineData lineCurr = table.getCurrentLine(); //(GralTable<RevisionEntry>.TableLineData)params[0];  //it is the table line.
    305      GralTable<RevisionEntry>.TableLineData line = table.getLineMousePressed(); //(GralTable<RevisionEntry>.TableLineData)params[0];  //it is the table line.
    306      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    307        String sFile = line.getKey();
    308        openNewFileSelector(sFile, GitGui.this.currentEntry); return true;
    309      } //if;
    310      return false;
    311  } };
    312
    313
    314  /**Action for diff view of the current file to the workspace.*/
    315  GralUserAction actionDiffCurrWork = new GralUserAction("actionCurrFileDiffView")
    316  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    317      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    318        GralTable<RevisionEntry>.TableLineData line = wdgTableVersion.getCurrentLine();
    319        RevisionEntry revision = line.getData();
    320        startDiffView(sLocalFile, null, revision); return true;
    321      } //if;
    322      return false;
    323  } };
    324
    325
    326  /**Action for diff view of the current file between revisions.*/
    327  GralUserAction actionFileDiffRev = new GralUserAction("actionFileDiffRev")
    328  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    329      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    330        GralTable<RevisionEntry>.TableLineData line = wdgTableVersion.getCurrentLine();
    331        RevisionEntry revision = line.getData();
    332        startDiffView(sLocalFile, null, revision); return true;
    333      } //if;
    334      return false;
    335  } };
    336
    337
    338  /**Action for show the version table for the given file.
    339   *
    340   */
    341  GralUserAction actionTableFileLog = new GralUserAction("actionTableFileLog")
    342  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    343      @SuppressWarnings("unchecked")
    344      GralTable<RevisionEntry> table = (GralTable<RevisionEntry>)widgd;  //it is the table line.
    345      GralTable<RevisionEntry>.TableLineData lineCurr = table.getCurrentLine(); //(GralTable<RevisionEntry>.TableLineData)params[0];  //it is the table line.
    346      GralTable<RevisionEntry>.TableLineData line = table.getLineMousePressed(); //(GralTable<RevisionEntry>.TableLineData)params[0];  //it is the table line.
    347      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    348        String sFile = line.getKey();   //working tree has key "*"
    349        startLog(sFile); return true;
    350      } //if;
    351      return false;
    352  } };
    353
    354
    355  GralUserAction actionExecCmd = new GralUserAction("actionExecCmd")
    356  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    357      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    358        String cmd = wdgCmd.getText();
    359        File cmdDir = workingDir;
    360        int posDir = cmd.indexOf('>');
    361        if(posDir >0) {
    362          cmdDir = new File(cmd.substring(0, posDir));
    363          cmd = cmd.substring(posDir+1);
    364        }
    365        String[] args = cmd.split(" ");
    366        if(args[0].equals("git")) {
    367          String[] args2 = args;
    368          args = new String[4];
    369          args[0] = exepath.gitsh_exe;
    370          args[1] = "-x";
    371          args[2] = "-c";
    372          args[3] = cmd;
    373        }
    374        gitOut.clear();
    375        gitCmd.addCmd(args, null, listOut, null, cmdDir, execShowListOut);
    376
    377      } //if;
    378      return false;
    379  } };
    380
    381  /**Action for show the version table for the given file.
    382   *
    383   */
    384  GralUserAction actionAdd = new GralUserAction("actionAdd")
    385  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    386      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    387        addFileFromSelection(); return true;
    388      } //if;
    389      return false;
    390  } };
    391
    392
    393
    394  /**Action for show the version table for the given file.
    395   *
    396   */
    397  GralUserAction actionMove = new GralUserAction("actionMove")
    398  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    399      if(KeyCode.isControlFunctionMouseUpOrMenu(actionCode)){
    400        moveFileListToSelection(); return true;
    401      } //if;
    402      return false;
    403  } };
    404
    405
    406
    407
    408  CmdExecuter.ExecuteAfterFinish execShowListOut = new CmdExecuter.ExecuteAfterFinish()
    409  { @Override
    410    public void exec(int errorcode, Appendable out, Appendable err)
    411    { //if(errorcode ==0) {
    412      wdgInfo.setText("cmd ouptut:\n");
    413      try { wdgInfo.append(gitOut);
    414      } catch (IOException e) { }
    415  } };
    416
    417
    418  /**The paths to the executables. */
    419  GitGuiPaths exepath;
    420
    421  /**The only one opened git repository. null if nothing is open or more as one is open.
    422   *
    423   */
    424  static File filePathRepository;
    425
    426  Settings settings = new Settings();
    427
    428  String sTypeOfImplementation = "SWT";  //default
    429
    430
    431  GralWindow window = new GralWindow("0+60, 0+90", "GitGui", "Git vishia", GralWindow_ifc.windResizeable | GralWindow_ifc.windRemoveOnClose);
    432
    433  GralTextField wdgCmd = new GralTextField("@2-2,0..-7=cmd", GralTextField.Type.editable);
    434
    435  GralButton wdgBtnCmd = new GralButton("@0..2, -6..0 = cmdExec", "exec", this.actionExecCmd);
    436
    437
    438  GralTable<RevisionEntry> wdgTableVersion = new GralTable<>("@3..-30,0..-40=git-versions", new int[] {2, 10, 0, -10});
    439
    440  GralTable<String> wdgTableFiles = new GralTable<>("@3..-30,-40..0=git-files", new int[] {3,20,0});
    441
    442  GralTextBox wdgInfo = new GralTextBox("@-30..0, 0..-20=info");
    443
    444  GralButton wdgBtnDiffCurrWork = new GralButton("@-29..-27, -18..-2 = diffCurrWork", "diff current file to workspace", this.actionDiffCurrWork);
    445
    446  GralButton wdgBtnDiffCurrFile = new GralButton("@-26..-24, -18..-2 = diffCurrFile", "diff current file", this.actionFileDiffRev);
    447
    448  GralButton wdgBtnAdd = new GralButton("@-18+2, -18..-8 = add", "add", this.actionAdd);
    449
    450  GralButton wdgBtnMove = new GralButton("@-15+2, -18..-8 = move", "mv", this.actionMove);
    451
    452  GralButton wdgRefresh = new GralButton("@-12+2, -18..-8 = refresh", "refresh", this.actionRefresh);
    453
    454  GralButton wdgCommitText = new GralButton("@-9+2, -18..-2 = commitText", "commit-text", this.actionOpenCommitText);
    455
    456  GralButton wdgCommit = new GralButton("@-6+2, -18..-2 = commit", "do commit", this.actionCommit);
    457
    458  /**If set to true, the {@link #cmdThread} should be aborted.
    459   *
    460   */
    461  boolean bCmdThreadClose;
    462
    463  CmdExecuter gitCmd = new CmdExecuter();
    464
    465
    466  /**Destination for output of all command line invocations.
    467   * This buffer will be cleared and filled with the git command, and then parsed to present the result.
    468   */
    469  StringPartAppend gitOut = new StringPartAppend();
    470
    471  /**The {@link CmdExecuter#execute(String[], boolean, String, List, List, org.vishia.cmd.CmdExecuter.ExecuteAfterFinish)}
    472   * needs a list of appendable, that is it.*/
    473  List<Appendable> listOut = new LinkedList<Appendable>();
    474  { listOut.add(gitOut); }
    475
    476  /**Stored arguments from {@link #startLog(String, String, String)}. */
    477  String sGitDir, sWorkingDir; //, sLocalFile;
    478
    479  File workingDir;
    480
    481  String sFileList = "_filelist.lst";
    482
    483  /**If given the file which's log and diff should be shown.
    484   * Elsewhere null. set in {@link #startLog(String)}
    485   */
    486  String sLocalFile;
    487
    488
    489  /**The presentation of the time stamps. */
    490  SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm");
    491
    492
    493
    494  /**The current line and the line before, to the earlier commit. The Predecessor is not the parent in any case, not on branch and merge points. */
    495  //GralTable<RevisionEntry>.TableLineData currentLine, cmpLine;
    496  GralTableLine_ifc<RevisionEntry> currentLine, cmpLine;
    497
    498  /**The current entry and the entry before, to the earlier commit. The Predecessor is not the parent in any case, not on branch and merge points. */
    499  RevisionEntry currentEntry, cmpEntry;
    500
    501
    502  public static void main(String[] args){
    503    GitGui main = new GitGui(args);
    504    //only test
    505    main.startLog("D:/GitArchive/D/vishia/srcJava_vishiaBase/.git", "D:/GitArchive/D/vishia/srcJava_vishiaBase", "org/vishia/util/CalculatorExpr.java");
    506    main.doSomethinginMainthreadTillCloseWindow();
    507  }
    508
    509
    510  public GitGui(String[] args) {
    511    if(args !=null && args.length >=1){
    512      sTypeOfImplementation = args[0];
    513    }
    514    initializeCmd();
    515    if(!settings.dirTemp1.exists()) { settings.dirTemp1.mkdirs(); }
    516    if(!settings.dirTemp2.exists()) { settings.dirTemp2.mkdirs(); }
    517    wdgTableVersion.specifyActionOnLineSelected(actionTableLineVersion);
    518    wdgTableFiles.specifyActionChange("actionTableFile", actionTableFile, null);
    519    window.specifyActionOnCloseWindow(actionOnCloseWindow);
    520    window.create(sTypeOfImplementation, 'B', null, initGraphic);
    521    cmdThread.start();
    522  }
    523
    524
    525  GralGraphicTimeOrder initGraphic = new GralGraphicTimeOrder("init")
    526  { @Override protected void executeOrder() {
    527      wdgTableFiles.addContextMenuEntryGthread(1, "diffView", "Diff view [Mouse double], [ctrl-Enter]", actionTableFileDiffView);
    528      wdgTableFiles.addContextMenuEntryGthread(1, "rename/move", "rename/move", actionTableFileRenMove);
    529      wdgTableFiles.addContextMenuEntryGthread(1, "restore", "Restore this file", actionRestore);
    530      wdgTableFiles.addContextMenuEntryGthread(1, "show Log for File", "Show log for this file [ctrl-s]", actionTableFileLog);
    531    }
    532  };
    533
    534
    535
    536
    537  GralUserAction actionOnCloseWindow = new GralUserAction("")
    538  { @Override public boolean exec(int actionCode, org.vishia.gral.ifc.GralWidget_ifc widgd, Object... params) {
    539      GitGui.this.gitCmd.close();
    540      bCmdThreadClose = true;
    541      return true;
    542    }
    543  };
    544
    545
    546  @Override public void finalize()
    547  {
    548  }
    549
    550  void initializeCmd() {
    551    gitCmd.setCharsetForOutput("UTF-8");  //git outputs in UTF-8
    552    Map<String, String> env = gitCmd.environment();
    553    env.put("HOMEPATH", "\\vishia\\HOME");
    554    env.put("HOMEDRIVE", "D:");
    555    String sPath = env.get("PATH");
    556    if(sPath ==null) { sPath = env.get("Path"); }
    557    if(sPath ==null) { sPath = env.get("path"); }
    558    sPath = "D:\\Programs\\Gitcmd\\bin;" + sPath;
    559    env.put("PATH", sPath);
    560  }
    561
    562
    563  public void doSomethinginMainthreadTillCloseWindow()
    564  { int ix = 0;
    565    while(! window.isGraphicDisposed()){
    566      try{ Thread.sleep(1000);}
    567      catch (InterruptedException e) { }
    568    }
    569
    570  }
    571
    572
    573
    574
    575
    576  /**Search the base dir and the repository for a given path/to/file.
    577   * @param startFile Any file path. Especially a ".git" directory or ".gitRepository*" file. In that  case the local file path will be set to null,
    578   *   because the whole repository is given.
    579   *   A ".gitRepository*" file contains the path to the extern .git directory.
    580   * @param dst maybe null, elsewhere new String[2].
    581   *   dst[0] will be filled with the absolute path of the .git directory.
    582   *   dst[1] will be filled with the absolute path to the basic directory for the working tree where .git or .gitRepository were found.
    583   *   dst[2] will be filled with the local file path or null if the .git or .gitRepositiory is the startFile.
    584   * @param dstMap maybe null, if not null it is a container maybe from JZcmd variables or an empty Map.
    585   * @param nameRepository if dstMap !=null, the name of the key (variable) for the repository path.
    586   * @param nameBasedir if dstMap !=null, the name of the key (variable) for the base directory.
    587   * @param nameLocalFile  if dstMap !=null, the name of the key (variable) for the local file.
    588   *   That variables will be set. nameLocalFile will be set to null if .git or .gitRepository is the startFile.
    589   * @return null on success or an error message
    590   */
    591  public static String searchRepository(File startFile, String[] dst, Map<String, DataAccess.Variable<Object>> dstMap
    592  , String nameRepository, String nameBasedir, String nameLocalFile)
    593  { File fRepo = null;
    594    File currDir;
    595    String ret = null;
    596    if(startFile.isDirectory()) {
    597      currDir = startFile;
    598      fRepo = new File(startFile, ".git"); //only to check whether it exists
    599      if(!fRepo.exists()){
    600        fRepo = new File(startFile, ".gitRepository");
    601        if(!fRepo.exists()) {  //if startFile as currdir contains .git or .gitRepository, it's okay
    602          currDir = null;  //not okay, startFile is any directory maybe inside a working tree
    603          fRepo = null;
    604        }
    605      }
    606    } else { //startFile is a file.
    607      String fName = startFile.getName();
    608      if(fName.equals(".git") || fName.endsWith(".gitRepository")) {
    609        fRepo = startFile;
    610        currDir = startFile.getParentFile();
    611      } else {
    612        currDir = null; //startFile is not a directory, search in parents.
    613      }
    614    }
    615    if(currDir == null) { //startFile is not a directory, or it is a directory which does not contain .git or .gitRepository
    616      currDir = startFile.getParentFile();
    617      //search whether a .git or .gitRepository exists and change to parent dir till it is found.
    618      do{
    619        fRepo = new File(currDir, ".git"); //only to check whether it exists
    620        if(!fRepo.exists()){
    621          fRepo = new File(currDir, ".gitRepository");
    622          if(!fRepo.exists()){
    623            fRepo = null;
    624            try{
    625              currDir = FileSystem.getDirectory(currDir);  //NOTE: currDir.getParent() is not successfully on relative dir "."
    626            } catch(FileNotFoundException exc){ currDir = null;}
    627          }
    628        }
    629      } while(fRepo ==null && currDir !=null);
    630    }
    631    //
    632    //currdir and fRepo should be set, or not:
    633    if(fRepo == null){
    634      ret = "searchRepository - .git or .gitRepository not found ;" + startFile.getAbsolutePath();
    635    } else {
    636      assert(currDir !=null); //set together with fRepo
    637      String sBaseDir = FileSystem.normalizePath(currDir).toString();
    638      //dst.put(bzrdir, sBzrDir);
    639      String sRepository;
    640      if(fRepo.getName().endsWith(".gitRepository")){   //File with link to repository
    641        filePathRepository = fRepo;
    642        sRepository = FileSystem.readFile(fRepo).trim();
    643      } else {
    644        sRepository = sBaseDir;
    645      }
    646      CharSequence sFilePath = FileSystem.normalizePath(startFile);
    647      String sLocalFilePath;
    648      if(currDir == startFile) {
    649        sLocalFilePath = null;
    650      } else {
    651        sLocalFilePath = sFilePath.subSequence(sBaseDir.length()+1, sFilePath.length()).toString();
    652        if(sLocalFilePath.length() == 0 || sLocalFilePath.equals(".git") || sLocalFilePath.endsWith(".gitRepository")){
    653          sLocalFilePath = null;  //no local file.
    654        }
    655      }
    656      if(dst !=null) {
    657        dst[0] = sRepository;
    658        dst[1] = sBaseDir;
    659        dst[2] = sLocalFilePath;
    660      }
    661      if(dstMap !=null) {
    662        try {
    663        DataAccess.createOrReplaceVariable(dstMap, nameBasedir, 'S', sBaseDir, true);
    664        DataAccess.createOrReplaceVariable(dstMap, nameRepository, 'S', sRepository, true);
    665        DataAccess.createOrReplaceVariable(dstMap, nameLocalFile, 'S', sLocalFilePath, true);
    666        } catch(Exception exc) {
    667          ret = "searchRepository - repository found, but write problems for dst Map,";
    668        }
    669      }
    670    }
    671    return ret;
    672  }
    673
    674
    675
    676  /**Returns the opened repository or repoository linking file or searches the next .git or .gitRepository file
    677   * in the parent dir. It is to return the correct repository linking file for the opened GUI
    678   * to add something with Fcmd and jzTc
    679   * @param dir The dir where a file should be handled.
    680   * @return The opened filePathRepository only if the dir is in the same file tree.
    681   *   Elsewhere it searches .git or .gitRepository in this dir and parents.
    682   */
    683  public static File getFilePathRepository(File startFileSearchRepository) {
    684    if(filePathRepository !=null) {
    685      String sFilePathRepository = FileSystem.getCanonicalPath(filePathRepository.getParentFile());
    686      String sstartFileSearchRepository = FileSystem.getCanonicalPath(startFileSearchRepository);
    687      if(sstartFileSearchRepository.startsWith(sFilePathRepository)) {
    688        return filePathRepository;  //same or sub dir as filePathRepository: Proper.
    689      }
    690    }
    691    //else:
    692    return FileSystem.searchInParent(startFileSearchRepository, ".gitRepository", ".git");  //search either .gitRepository or .git
    693  }
    694
    695
    696
    697
    698  /**Searches the git repository and the root of the working tree and opens the window for the git gui.
    699   * This routine can be invoked from any Java program without additional conditions.
    700   * @param srcFile Any file to start searching the root of the working tree and the git repository inside this tree.
    701   *   If it is a directory and that directory contains either ".git" or ".gitRepository", it is the root of the working tree.
    702   *   Elsewhere the root will be searched backward to the root of the file system.
    703   *   It means you can start this routine with any srcFile inside the working tree.
    704   *   This file will be used as additional argument for example to show the history of that file.
    705   */
    706  public static void guiRepository(GitGuiPaths exepath, JZtxtcmdFilepath repoFile)
    707  {
    708    File srcFile = null;
    709    try {
    710      //copy to the working version of .gitignore
    711      File fIgnore = new File(repoFile.absname() + ".gitignore");
    712      if(fIgnore.exists()) {
    713        File fIgnore2 = new File(repoFile.absdir() + "/.gitignore");
    714        FileSystem.copyFile(fIgnore, fIgnore2);
    715      }
    716      srcFile = new File(repoFile.absfile().toString());
    717    } catch (NoSuchFieldException | IOException e) { System.err.println(e.getMessage());}
    718    String[] paths = new String[3];
    719    String error = searchRepository(srcFile, paths, null, null, null, null);
    720    if(error !=null) { System.err.println(error); }
    721    else {
    722      GitGui main = new GitGui(null);
    723      main.exepath = exepath;
    724      main.sFileList = repoFile.name() + ".filelist";
    725      main.startLog(paths[0], paths[1], paths[2]);
    726    }
    727  }
    728
    729
    730  /**Searches the git repository and the root of the working tree and opens the window for the git gui.
    731   * This routine can be invoked from any Java program without additional conditions.
    732   * @param srcFile Any file to start searching the root of the working tree and the git repository inside this tree.
    733   *   If it is a directory and that directory contains either ".git" or ".gitRepository", it is the root of the working tree.
    734   *   Elsewhere the root will be searched backward to the root of the file system.
    735   *   It means you can start this routine with any srcFile inside the working tree.
    736   *   This file will be used as additional argument for example to show the history of that file.
    737   */
    738  public static void showLog(File srcFile)
    739  {
    740    String[] paths = new String[3];
    741    String error = searchRepository(srcFile, paths, null, null, null, null);
    742    if(error !=null) { System.err.println(error); }
    743    else {
    744      GitGui main = new GitGui(null);
    745      main.startLog(paths[0], paths[1], paths[2]);
    746    }
    747  }
    748
    749
    750
    751
    752  public void startLog(String sGitDir, String sWorkingDir, String sLocalFile) {
    753    this.sGitDir = sGitDir;
    754    this.sWorkingDir = sWorkingDir;
    755    this.workingDir = new File(sWorkingDir);
    756    startLog(sLocalFile);
    757  }
    758
    759  /**
    760   * @param sLocalFile "*" for all files, else "path/in/loacal/tree/file.ext"
    761   */
    762  void startLog(String sLocalFile) {
    763    //this.sLocalFile = sLocalFile;
    764    String sPathShow;
    765    this.sLocalFile = sLocalFile;
    766    if(sLocalFile !=null) {
    767      sPathShow = sGitDir + " : " + sLocalFile;
    768      wdgBtnDiffCurrFile.setVisible(true);
    769      wdgBtnDiffCurrWork.setVisible(true);
    770    } else {
    771      sPathShow = sGitDir;
    772      wdgBtnDiffCurrFile.setVisible(false);
    773      wdgBtnDiffCurrWork.setVisible(false);
    774    }
    775    window.setTitle("Git " + sPathShow);
    776
    777    wdgTableVersion.clearTable();
    778    wdgTableVersion.addLine("*", new String[] {"", "", "wait for prepairing log", ""}, null);
    779    gitOut.buffer().setLength(0);
    780    gitOut.assign(gitOut.buffer());   //to reset positions to the changed gitOut.buffer()
    781    String sGitCmd = "git";
    782    if(! sGitDir.startsWith(sWorkingDir)) {
    783      sGitCmd += " '--git-dir=" + sGitDir + "'";
    784    }
    785    sGitCmd += " log --date=iso '--pretty=raw'";
    786    if(sLocalFile !=null && sLocalFile.length() >0) {
    787      sGitCmd +=  " -- '" + sLocalFile + "'";
    788    }
    789    wdgCmd.setText(sGitCmd);
    790    String[] args ={exepath.gitsh_exe, "-x", "-c", sGitCmd};
    791    gitCmd.clearCmdQueue();
    792    gitCmd.abortCmd();
    793    gitOut.buffer().setLength(0);
    794    gitOut.assign(gitOut.buffer());   //to reset positions to the changed gitOut.buffer()
    795    gitCmd.addCmd(args, null, listOut, null, workingDir, exec_fillRevisionTable);
    796    synchronized(cmdThread) { cmdThread.notify(); }
    797    //int error = cmd.execute(args, null,  gitOut, null);
    798    //fillRevisionTable();
    799  }
    800
    801
    802
    803
    804  /**Fills the {@link #wdgTableVersion} with the gotten output after git log command. This routine is invoked as {@link #execAfterCmdLog}.
    805   *
    806   */
    807  void fillRevisionTable() {
    808    gitOut.firstlineMaxpart();
    809    String lineTexts[] = new String[4];
    810    boolean contCommits = true;
    811    wdgTableVersion.clearTable();
    812    lineTexts[0] = "";
    813    lineTexts[1] = "*";
    814    lineTexts[2] = "--working area --";
    815    lineTexts[3] = "";
    816    wdgTableVersion.addLine("*", lineTexts, null);
    817    RevisionEntry entryLast = null;
    818    do {
    819      if(gitOut.scanStart().scan("commit ").scanOk()) {
    820        contCommits = false;  //set to true on the next "commit " line.
    821        String hash = gitOut.getCurrentPart().toString();
    822        RevisionEntry entry = new RevisionEntry(hash);
    823        try {
    824          boolean cont = true;
    825          while (cont && gitOut.nextlineMaxpart().found()) {
    826            if(gitOut.scanStart().scan("tree ").scanOk()) {
    827              entry.treeHash = gitOut.getCurrentPart().toString();
    828            }
    829            else if(gitOut.scanStart().scan("parent ").scanOk()) {
    830              entry.parentHash = gitOut.getCurrentPart().toString();
    831            }
    832            else if(gitOut.scanStart().scan("author ").scanOk()) {
    833              gitOut.lento('<');
    834              entry.author = gitOut.getCurrentPart().toString();
    835              gitOut.fromEnd().seekPosBack(16).lento(' ');
    836              if(gitOut.scan().scanInteger().scanOk()) {
    837                entry.dateAuthor = new Date(1000*gitOut.scan().getLastScannedIntegerNumber());
    838              }
    839            } else if(gitOut.scanStart().scan("committer ").scanOk()) {
    840              gitOut.lento('<');
    841              entry.committer = gitOut.getCurrentPart().toString();
    842              gitOut.fromEnd().seekPosBack(16).lento(' ');
    843              if(gitOut.scan().scanInteger().scanOk()) {
    844                entry.dateCommit = new Date(1000*gitOut.scan().getLastScannedIntegerNumber());
    845              }
    846            } else {
    847              do {
    848                if(gitOut.scanStart().scan("commit ").scanOk()) {
    849                  gitOut.seekBegin();
    850                  cont = false;
    851                  contCommits = true;
    852                  break;
    853                } else {
    854                  CharSequence commitTextline = gitOut.getCurrentPart();
    855                  if(entry.commitTitle == null && commitTextline.length() >6){
    856                    entry.commitTitle = commitTextline.toString();  //first line with at least 6 character: Use as title.
    857                  }
    858                  entry.commitText.append(commitTextline).append('\n');
    859                }
    860              } while(cont && (gitOut.nextlineMaxpart().found()));
    861            }
    862          }//while lines of one commit
    863        }catch(Exception exc) {
    864          Debugutil.stop();
    865        }
    866        if(entry.dateAuthor !=null) {
    867          String cL = "A";
    868          if(entryLast !=null && entryLast.parentHash !=null && entryLast.parentHash.equals(entry.revisionHash)){ cL = "B"; }
    869          lineTexts[0] = cL;
    870          lineTexts[1] = dateFormat.format(entry.dateAuthor);
    871          lineTexts[2] = entry.commitTitle;
    872          lineTexts[3] = entry.author;
    873          GralTableLine_ifc<RevisionEntry> line = wdgTableVersion.addLine(hash, lineTexts, entry);
    874          line.repaint();
    875        }
    876        entryLast = entry;
    877      } //
    878      else {
    879        contCommits = gitOut.nextlineMaxpart().found();
    880      }
    881    } while(contCommits);
    882    //wdgTableVersion.
    883    wdgTableVersion.repaint();
    884  }
    885
    886
    887  /**Starts the command 'git diff' to fill the {@link #wdgTableFiles} for the selected revision.
    888   *
    889   * @param line
    890   */
    891  void showLog_startRevisionDiff4FileTable(GralTable<RevisionEntry>.TableLineData line) {
    892    if(line == null){
    893       return;
    894    }
    895    //
    896    //abort the current cmd for diff view (or any other)
    897    gitCmd.clearCmdQueue();
    898    gitCmd.abortCmd();
    899    gitOut.buffer().setLength(0);
    900    gitOut.assign(gitOut.buffer());   //to reset positions to the changed gitOut.buffer()
    901    //
    902    //
    903    GitGui.this.currentLine = line;
    904    GralTable<RevisionEntry> table = line.getTable();
    905    List<GralTableLine_ifc<RevisionEntry>> markedLines = table.getMarkedLines(1);
    906    GitGui.this.cmpLine = line.nextSibling();
    907    if(markedLines !=null && markedLines.size() >0) {
    908      if(markedLines.size() ==1) {
    909        cmpLine = markedLines.get(0);
    910      }
    911      else {
    912        System.err.println("more as one line marked");
    913      }
    914    }
    915    GitGui.this.currentEntry = line.data;
    916    GitGui.this.cmpEntry = cmpLine == null ? null : cmpLine.getUserData();
    917    String sGitCmd = "git";
    918    if(! sGitDir.startsWith(sWorkingDir)) {
    919      sGitCmd += " '--git-dir=" + sGitDir + "'";
    920    }
    921    if(currentEntry ==null) {
    922      String[] args ={exepath.gitsh_exe, "-x", "-c", sGitCmd + " status"};
    923      gitCmd.addCmd(args, null, listOut, null, workingDir, exec_ShowStatus);
    924      wdgInfo.setText("(working area)");
    925      try{
    926        wdgInfo.append("\n");
    927      } catch(IOException exc){}
    928      sGitCmd += " diff --name-status HEAD";
    929    } else {
    930      wdgInfo.setText(currentEntry.revisionHash);
    931      try{
    932        wdgInfo.append("=>").append(currentEntry.parentHash).append(" @").append(currentEntry.treeHash).append("\n");
    933        if(cmpEntry !=null) {
    934          wdgInfo.append(cmpEntry.revisionHash).append("=>").append(cmpEntry.parentHash).append(" @").append(cmpEntry.treeHash).append("\n");
    935        }
    936        wdgInfo.append(currentEntry.commitText);
    937      } catch(IOException exc){}
    938      sGitCmd += " diff --name-status " + currentEntry.parentHash + ".." + currentEntry.revisionHash;
    939    }
    940    wdgCmd.setText(sGitCmd);
    941    String[] args ={exepath.gitsh_exe, "-x", "-c", sGitCmd};
    942    //
    943    gitCmd.addCmd(args, null, listOut, null, workingDir, exec_fillFileTable4Revision);
    944    synchronized(cmdThread) { cmdThread.notify(); }
    945  }
    946
    947
    948
    949  void restoreFile(String sFile) {
    950    String sGitCmd2 = "git '--git-dir=" + sGitDir + "' checkout " + cmpEntry.revisionHash + " -- " + this.sFileList; ///
    951    wdgCmd.setText(sGitCmd2);
    952    String[] args2 ={exepath.gitsh_exe, "-x", "-c", sGitCmd2};
    953    gitCmd.addCmd(args2, null, listOut, null, workingDir, null);
    954
    955    String sGitCmd = "git '--git-dir=" + sGitDir + "' checkout " + cmpEntry.revisionHash + " -- " + sFile;
    956    String[] args ={exepath.gitsh_exe, "-x", "-c", sGitCmd};
    957    gitCmd.addCmd(args, null, listOut, null, workingDir,
    958      new CmdExecuter.ExecuteAfterFinish()
    959        { @Override
    960          public void exec(int errorcode, Appendable out, Appendable err)
    961          { try {FileList.touch(sWorkingDir, GitGui.this.sFileList, sFile, null);
    962            } catch (IOException exc) {
    963              System.err.println("IOexception for File.touch()");
    964            }
    965          }
    966        }
    967     );
    968
    969
    970
    971    //
    972
    973    //
    974  }
    975
    976
    977
    978  void addFileFromSelection() {
    979    int pos = wdgInfo.getCursorPos();
    980    String text = wdgInfo.getText();
    981
    982    String sGitCmd = "git";
    983    if(! sGitDir.startsWith(sWorkingDir)) {
    984      sGitCmd += " '--git-dir=" + sGitDir + "'";
    985    }
    986    sGitCmd += " add ";
    987    wdgCmd.setText(sGitCmd);
    988
    989  }
    990
    991
    992  void moveFileListToSelection() {
    993    int pos = wdgInfo.getCursorPos();
    994    String text = wdgInfo.getText();
    995
    996
    997
    998
    999    GralTable<String>.TableLineData line = wdgTableFiles.getCurrentLine();
    1000    int posSep = sGitDir.lastIndexOf('/');
    1001    assert(sGitDir.substring(posSep).equals("/.git"));
    1002    String sGitCmd = sGitDir.substring(0, posSep) + ">git mv ";
    1003    sGitCmd += line.getCellText(2) + "/" + line.getCellText(1) + " ";
    1004
    1005    wdgCmd.setText(sGitCmd);
    1006
    1007
    1008
    1009
    1010  }
    1011
    1012
    1013  /**Gets the file from repository and writes to tmp directory, starts diff tool
    1014   * @param sFile The local path of the file
    1015   * @param currRev maybe null, then compare with working tree, elsewhere a selected revision
    1016   * @param cmpRev the selected revision to compare.
    1017   */
    1018  void startDiffView(String sFile,  RevisionEntry currRev, RevisionEntry cmpRev) {
    1019    String sFile1 = settings.dirTemp1.getAbsolutePath() + "/" + sFile;
    1020    String sFile2 = settings.dirTemp2.getAbsolutePath() + "/" + sFile;
    1021    if(currRev == null) {
    1022      sFile1 = sWorkingDir + "/" + sFile;
    1023    } else  {
    1024      String sGitCmd = "git '--git-dir=" + sGitDir + "' checkout " + currRev.revisionHash + " -- " + sFile;
    1025      String[] args ={exepath.gitsh_exe, "-x", "-c", sGitCmd};
    1026      gitCmd.addCmd(args, null, listOut, null, settings.dirTemp1, null);
    1027    }
    1028    if(cmpRev == null) {
    1029      sFile2 = sFile1;
    1030    }
    1031    else {
    1032      String sGitCmd = "git '--git-dir=" + sGitDir + "' checkout " + cmpRev.revisionHash + " -- " + sFile;
    1033      wdgCmd.setText(settings.dirTemp2 + ">" + sGitCmd);
    1034      String[] args ={exepath.gitsh_exe, "-x", "-c", sGitCmd};
    1035      gitCmd.addCmd(args, null, listOut, null, settings.dirTemp2, null);
    1036    }
    1037    sFile1 = sFile1.replace('/', '\\');
    1038    sFile2 = sFile2.replace('/', '\\');
    1039    String sCmdDiff = "cmd.exe /C start " + exepath.diff_exe + " " + sFile1 + " " + sFile2;
    1040    String[] cmdDiffView = CmdExecuter.splitArgs(sCmdDiff);
    1041    gitCmd.addCmd(cmdDiffView, null, listOut, null, null, null);
    1042    synchronized(cmdThread) { cmdThread.notify(); }
    1043  }
    1044
    1045
    1046
    1047
    1048  void openNewFileSelector(String sFile,  RevisionEntry currRev) {
    1049    //wdgViewFiles.openDialog(FileRemote.fromFile(workingDir), "files", false, null);
    1050  }
    1051
    1052
    1053
    1054
    1055  /**Fills the file table with the gotten output after git diff name-status command. This routine is invoked as {@link #exec_fillFileTable4Revision}.
    1056   *
    1057   */
    1058  void fillFileTable4Revision() {
    1059    wdgTableFiles.clearTable();
    1060    GralTableLine_ifc<String> line = wdgTableFiles.addLine("*", new String[] {"!","(all files)",""}, "*");
    1061    wdgTableFiles.repaint();
    1062    gitOut.firstlineMaxpart();
    1063    do {
    1064      String sLine = gitOut.getCurrentPart().toString();
    1065      //the line starts with the status character, then '\t', then the file path.
    1066      if(!sLine.startsWith("+ git") && sLine.length() >2) {
    1067        String[] col = new String[3];
    1068        int posSlash = sLine.lastIndexOf('/');
    1069        col[0] = sLine.substring(0,1);
    1070        if(posSlash >0) {
    1071          col[1] = sLine.substring(posSlash+1);
    1072          col[2] = sLine.substring(2, posSlash);
    1073        } else {
    1074          col[1] = sLine.substring(2);
    1075          col[2] = "";
    1076        }
    1077        String key = sLine.substring(2); //without "M\t" (sign of change from <code>diff --name-status</code> output )
    1078        line = wdgTableFiles.addLine(key, col, sLine);
    1079        line.repaint();
    1080      }
    1081    } while(gitOut.nextlineMaxpart().found());
    1082  }
    1083
    1084
    1085
    1086  Thread cmdThread = new Thread("gitGui-Cmd") {
    1087    @Override public void run() {
    1088      do {
    1089        gitCmd.executeCmdQueue(true);
    1090        try {
    1091          synchronized(this){ wait(1000); }
    1092        } catch (InterruptedException e) { }
    1093      } while (!bCmdThreadClose);
    1094    }
    1095  };
    1096
    1097
    1098
    1099
    1100}