int CLogDataVector::ParserFromRefLog(CString ref)
{
	if(g_Git.m_IsUseGitDLL)
	{
		git_for_each_reflog_ent(CUnicodeUtils::GetUTF8(ref),AddTolist,this);
		for(int i=0;i<size();i++)
		{
			m_pLogCache->m_HashMap[at(i)].m_Ref.Format(_T("%s{%d}"), ref,i);
		}

	}
	else
	{

		CString cmd, out;
		GitRev rev;
		cmd.Format(_T("git.exe reflog show %s"),ref);
		if (g_Git.Run(cmd, &out, NULL, CP_UTF8))
			return -1;

		int pos=0;
		while(pos>=0)
		{
			CString one=out.Tokenize(_T("\n"),pos);
			int ref=one.Find(_T(' '),0);
			if(ref<0)
				continue;

			rev.Clear();

			rev.m_CommitHash=g_Git.GetHash(one.Left(ref));
			int action=one.Find(_T(' '),ref+1);
			int message;
			if(action>0)
			{
				rev.m_Ref=one.Mid(ref+1,action-ref-2);
				message=one.Find(_T(":"),action);
				if(message>0)
				{
					rev.m_RefAction=one.Mid(action+1,message-action-1);
					rev.GetSubject()=one.Right(one.GetLength()-message-1);
				}
			}

			this->m_pLogCache->m_HashMap[rev.m_CommitHash]=rev;

			this->push_back(rev.m_CommitHash);

		}
	}
	return 0;
}
//CLogDataVector Class
int CLogDataVector::ParserFromLog(CTGitPath *path ,int count ,int infomask,CString *from,CString *to)
{
	// only enable --follow on files
	if ((path == NULL || path->IsDirectory()) && (infomask & CGit::LOG_INFO_FOLLOW))
		infomask = infomask ^ CGit::LOG_INFO_FOLLOW;

	CString hash;
	CString cmd=g_Git.GetLogCmd(hash,path,count,infomask,from,to,true);

	if(g_Git.IsInitRepos())
		return 0;

	git_init();

	GIT_LOG handle;
	if(git_open_log(&handle,CUnicodeUtils::GetMulti(cmd,CP_ACP).GetBuffer()))
	{
		return -1;
	}

	git_get_log_firstcommit(handle);

	GIT_COMMIT commit;

	GitRev rev;

	while (git_get_log_nextcommit(handle, &commit, infomask & CGit::LOG_INFO_FOLLOW) == 0)
	{
		if (commit.m_ignore == 1)
		{
			git_free_commit(&commit);
			continue;
		}

		CGitHash hash = (char*)commit.m_hash ;
		rev.Clear();

		GitRev *pRev = this->m_pLogCache->GetCacheData(hash);

		char *note=NULL;
		git_get_notes(commit.m_hash,&note);
		if(note)
		{
			pRev->m_Notes.Empty();
			g_Git.StringAppend(&pRev->m_Notes,(BYTE*)note);
		}

		if(pRev == NULL || !pRev->m_IsFull)
		{
			pRev->ParserFromCommit(&commit);
			pRev->ParserParentFromCommit(&commit);
			git_free_commit(&commit);
			//Must call free commit before SafeFetchFullInfo, commit parent is rewrite by log.
			//file list will wrong if parent rewrite.
			pRev->SafeFetchFullInfo(&g_Git);

		}
		else
		{
			ASSERT(pRev->m_CommitHash == hash);
			pRev->ParserParentFromCommit(&commit);
			git_free_commit(&commit);
		}

		this->push_back(pRev->m_CommitHash);

		m_HashMap[rev.m_CommitHash]=size()-1;

	}

	git_close_log(handle);

	return 0;
}
void GetRevParsingTests()
{
	GitRev rev;
	EXPECT_TRUE(rev.m_CommitHash.IsEmpty());
	EXPECT_EQ(0, rev.GetCommit(_T("HEAD")));
	EXPECT_STREQ(_T("7c3cbfe13a929d2291a574dca45e4fd2d2ac1aa6"), rev.m_CommitHash.ToString());
	EXPECT_STREQ(_T("Sven Strickroth"), rev.GetAuthorName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail());
	EXPECT_STREQ(_T("2015-03-07 18:03:58"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Sven Strickroth"), rev.GetCommitterName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail());
	EXPECT_STREQ(_T("2015-03-07 18:03:58"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Changed ASCII file"), rev.GetSubject());
	EXPECT_STREQ(_T(""), rev.GetBody());
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
	EXPECT_EQ(0, rev.ParentsCount());
	EXPECT_EQ(0, rev.GetParentFromHash(rev.m_CommitHash));
	ASSERT_EQ(1, rev.ParentsCount());
	EXPECT_STREQ(_T("1fc3c9688e27596d8717b54f2939dc951568f6cb"), rev.m_ParentHash[0].ToString());
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
	rev.Clear();
	EXPECT_EQ(0, rev.GetCommit(GitRev::GetWorkingCopy()));
	EXPECT_TRUE(rev.m_CommitHash.IsEmpty());
	EXPECT_STREQ(_T(""), rev.GetAuthorName());
	EXPECT_STREQ(_T(""), rev.GetAuthorEmail());
	EXPECT_STREQ(_T(""), rev.GetCommitterName());
	EXPECT_STREQ(_T(""), rev.GetCommitterEmail());
	EXPECT_STREQ(_T("Working Copy"), rev.GetSubject());
	EXPECT_STREQ(_T(""), rev.GetBody());
	EXPECT_EQ(0, rev.ParentsCount());
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
	rev.Clear();
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
	EXPECT_EQ(0, rev.GetCommit(_T("aa5b97f89cea6863222823c8289ce392d06d1691")));
	EXPECT_STREQ(_T("aa5b97f89cea6863222823c8289ce392d06d1691"), rev.m_CommitHash.ToString());
	EXPECT_STREQ(_T("Another dummy with ümlaut"), rev.GetAuthorName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail());
	EXPECT_STREQ(_T("2015-03-14 22:30:06"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Another dummy with ümlaut"), rev.GetCommitterName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail());
	EXPECT_STREQ(_T("2015-03-14 22:30:06"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Subject line"), rev.GetSubject());
	EXPECT_STREQ(_T("\nalso some more lines\n\nhere in body\n\nSigned-off-by: Another dummy with ümlaut <*****@*****.**>\n"), rev.GetBody());
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
	rev.Clear();
	EXPECT_TRUE(rev.m_CommitHash.IsEmpty());
	EXPECT_EQ(0, rev.GetCommit(_T("1fc3c9688e27596d8717b54f2939dc951568f6cb")));
	EXPECT_STREQ(_T("1fc3c9688e27596d8717b54f2939dc951568f6cb"), rev.m_CommitHash.ToString());
	EXPECT_STREQ(_T("Some other User"), rev.GetAuthorName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail());
	EXPECT_STREQ(_T("2015-03-07 18:03:39"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Sven Strickroth"), rev.GetCommitterName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail());
	EXPECT_STREQ(_T("2015-03-07 18:03:39"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Added an ascii file"), rev.GetSubject());
	EXPECT_STREQ(_T(""), rev.GetBody());
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
	rev.Clear();
	EXPECT_EQ(-1, rev.GetCommit(_T("does-not-exist")));
	EXPECT_FALSE(rev.GetLastErr().IsEmpty());
	EXPECT_TRUE(rev.m_CommitHash.IsEmpty());
	rev.Clear();
	CGitHash hash(_T("aa5b97f89cea6863222823c8289ce392d06d1691"));
	EXPECT_EQ(0, rev.GetCommitFromHash(hash));
	EXPECT_EQ(hash, rev.m_CommitHash);
	EXPECT_STREQ(_T("Another dummy with ümlaut"), rev.GetAuthorName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail());
	EXPECT_STREQ(_T("2015-03-14 22:30:06"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Another dummy with ümlaut"), rev.GetCommitterName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail());
	EXPECT_STREQ(_T("2015-03-14 22:30:06"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Subject line"), rev.GetSubject());
	EXPECT_STREQ(_T("\nalso some more lines\n\nhere in body\n\nSigned-off-by: Another dummy with ümlaut <*****@*****.**>\n"), rev.GetBody());
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
	rev.Clear();
	EXPECT_EQ(0, rev.GetCommit(_T("8d1ebbcc7eeb63af10ff8bcf7712afb9fcc90b8a")));
	EXPECT_STREQ(_T("8d1ebbcc7eeb63af10ff8bcf7712afb9fcc90b8a"), rev.m_CommitHash.ToString());
	EXPECT_STREQ(_T("Sven Strickroth"), rev.GetAuthorName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail());
	EXPECT_STREQ(_T("2015-03-04 17:50:24"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Sven Strickroth"), rev.GetCommitterName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail());
	EXPECT_STREQ(_T("2015-03-04 17:50:24"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Merge branch 'for-merge' into subdir/branch"), rev.GetSubject());
	EXPECT_STREQ(_T(""), rev.GetBody());
	EXPECT_EQ(0, rev.ParentsCount());
	EXPECT_EQ(0, rev.GetParentFromHash(rev.m_CommitHash));
	ASSERT_EQ(2, rev.ParentsCount());
	EXPECT_STREQ(_T("3686b9cf74f1a4ef96d6bfe736595ef9abf0fb8d"), rev.m_ParentHash[0].ToString());
	EXPECT_STREQ(_T("1ce788330fd3a306c8ad37654063ceee13a7f172"), rev.m_ParentHash[1].ToString());
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
	rev.Clear();
	EXPECT_TRUE(rev.m_CommitHash.IsEmpty());
	EXPECT_EQ(0, rev.GetCommit(_T("844309789a13614b52d5e7cbfe6350dd73d1dc72"))); // root-commit
	EXPECT_STREQ(_T("844309789a13614b52d5e7cbfe6350dd73d1dc72"), rev.m_CommitHash.ToString());
	EXPECT_STREQ(_T("Sven Strickroth"), rev.GetAuthorName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail());
	EXPECT_STREQ(_T("2015-03-04 17:35:13"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Sven Strickroth"), rev.GetCommitterName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail());
	EXPECT_STREQ(_T("2015-03-04 17:35:13"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("added ansi file"), rev.GetSubject());
	EXPECT_STREQ(_T(""), rev.GetBody());
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
	EXPECT_EQ(0, rev.ParentsCount());
	EXPECT_EQ(0, rev.GetParentFromHash(rev.m_CommitHash));
	EXPECT_EQ(0, rev.ParentsCount());
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
	rev.Clear();
	// GPG signed commit which was also amended with different dates
	EXPECT_EQ(0, rev.GetCommit(_T("subdir/branch")));
	EXPECT_STREQ(_T("4c5c93d2a0b368bc4570d5ec02ab03b9c4334d44"), rev.m_CommitHash.ToString());
	EXPECT_STREQ(_T("Sven Strickroth"), rev.GetAuthorName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetAuthorEmail());
	EXPECT_STREQ(_T("2015-03-16 12:52:29"), rev.GetAuthorDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Sven Strickroth"), rev.GetCommitterName());
	EXPECT_STREQ(_T("*****@*****.**"), rev.GetCommitterEmail());
	EXPECT_STREQ(_T("2015-03-16 13:06:08"), rev.GetCommitterDate().FormatGmt(L"%Y-%m-%d %H:%M:%S"));
	EXPECT_STREQ(_T("Several actions"), rev.GetSubject());
	EXPECT_STREQ(_T("\n* amended with different date\n* make utf16-be-nobom.txt a symlink ti ascii.txt\n* remove utf8-bom.txt\n* Copied ascii.txt\n\nSigned-off-by: Sven Strickroth <*****@*****.**>\n"), rev.GetBody());
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
	EXPECT_EQ(0, rev.ParentsCount());
	EXPECT_EQ(0, rev.GetParentFromHash(rev.m_CommitHash));
	ASSERT_EQ(1, rev.ParentsCount());
	EXPECT_STREQ(_T("aa5b97f89cea6863222823c8289ce392d06d1691"), rev.m_ParentHash[0].ToString());
	EXPECT_TRUE(rev.GetLastErr().IsEmpty());
}
int ParserFromRefLog(CString ref, std::vector<GitRev> &refloglist)
{
	refloglist.clear();
	if (g_Git.m_IsUseLibGit2)
	{
		CAutoRepository repo(g_Git.GetGitRepository());
		if (!repo)
		{
			MessageBox(nullptr, CGit::GetLibGit2LastErr(_T("Could not open repository.")), _T("TortoiseGit"), MB_ICONERROR);
			return -1;
		}

		CAutoReflog reflog;
		if (git_reflog_read(reflog.GetPointer(), repo, CUnicodeUtils::GetUTF8(ref)) < 0)
		{
			MessageBox(nullptr, CGit::GetLibGit2LastErr(_T("Could not read reflog.")), _T("TortoiseGit"), MB_ICONERROR);
			return -1;
		}

		for (size_t i = 0; i < git_reflog_entrycount(reflog); ++i)
		{
			const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i);
			if (!entry)
				continue;

			GitRev rev;
			rev.m_CommitHash = (char *)git_reflog_entry_id_new(entry)->id;
			rev.m_Ref.Format(_T("%s@{%d}"), ref, i);
			rev.GetCommitterDate() = CTime(git_reflog_entry_committer(entry)->when.time);
			if (git_reflog_entry_message(entry) != nullptr)
			{
				CString one;
				g_Git.StringAppend(&one, (BYTE *)git_reflog_entry_message(entry));
				int message = one.Find(_T(":"), 0);
				if (message > 0)
				{
					rev.m_RefAction = one.Left(message);
					rev.GetSubject() = one.Mid(message + 1);
				}
			}
			refloglist.push_back(rev); 
		}
	}
	else if (g_Git.m_IsUseGitDLL)
	{
		git_for_each_reflog_ent(CUnicodeUtils::GetUTF8(ref), AddToRefLoglist, &refloglist);
		for (size_t i = 0; i < refloglist.size(); ++i)
			refloglist[i].m_Ref.Format(_T("%s@{%d}"), ref, i);
	}
	else
	{
		CString cmd, out;
		GitRev rev;
		cmd.Format(_T("git.exe reflog show --pretty=\"%%H %%gD: %%gs\" --date=raw %s"), ref);
		if (g_Git.Run(cmd, &out, NULL, CP_UTF8))
			return -1;

		int i = 0;
		CString prefix = ref + _T("@{");
		int pos = 0;
		while (pos >= 0)
		{
			CString one = out.Tokenize(_T("\n"), pos);
			int refPos = one.Find(_T(' '), 0);
			if (refPos < 0)
				continue;

			rev.Clear();

			CString hashStr = one.Left(refPos);
			rev.m_CommitHash = hashStr;
			rev.m_Ref.Format(_T("%s@{%d}"), ref, i++);
			int prefixPos = one.Find(prefix, refPos + 1);
			if (prefixPos != refPos + 1)
				continue;

			int spacePos = one.Find(_T(' '), prefixPos + prefix.GetLength() + 1);
			if (spacePos < 0)
				continue;

			CString timeStr = one.Mid(prefixPos + prefix.GetLength(), spacePos - prefixPos - prefix.GetLength());
			rev.GetCommitterDate() = CTime(_ttoi(timeStr));
			int action = one.Find(_T("}: "), spacePos + 1);
			if (action > 0)
			{
				action += 2;
				int message = one.Find(_T(":"), action);
				if (message > 0)
				{
					rev.m_RefAction = one.Mid(action + 1, message - action - 1);
					rev.GetSubject() = one.Right(one.GetLength() - message - 1);
				}
			}

			refloglist.push_back(rev);
		}
	}
	return 0;
}