Skip to content

Commit

Permalink
Merge pull request #553 from davidpcaldwell/issue/#174
Browse files Browse the repository at this point in the history
Resolve #174: parse git status renames correctly
  • Loading branch information
davidpcaldwell authored Jun 25, 2022
2 parents 44690b7 + 592da44 commit 6a87445
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 66 deletions.
68 changes: 47 additions & 21 deletions rhino/tools/git/commands.fifty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,31 +71,57 @@ namespace slime.jrunscript.tools.git {
verify(status).evaluate.property("paths").is(void(0));
};

/**
* Verifies the behavior of various operations that affect the git staging area.
*/
fifty.tests.world.staging = function() {
var commit: slime.jrunscript.tools.git.Command<{
message: string
},void> = {
invocation: function(p) {
return {
command: "commit",
arguments: $api.Array.build(function(rv) {
rv.push("--message", p.message);
})
}
var commit: slime.jrunscript.tools.git.Command<{
message: string
},void> = {
invocation: function(p) {
return {
command: "commit",
arguments: $api.Array.build(function(rv) {
rv.push("--message", p.message);
})
}
};
}
};

var reset: slime.jrunscript.tools.git.Command<void,void> = {
invocation: function(p) {
return {
command: "reset"
}
var reset: slime.jrunscript.tools.git.Command<void,void> = {
invocation: function(p) {
return {
command: "reset"
}
};
}
};

var rename: slime.jrunscript.tools.git.Command<{ from: string, to: string },void> = {
invocation: function(p) {
return {
command: "mv",
arguments: [p.from, p.to]
}
}
};

fifty.tests.exports.status.rename = function() {
var it = fixtures.empty();

fixtures.edit(it, "a", function(before) { return "a"; });
it.api.command(add).argument("a").run();
it.api.command(commit).argument({ message: "a" }).run();

it.api.command(rename).argument({ from: "a", to: "b" }).run();

var status = it.api.command(jsh.tools.git.commands.status).argument().run();
verify(status).entries.length.is(1);
verify(status).entries[0].code.is("R ");
verify(status).entries[0].path.is("b");
verify(status).entries[0].orig_path.is("a");
jsh.shell.console(JSON.stringify(status));
};

/**
* Verifies the behavior of various operations that affect the git staging area.
*/
fifty.tests.world.staging = function() {
function status() {
return it.api.command(jsh.tools.git.commands.status).argument().run();
}
Expand Down
30 changes: 26 additions & 4 deletions rhino/tools/git/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,13 @@
},
result: function(output) {
// TODO This ignores renamed files; see git help status
/**
* @type { slime.jrunscript.tools.git.command.status.Result }
*/
var rv = {
branch: void(0)
branch: void(0),
entries: [],
paths: void(0)
};
output.split("\n").forEach(function(line) {
var NO_COMMITS_PREFIX = "## No commits yet on ";
Expand All @@ -42,18 +47,35 @@
var detached = Boolean(branchName == "HEAD (no branch)")
rv.branch = (detached) ? null : branchName;
} else {
var parser = /(..) (\S+)/;
var parser = /(..) (\S+)(?: -> (\S+))?/;
var match = parser.exec(line);
if (match) {
if (!rv.paths) rv.paths = {};
rv.paths[match[2]] = match[1];
if (match[3]) {
rv.entries.push({
code: match[1],
path: match[3],
orig_path: match[2]
});
} else {
rv.entries.push({
code: match[1],
path: match[2]
});
}
} else if (line == "") {
// do nothing
} else {
throw new Error("Unexpected line: [" + line + "]");
}
}
});
rv.paths = rv.entries.reduce(function(rv,entry) {
if (typeof(rv) == "undefined") {
rv = {};
}
rv[entry.path] = entry.code;
return rv;
}, rv.paths);
return rv;
}
}
Expand Down
12 changes: 12 additions & 0 deletions rhino/tools/git/module.fifty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -758,12 +758,24 @@ namespace slime.jrunscript.tools.git {
*/
branch: string

entries: {
code: string
path: string
orig_path?: string
}[]

/**
* @deprecated Replaced by the `entries` property, which properly captures rename entries.
*
* An object whose keys are string paths within the repository, and whose values are the two-letter output
* of the `git status --porcelain` command. This property is absent if no files have a status.
*/
paths?: { [path: string]: string }
}

export namespace old {
export type Result = Pick<command.status.Result,"branch" | "paths">
}
}

export interface Exports {
Expand Down
73 changes: 47 additions & 26 deletions tools/wf/plugin-standard.jsh.fifty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ namespace slime.jsh.wf {
function fixture() {
var project = fifty.jsh.file.object.temporary.location();
fifty.jsh.file.object.getRelativePath("test/data/plugin-standard").directory.copy(project);

// Add a sample file to the fixture
project.directory.getRelativePath("a.js").write("", { append: false });

var repository = jsh.tools.git.init({
pathname: project
});
Expand Down Expand Up @@ -391,17 +395,18 @@ namespace slime.jsh.wf {
}
};

var fixtures = {
git: (
function() {
var script: slime.jrunscript.tools.git.test.fixtures.Script = fifty.$loader.script("../../rhino/tools/git/fixtures.ts");
var setup = script();
return setup(fifty);
}
)(),
subject: test.fixtures
};

fifty.tests.issue485 = function() {
var fixtures = {
git: (
function() {
var script: slime.jrunscript.tools.git.test.fixtures.Script = fifty.$loader.script("../../rhino/tools/git/fixtures.ts");
var setup = script();
return setup(fifty);
}
)(),
subject: test.fixtures
};
var cloned = fixtures.subject.project();
var repository = toGitFixturesRepository(cloned);
jsh.shell.world.run(
Expand All @@ -425,26 +430,42 @@ namespace slime.jsh.wf {
} catch (e) {
success = false;
}
// var result = jsh.shell.run({
// command: cloned.directory.getRelativePath("slime/tools/wf.bash").toString(),
// arguments: ["commit", "--message", "test"],
// directory: cloned.directory,
// // TODO add access to $api.Object in Fifty tests
// environment: Object.assign({},
// jsh.shell.environment,
// { PROJECT: cloned.directory.toString() }
// ),
// // stdio: {
// // output: String,
// // error: String
// // },
// evaluate: function(result) { return result; }
// });
// verify(result).status.is(1);
verify(success).is(false);
var after = repository.api.command(jsh.tools.git.commands.status).argument().run();
verify(after).paths["wf.js"].is(" M");
}

fifty.tests.issue174 = function() {
var mv: slime.jrunscript.tools.git.Command<{ from: string, to: string },void> = {
invocation: function(p) {
return {
command: "mv",
arguments: [p.from, p.to]
}
}
};

var project = toGitFixturesRepository(fixtures.subject.project());

jsh.shell.world.run(
jsh.shell.Invocation.create({
command: $api.Function.result(project.location, jsh.file.world.Location.relative("wf")).pathname,
arguments: ["status"]
})
)({
});

fixtures.git.edit(project, "new.js", () => "new");
project.api.command(mv).argument({ from: "a.js", to: "b.js" }).run();

jsh.shell.world.run(
jsh.shell.Invocation.create({
command: $api.Function.result(project.location, jsh.file.world.Location.relative("wf")).pathname,
arguments: ["status"]
})
)({
});
}
}
//@ts-ignore
)(fifty);
Expand Down
36 changes: 21 additions & 15 deletions tools/wf/plugin-standard.jsh.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,26 +195,32 @@

$exports.status = function(p) {
// TODO add option for offline
var repository = jsh.wf.git.fetch();
var oRepository = jsh.wf.git.fetch();
var fRepository = jsh.tools.git.program({ command: "git" }).repository(oRepository.directory.toString());
var remote = "origin";
var status = repository.status();
var branch = status.branch.name;
var status = fRepository.command(jsh.tools.git.commands.status).argument().run();
var branch = status.branch;
var base = remote + "/" + branch;
if (!branchExists(repository.directory.toString(), base)) {
if (!branchExists(oRepository.directory.toString(), base)) {
base = "origin/master";
}
var vsRemote = (branch) ? jsh.wf.git.compareTo(base)(repository) : null;
jsh.shell.console("Current branch: " + displayBranchName(status.branch.name));
var vsRemote = (branch) ? jsh.wf.git.compareTo(base)(oRepository) : null;
jsh.shell.console("Current branch: " + displayBranchName(status.branch));
if (vsRemote && vsRemote.ahead.length) jsh.shell.console("ahead of " + base + ": " + vsRemote.ahead.length);
if (vsRemote && vsRemote.behind.length) jsh.shell.console("behind " + base + ": " + vsRemote.behind.length);
var output = $api.Function.result(
status.paths,
status.entries,
$api.Function.conditional({
condition: Boolean,
condition: function(entries) {
return entries.length > 0;
},
true: $api.Function.pipe(
Object.entries,
$api.Function.Array.map(function(entry) {
return entry[1] + " " + entry[0];
if (entry.orig_path) {
return entry.code + " " + entry.path + " (was: " + entry.orig_path + ")";
} else {
return entry.code + " " + entry.path;
}
}),
$api.Function.Array.join("\n")
),
Expand All @@ -224,15 +230,15 @@
if (output) jsh.shell.console(output);
if (vsRemote && vsRemote.behind.length && !vsRemote.ahead.length && !vsRemote.paths) {
jsh.shell.console("Fast-forwarding ...");
repository.merge({ ffOnly: true, name: base });
oRepository.merge({ ffOnly: true, name: base });
}
var branches = repository.branch({ remote: true, all: true });
var branches = oRepository.branch({ remote: true, all: true });
var first = true;
branches.forEach(function findUnmergedBranches(branch) {
if (branch.name === null) {
return;
} else {
var compared = jsh.wf.git.compareTo(branch.name)(repository);
var compared = jsh.wf.git.compareTo(branch.name)(oRepository);
if (compared.behind.length) {
if (first) {
jsh.shell.console("");
Expand All @@ -245,7 +251,7 @@
if (!first) {
jsh.shell.console("");
}
if (repository.submodule().length) {
if (oRepository.submodule().length) {
jsh.shell.console("");
jsh.shell.console("Submodules:");
var submodules = jsh.wf.project.submodule.status();
Expand Down Expand Up @@ -482,7 +488,7 @@
}
) : void(0),
remove: $api.Function.pipe(
$api.Function.impure.revise(jsh.script.cli.option.string({ longname: "path" })),
jsh.script.cli.option.string({ longname: "path" }),
function(p) {
var path = p.options.path;
jsh.wf.project.submodule.remove({ path: path });
Expand Down

0 comments on commit 6a87445

Please sign in to comment.