Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add listCommits to public API #1742

Open
henri-hulski opened this issue Mar 10, 2023 · 15 comments
Open

Add listCommits to public API #1742

henri-hulski opened this issue Mar 10, 2023 · 15 comments

Comments

@henri-hulski
Copy link

I'm in the process of switching repo-cooker from nodegit to isomorphic-git .
I'm stuck now when trying to list all commits between two commit OIDs for example between two releases.

The signature is

async function getHashListFromHashToHash(fromHash, toHash, dir) {}

and it should return a list of OIDs between fromHash and toHash not including fromHash in historical order (oldest first).

The listCommitsAndTags command seems to be a good solution for this task beside that I need only commits not tags.
Any reason why the command is not public? Would it be possible to make it public maybe with a switch to return only commit hashes.
BTW listObjects seems also to be a quite useful command.

Or do you only want to expose commands which are equivalents to git commands. In this case would this task be possible to do with walk or log commands?

Thank you for this great library!

@henri-hulski
Copy link
Author

henri-hulski commented Mar 10, 2023

@jcubic
Copy link
Contributor

jcubic commented Mar 10, 2023

Sorry, I have no idea, maybe @mojavelinux will know. If not you will need to read the code and figure it out on your own. The problem is that the main author that knows everything about the project left, and we're maintaining it. I'm the most active maintainer but I don't write much code myself and I'm not super familiar with the code base. I only want the code to not die.

@henri-hulski
Copy link
Author

I see. So thanks for your effort. I didn't know the original author left.

@henri-hulski
Copy link
Author

henri-hulski commented Mar 11, 2023

For now I have this solution, if someone is interested.

/** Get commit OID list between fromHash and toHash not including fromHash.
 * Returns the list in historical order (oldest commit first).
 */
async function getHashListFromHashToHash(fromHash, toHash, dir) {
  const logs = (await git.log({ fs, dir, ref: toHash })).map(
    commit => commit.oid
  )

  return logs.splice(logList.indexOf(fromHash)).reverse()
}

Problem is that it retrieves the whole commit history of the branch.
Maybe it would be more efficient with git.walk but not sure how to resolve this.

@jcubic
Copy link
Contributor

jcubic commented Mar 11, 2023

I've asked chatGPT it show invalid code but this is interesting:

const { listCommits } = require('isomorphic-git')

async function traverseHistory(repo, fromRef, toRef) {
  let commits = await listCommits({
    fs: repo,
    ref: fromRef,
    depth: null,
  })

  const toRefCommits = await listCommits({
    fs: repo,
    ref: toRef,
    depth: null,
  })

  const toRefSet = new Set(toRefCommits.map((commit) => commit.oid))

  // Traverse commits in reverse chronological order
  commits = commits.reverse()

  for (const commit of commits) {
    // If this commit is not in the history of the toRef, skip it
    if (!toRefSet.has(commit.oid)) {
      continue
    }

    // Do something with each commit, such as printing its message
    console.log(commit.message)
  }
}

If you want to modify the code of listCommitsAndTags so it's just commits between two refs I will be able to merge those changes seems this is important and it's missing.

This code completely doesn't make sense. but It would be great to have:

function listCommits(dir, gitdir, from, to) {

}

If you want to contribute that would be great.

@jcubic
Copy link
Contributor

jcubic commented Mar 11, 2023

the last chatGPT output shows this:

const { log } = require('isomorphic-git')

async function traverseHistory(repo, fromRef, toRef) {
  const commits = await log({
    fs: repo,
    depth: null,
    ref: `${fromRef}..${toRef}`,
    merges: false,
  })

  // Traverse commits in reverse chronological order
  commits.reverse()

  for (const commit of commits) {
    // Do something with each commit, such as printing its message
    console.log(commit.message)
  }
}

// Example usage
traverseHistory('/path/to/repo', 'master', 'main')

But I'm not able to test this right now. If this doesn't work I would love to have this implemented and documented. Since this is the syntax that is used by canonical git.

@jcubic
Copy link
Contributor

jcubic commented Mar 11, 2023

This may be a very important feature to make parse ref as a range.

BTW: I need to make issues as "work needed".

@henri-hulski
Copy link
Author

I think the only thing to do in the code is adding the incoming OID only to the visited array if it points to a commit.
This would still allow us to use tag refs/OIDs as starting and end points, but they would not be returned.
Also I think we need only one start and end ref/OID, not an array as now.
This should go in a separate public command listCommits.
listCommitsAndTags is used internally by push.
Most work is documentation and tests.

@araknast
Copy link
Contributor

araknast commented Mar 21, 2023

Isomorphic-git already supports the since argument to git.log to limit the results to those after a certain time stamp, so you can specify the timestamp of a certain commit to get results since that commit. You should then be able combine this with the ref argument to get the desired behavior. Something like:

async function commitsSince(fromHash, toHash, dir) {
    let sinceCommit = await git.readCommit({ fs, dir, oid: <fromHash> })
    let sinceDate = new Date(sinceCommit.author.timestamp * 1000)
    return  git.log({ fs, dir, ref: <toHash>, since: sinceDate})
}

This isn't perfect since we are getting commits since a timestamp and not a hash, so if two commits have the same timespamp they will both be returned. Ideally the solution would be an extra argument to git.log, something like sinceRef, then we could stop walking backwards in the history once we see that ref.

@henri-hulski
Copy link
Author

@araknast this was actually the first thing I tried, but the result was not as expected.
Seems that the since argument is somehow buggy or inaccurate.

@araknast
Copy link
Contributor

Hmm, if there is a bug with the since argument I think this is something we should be focusing on, can you provide instructions to reproduce the inaccurate results?

@henri-hulski
Copy link
Author

henri-hulski commented Mar 23, 2023

@araknast It works actually. I just made the same mistake as you. The commit timestamp is not commit.author.timestamp but commit.committer.timestamp.
See https://github.com/isomorphic-git/isomorphic-git/blob/main/src/commands/log.js#L70

I made the same mistake in a different context, so it would be good to mention it in the docs.

The correct function would be

async function commitsSince(fromHash, toHash, dir) {
    const fromCommit = await git.readCommit({ fs, dir, oid: fromHash })
    const since = new Date(fromCommit.commit.committer.timestamp * 1000)
    return  git.log({ fs, dir, ref: toHash, since })
}

@henri-hulski
Copy link
Author

henri-hulski commented Mar 23, 2023

The result doesn't include the fromCommit which is what I want, but I think it's not obvious from the docs.

@jcubic
Copy link
Contributor

jcubic commented Mar 23, 2023

@henri-hulski do you want to update the docs? you have a fresh knowledge about this.

@matejdro
Copy link

Problem with above workaround is that it assumes that all commits between fromHash and toHash are actually newer than fromHash commit. If there is any older commit in there, log will stop prematurely on that commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants