将git show输出转换为入口对象和对应的路径树对象

时间:2019-03-17 14:57:22

标签: javascript git object

我正在寻找一种从git show输出创建对象的方法 我使用git show -m --name-status --oneline <commit-hash>。输出是这样的:

5b5f664 (from fd9fe89) Merge branch 'develop' into COMMIT-4
M       src/app/app.module.ts
M       src/app/components/accordion/accordion.component.scss
A       src/app/components/file-diff-commit/file-diff-commit.component.html
A       src/app/components/file-diff-commit/file-diff-commit.component.ts
M       src/app/models/MockGitService.ts
M       src/app/models/MockLeftPanelService.ts
M       src/assets/i18n/en.json
M       src/assets/i18n/fr.json
M       package.json
5b5f664 (from a0ebd90) Merge branch 'develop' into COMMIT-4
M       package.json
M       src/app/components/copy-button/copy-button.component.scss
A       src/app/models/CommitInformations.ts
M       src/app/models/MockGitService.ts
M       src/app/models/MockRightPanelService.ts

我只希望两个提交描述之间的部分并获得这种对象:

[
    { status: "M",  path: "src/app/app.module.ts" },
    { status: "M",  path: "src/app/components/accordion/accordion.component.scss" },
    { status: "A",  path: "src/app/components/file-diff-commit/file-diff-commit.component.html" },
    { status: "A",  path: "src/app/components/file-diff-commit/file-diff-commit.component.ts" },
    { status: "M",  path: "src/app/models/MockGitService.ts" },
    { status: "M",  path: "src/app/models/MockLeftPanelService.ts" },
    { status: "M",  path: "src/assets/i18n/en.json" },
    { status: "M",  path: "src/assets/i18n/fr.json" },
    { status: "M",  path: "package.json" }
]

还有

{
    "src": {
        "app": {
            { file: "app.module.ts", status: "M" },
            "components": {
                "accordion": {
                    { file: "accordion.component.scss" status: "m" }
                }
                ...
            }
        },
        "assets": {
            ....
        }
    },
    { file: "package.json", status: "M" }
}

我不知道如何创建第二个对象。但是我第一次尝试了这个:

var first = test.split(/\n/).map(x => {
    return (x.split(/\s{7}/g))
}).filter(o => o.length === 2);
console.log(first);

问题是我在第二次提交描述之后得到了一部分。

如何继续创建这两个对象?

编辑 第一个对象的工作解决方案

var output = `5b5f664 (from fd9fe89) Merge branch 'develop' into COMMIT-4
M       src/app/app.module.ts
M       src/app/components/accordion/accordion.component.scss
A       src/app/components/file-diff-commit/file-diff-commit.component.html
A       src/app/components/file-diff-commit/file-diff-commit.component.ts
M       src/app/models/MockGitService.ts
M       src/app/models/MockLeftPanelService.ts
M       src/assets/i18n/en.json
M       src/assets/i18n/fr.json
M       package.json
5b5f664 (from a0ebd90) Merge branch 'develop' into COMMIT-4
M       package.json
M       src/app/components/copy-button/copy-button.component.scss
A       src/app/models/CommitInformations.ts
M       src/app/models/MockGitService.ts
M       src/app/models/MockRightPanelService.ts`;

var temp = output.split(/\n/);
temp.shift();
var arr = temp.map(x => {
	return (x.split(/\s{7}/g))
})

var final = [];
for (s in arr)  {
	if (Array.isArray(arr[s]) && arr[s].length === 2) {
  	final.push({ status: arr[s][0], path: arr[s][1]});
  } else {
  	break;
  }
}
console.log(final);

1 个答案:

答案 0 :(得分:2)

在下面,您将找到带有轻型单元测试的可行解决方案。

我不得不更改第二个对象结构,并用预期的数据填充它以完成测试。

// this is the git output provided by the OP
const output = `
5b5f664 (from fd9fe89) Merge branch 'develop' into COMMIT-4
M       src/app/app.module.ts
M       src/app/components/accordion/accordion.component.scss
A       src/app/components/file-diff-commit/file-diff-commit.component.html
A       src/app/components/file-diff-commit/file-diff-commit.component.ts
M       src/app/models/MockGitService.ts
M       src/app/models/MockLeftPanelService.ts
M       src/assets/i18n/en.json
M       src/assets/i18n/fr.json
M       package.json
5b5f664 (from a0ebd90) Merge branch 'develop' into COMMIT-4
M       package.json
M       src/app/components/copy-button/copy-button.component.scss
A       src/app/models/CommitInformations.ts
M       src/app/models/MockGitService.ts
M       src/app/models/MockRightPanelService.ts
`
// this is the first shape of object required by the OP:
// it looks like the result of splitting each git output line into the file and its status
// the OP mentionned that he/she wanted only the first commit, the solution below generates a set of object per commit
const entries = [
    { status: "M",  path: "src/app/app.module.ts" },
    { status: "M",  path: "src/app/components/accordion/accordion.component.scss" },
    { status: "A",  path: "src/app/components/file-diff-commit/file-diff-commit.component.html" },
    { status: "A",  path: "src/app/components/file-diff-commit/file-diff-commit.component.ts" },
    { status: "M",  path: "src/app/models/MockGitService.ts" },
    { status: "M",  path: "src/app/models/MockLeftPanelService.ts" },
    { status: "M",  path: "src/assets/i18n/en.json" },
    { status: "M",  path: "src/assets/i18n/fr.json" },
    { status: "M",  path: "package.json" }
];

// This is the second object shape required by the OP, slightly modified, it looks like a full folder tree structure built from the entries extracted before
const folderTree = {
    folders: {
        "src": {
            folders: {
                "app": {
                    folders: {
                        "components": {
                            folders: {
                                "accordion": {
                                    files: {
                                        "accordion.component.scss": { status: "m" }
                                    }
                                },
                                "file-diff-commit": {
                                    files: {
                                        "file-diff-commit.component.html": { status: "a" },
                                        "file-diff-commit.component.ts": { status: "a" }
                                    }
                                }
                            }
                        },
                        "models": {
                            files: {
                                "MockGitService.ts": { status: "m" },
                                "MockLeftPanelService.ts": { status: "m" }
                            }
                        }
                    },
                    files: {
                        "app.module.ts": { status: "m" },
                    }
                },
                "assets": {
                    folders: {
                        "i18n": {
                            files: {
                                "en.json": { status: "m" },
                                "fr.json": { status: "m" }
                            }
                        }
                    }
                }
            }
        }
    },
    files: {
        "package.json": { status: "m" }
    }
};
// this will serve as commit splitter
const commitReg = /[a-f0-9]{7} \(from [a-f0-9]{7}\) Merge branch '[\w\d-_]+' into [\w\d-_]+\n/g;

// converts the first object shape into the second one
function buildTreeFromEntries(entries) {
  // Note and entry is { status: A|M|U, path: /\w+(/\w+)*\.\w+ }
  const root = {};

  entries.forEach((entry) => {
    const pathSplit = entry.path.split('/');
    if (pathSplit.length < 2) {
      if (!root.files) {
        root.files = {};
      }
      root.files[pathSplit[0]] = { status: entry.status.toLowerCase() };
      return;
    }
    let current = root;

    pathSplit.forEach((p, index, list) => {
      if (index === list.length - 1) {
        if (!current.files) {
          current.files = {};
        }
        current.files[p] = { status: entry.status.toLowerCase() };
        return;
      }
      if (!current.folders) {
        current.folders = {};
      }
      if (!current.folders[p]) {
        current.folders[p] = {};
      }
      current = current.folders[p];
    });
  });
  return root;
}

// returns a set of results as requested by the OP by commit
function parseGitOutput(output) {
  const commits = output.split(commitReg).filter(c => c.trim() !== '');

  const result = [];

  commits.forEach((commit) => {
    const currentEntries = [];
    const currentCommitResult = { entries: currentEntries };
    result.push(currentCommitResult);
    const files = commit.split('\n').filter(c => c.trim() !== '');

    files.forEach((file) => {
      const statusFilePair = file.split(/\s+/);
      const path = statusFilePair[1];
      const status = statusFilePair[0];
      currentEntries.push({ status, path });
    });
  });

  result.forEach((commitResult) => {
    commitResult.folderTree = buildTreeFromEntries(commitResult.entries);
  });
  return result;
}

// for testing purpose only, the test is far from perfect but more or less proves the concept
function objectMatch(obj1, obj2) {
  const keys = Object.keys(obj1);
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];
    const v1 = obj1[key];
    const v2 = obj2[key];
    if (typeof v1 !== typeof v2) {
      console.log(v1, ' is not the same type as ', v2);
      return false;
    }
    if (typeof v1 === 'string') {
      if (v1 !== v2) {
        console.log(v1, ' !== ', v2);
        return false;
      }
      continue;
    }
    if (!objectMatch(v1, v2)) {
      return false;
    }
  }
  return true;
}

// parsing
const parsedOutput = parseGitOutput(output)[0];

// console output of the results and tests
console.log(parsedOutput.entries);
console.log(
  "entries match:",
  parsedOutput.entries.every((entry, index) => objectMatch(entries[index], entry))
);

console.log("tree match:", parsedOutput.folderTree, folderTree);
console.log(parsedOutput.folderTree);