// ReadTree is/must only be executed on an empty list
int CGitHeadFileList::ReadTree()
{
	CAutoWriteLock lock(m_SharedMutex);
	ATLASSERT(empty());

	CAutoRepository repository(m_Gitdir);
	CAutoCommit commit;
	CAutoTree tree;
	bool ret = repository;
	ret = ret && !git_commit_lookup(commit.GetPointer(), repository, (const git_oid*)m_Head.m_hash);
	ret = ret && !git_commit_tree(tree.GetPointer(), commit);
	try
	{
		ret = ret && !ReadTreeRecursive(*repository, tree, "", CGitHeadFileList::CallBack, this);
	}
	catch (const std::bad_alloc& ex)
	{
		CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Catched exception inside ReadTreeRecursive: %s\n", ex.what());
		return -1;
	}
	if (!ret)
	{
		clear();
		m_LastModifyTimeHead = 0;
		return -1;
	}

	std::sort(this->begin(), this->end(), SortTree);
	m_TreeHash = git_commit_id(commit)->id;

	return 0;
}
Exemple #2
0
int GitRev::GetCommitFromHash(git_repository* repo, const CGitHash& hash)
{
	CAutoCommit commit;
	if (git_commit_lookup(commit.GetPointer(), repo, (const git_oid*)hash.m_hash) < 0)
	{
		m_sErr = CGit::GetLibGit2LastErr();
		return -1;
	}

	return ParserFromCommit(commit);
}
Exemple #3
0
// ReadTree is/must only be executed on an empty list
int CGitHeadFileList::ReadTree()
{
	CAutoWriteLock lock(m_SharedMutex);
	ATLASSERT(empty());

	CAutoRepository repository(m_Gitdir);
	CAutoCommit commit;
	CAutoTree tree;
	bool ret = repository;
	ret = ret && !git_commit_lookup(commit.GetPointer(), repository, (const git_oid*)m_Head.m_hash);
	ret = ret && !git_commit_tree(tree.GetPointer(), commit);
	ret = ret && !ReadTreeRecursive(*repository, tree, "", CGitHeadFileList::CallBack, this);
	if (!ret)
	{
		clear();
		m_LastModifyTimeHead = 0;
		return -1;
	}

	std::sort(this->begin(), this->end(), SortTree);
	m_TreeHash = git_commit_id(commit)->id;

	return 0;
}
int CGitRefCompareList::AddEntry(git_repository* repo, const CString& ref, const CGitHash* oldHash, const CGitHash* newHash)
{
	RefEntry entry;
	entry.fullName = ref;
	entry.shortName = CGit::GetShortName(ref, &entry.refType);
	if (oldHash)
		entry.oldHash = oldHash->ToString().Left(g_Git.GetShortHASHLength());
	if (newHash)
		entry.newHash = newHash->ToString().Left(g_Git.GetShortHASHLength());

	CAutoCommit oldCommit;
	if (oldHash)
	{
		if (!git_commit_lookup(oldCommit.GetPointer(), repo, (const git_oid *)&oldHash->m_hash))
			entry.oldMessage = GetCommitMessage(oldCommit);
	}

	CAutoCommit newCommit;
	if (newHash)
	{
		if (!git_commit_lookup(newCommit.GetPointer(), repo, (const git_oid *)&newHash->m_hash))
			entry.newMessage = GetCommitMessage(newCommit);
	}

	if (oldHash && newHash)
	{
		if (*oldHash == *newHash)
		{
			entry.change.LoadString(IDS_SAME);
			entry.changeType = ChangeType::Same;
		}
		else
		{
			size_t ahead = 0, behind = 0;
			if (!git_graph_ahead_behind(&ahead, &behind, repo, (const git_oid *)&newHash->m_hash, (const git_oid *)&oldHash->m_hash))
			{
				CString change;
				if (ahead > 0 && behind == 0)
				{
					entry.change.Format(IDS_FORWARDN, ahead);
					entry.changeType = ChangeType::FastForward;
				}
				else if (ahead == 0 && behind > 0)
				{
					entry.change.Format(IDS_REWINDN, behind);
					entry.changeType = ChangeType::Rewind;
				}
				else
				{
					git_time_t oldTime = git_commit_committer(oldCommit)->when.time;
					git_time_t newTime = git_commit_committer(newCommit)->when.time;
					if (oldTime < newTime)
					{
						entry.change.LoadString(IDS_SUBMODULEDIFF_NEWERTIME);
						entry.changeType = ChangeType::NewerTime;
					}
					else if (oldTime > newTime)
					{
						entry.change.LoadString(IDS_SUBMODULEDIFF_OLDERTIME);
						entry.changeType = ChangeType::OlderTime;
					}
					else
					{
						entry.change.LoadString(IDS_SUBMODULEDIFF_SAMETIME);
						entry.changeType = ChangeType::SameTime;
					}
				}
			}
		}
	}
	else if (oldHash)
	{
		entry.change.LoadString(IDS_DELETED);
		entry.changeType = ChangeType::Deleted;
	}
	else if (newHash)
	{
		entry.change.LoadString(IDS_NEW);
		entry.changeType = ChangeType::New;
	}

	m_RefList.push_back(entry);
	return (int)m_RefList.size() - 1;
}
int CRepositoryBrowser::ReadTree(CShadowFilesTree * treeroot, const CString& root)
{
	CWaitCursor wait;
	CAutoRepository repository(g_Git.GetGitRepository());
	if (!repository)
	{
		MessageBox(CGit::GetLibGit2LastErr(_T("Could not open repository.")), _T("TortoiseGit"), MB_ICONERROR);
		return -1;
	}

	if (m_sRevision == _T("HEAD"))
	{
		int ret = git_repository_head_unborn(repository);
		if (ret == 1)	// is orphan
			return ret;
		else if (ret != 0)
		{
			MessageBox(g_Git.GetLibGit2LastErr(_T("Could not check HEAD.")), _T("TortoiseGit"), MB_ICONERROR);
			return ret;
		}
	}

	CGitHash hash;
	if (CGit::GetHash(repository, hash, m_sRevision))
	{
		MessageBox(CGit::GetLibGit2LastErr(_T("Could not get hash of ") + m_sRevision + _T(".")), _T("TortoiseGit"), MB_ICONERROR);
		return -1;
	}

	CAutoCommit commit;
	if (git_commit_lookup(commit.GetPointer(), repository, (git_oid *)hash.m_hash))
	{
		MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup commit.")), _T("TortoiseGit"), MB_ICONERROR);
		return -1;
	}

	CAutoTree tree;
	if (git_commit_tree(tree.GetPointer(), commit))
	{
		MessageBox(CGit::GetLibGit2LastErr(_T("Could not get tree of commit.")), _T("TortoiseGit"), MB_ICONERROR);
		return -1;
	}

	if (!root.IsEmpty())
	{
		CAutoTreeEntry treeEntry;
		if (git_tree_entry_bypath(treeEntry.GetPointer(), tree, CUnicodeUtils::GetUTF8(root)))
		{
			MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR);
			return -1;
		}
		if (git_tree_entry_type(treeEntry) != GIT_OBJ_TREE)
		{
			MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR);
			return -1;
		}

		CAutoObject object;
		if (git_tree_entry_to_object(object.GetPointer(), repository, treeEntry))
		{
			MessageBox(CGit::GetLibGit2LastErr(_T("Could not lookup path.")), _T("TortoiseGit"), MB_ICONERROR);
			return -1;
		}

		tree = (git_tree*)object.Detach();
	}

	treeroot->m_hash = CGitHash((char *)git_tree_id(tree)->id);
	ReadTreeRecursive(*repository, tree, treeroot);

	// try to resolve hash to a branch name
	if (m_sRevision == hash.ToString())
	{
		MAP_HASH_NAME map;
		if (CGit::GetMapHashToFriendName(repository, map))
			MessageBox(g_Git.GetLibGit2LastErr(_T("Could not get all refs.")), _T("TortoiseGit"), MB_ICONERROR);
		if (!map[hash].empty())
			m_sRevision = map[hash].at(0);
	}
	this->GetDlgItem(IDC_BUTTON_REVISION)->SetWindowText(m_sRevision);

	return 0;
}
void CRepositoryBrowser::OpenFile(const CString path, eOpenType mode, bool isSubmodule, CGitHash itemHash)
{
	CTGitPath gitPath(path);

	CString temppath;
	CString file;
	GetTempPath(temppath);
	CGitHash hash;
	if (g_Git.GetHash(hash, m_sRevision))
	{
		MessageBox(g_Git.GetGitLastErr(_T("Could not get hash of ") + m_sRevision + _T(".")), _T("TortoiseGit"), MB_ICONERROR);
		return;
	}

	file.Format(_T("%s%s_%s%s"), (LPCTSTR)temppath, (LPCTSTR)gitPath.GetBaseFilename(), (LPCTSTR)hash.ToString().Left(g_Git.GetShortHASHLength()), (LPCTSTR)gitPath.GetFileExtension());

	if (isSubmodule)
	{
		if (mode == OPEN && !GitAdminDir::IsBareRepo(g_Git.m_CurrentDir))
		{
			CTGitPath subPath = CTGitPath(g_Git.m_CurrentDir);
			subPath.AppendPathString(gitPath.GetWinPathString());
			CAutoRepository repo(subPath.GetGitPathString());
			CAutoCommit commit;
			if (!repo || git_commit_lookup(commit.GetPointer(), repo, (const git_oid *)itemHash.m_hash))
			{
				CString out;
				out.Format(IDS_REPOBROWSEASKSUBMODULEUPDATE, (LPCTSTR)itemHash.ToString(), (LPCTSTR)gitPath.GetGitPathString());
				if (MessageBox(out, _T("TortoiseGit"), MB_YESNO | MB_ICONQUESTION) != IDYES)
					return;

				CString sCmd;
				sCmd.Format(_T("/command:subupdate /bkpath:\"%s\" /selectedpath:\"%s\""), (LPCTSTR)g_Git.m_CurrentDir, (LPCTSTR)gitPath.GetGitPathString());
				CAppUtils::RunTortoiseGitProc(sCmd);
				return;
			}

			CString cmd;
			cmd.Format(_T("/command:repobrowser /path:\"%s\" /rev:%s"), (LPCTSTR)g_Git.CombinePath(path), (LPCTSTR)itemHash.ToString());
			CAppUtils::RunTortoiseGitProc(cmd);
			return;
		}

		file += _T(".txt");
		CFile submoduleCommit(file, CFile::modeCreate | CFile::modeWrite);
		CStringA commitInfo = "Subproject commit " + CStringA(itemHash.ToString());
		submoduleCommit.Write(commitInfo, commitInfo.GetLength());
	}
	else if (g_Git.GetOneFile(m_sRevision, gitPath, file))
	{
		CString out;
		out.Format(IDS_STATUSLIST_CHECKOUTFILEFAILED, (LPCTSTR)gitPath.GetGitPathString(), (LPCTSTR)m_sRevision, (LPCTSTR)file);
		MessageBox(g_Git.GetGitLastErr(out, CGit::GIT_CMD_GETONEFILE), _T("TortoiseGit"), MB_ICONERROR);
		return;
	}

	if (mode == ALTERNATIVEEDITOR)
	{
		CAppUtils::LaunchAlternativeEditor(file);
		return;
	}
	else if (mode == OPEN)
	{
		CAppUtils::ShellOpen(file);
		return;
	}

	CAppUtils::ShowOpenWithDialog(file);
}
void CGitPropertyPage::InitWorkfileView()
{
	if (filenames.empty())
		return;

	CTGitPath path(filenames.front().c_str());

	CString ProjectTopDir;
	if(!path.HasAdminDir(&ProjectTopDir))
		return;

	CAutoRepository repository(CUnicodeUtils::GetUTF8(ProjectTopDir));
	if (!repository)
		return;

	CString username;
	CString useremail;
	CString autocrlf;
	CString safecrlf;

	CAutoConfig config(repository);
	if (config)
	{
		config.GetString(L"user.name", username);
		config.GetString(L"user.email", useremail);
		config.GetString(L"core.autocrlf", autocrlf);
		config.GetString(L"core.safecrlf", safecrlf);
	}

	CString branch;
	CString remotebranch;
	if (!git_repository_head_detached(repository))
	{
		CAutoReference head;
		if (git_repository_head_unborn(repository))
		{
			git_reference_lookup(head.GetPointer(), repository, "HEAD");
			branch = CUnicodeUtils::GetUnicode(git_reference_symbolic_target(head));
			if (branch.Find(_T("refs/heads/")) == 0)
				branch = branch.Mid(11); // 11 = len("refs/heads/")
		}
		else if (!git_repository_head(head.GetPointer(), repository))
		{
			const char * branchChar = git_reference_shorthand(head);
			branch = CUnicodeUtils::GetUnicode(branchChar);

			const char * branchFullChar = git_reference_name(head);
			CAutoBuf upstreambranchname;
			if (!git_branch_upstream_name(upstreambranchname, repository, branchFullChar))
			{
				remotebranch = CUnicodeUtils::GetUnicode(CStringA(upstreambranchname->ptr, (int)upstreambranchname->size));
				remotebranch = remotebranch.Mid(13); // 13=len("refs/remotes/")
			}
		}
	}
	else
		branch = _T("detached HEAD");

	if (autocrlf.Trim().IsEmpty())
		autocrlf = _T("false");
	if (safecrlf.Trim().IsEmpty())
		safecrlf = _T("false");

	SetDlgItemText(m_hwnd,IDC_CONFIG_USERNAME,username.Trim());
	SetDlgItemText(m_hwnd,IDC_CONFIG_USEREMAIL,useremail.Trim());
	SetDlgItemText(m_hwnd,IDC_CONFIG_AUTOCRLF,autocrlf.Trim());
	SetDlgItemText(m_hwnd,IDC_CONFIG_SAFECRLF,safecrlf.Trim());

	SetDlgItemText(m_hwnd,IDC_SHELL_CURRENT_BRANCH,branch.Trim());
	SetDlgItemText(m_hwnd,IDC_SHELL_REMOTE_BRANCH, remotebranch);

	git_oid oid;
	CAutoCommit HEADcommit;
	if (!git_reference_name_to_id(&oid, repository, "HEAD") && !git_commit_lookup(HEADcommit.GetPointer(), repository, &oid) && HEADcommit)
		DisplayCommit(HEADcommit, IDC_HEAD_HASH, IDC_HEAD_SUBJECT, IDC_HEAD_AUTHOR, IDC_HEAD_DATE);

	{
		int stripLength = ProjectTopDir.GetLength();
		if (ProjectTopDir[stripLength - 1] != _T('\\'))
			++stripLength;

		bool allAreFiles = true;
		for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
		{
			if (PathIsDirectory(it->c_str()))
			{
				allAreFiles = false;
				break;
			}
		}
		if (allAreFiles)
		{
			size_t assumevalid = 0;
			size_t skipworktree = 0;
			size_t executable = 0;
			size_t symlink = 0;
			do
			{
				CAutoIndex index;
				if (git_repository_index(index.GetPointer(), repository))
					break;

				for (auto it = filenames.cbegin(); it < filenames.cend(); ++it)
				{
					CTGitPath file;
					file.SetFromWin(CString(it->c_str()).Mid(stripLength));
					CStringA pathA = CUnicodeUtils::GetMulti(file.GetGitPathString(), CP_UTF8);
					size_t idx;
					if (!git_index_find(&idx, index, pathA))
					{
						const git_index_entry *e = git_index_get_byindex(index, idx);

						if (e->flags & GIT_IDXENTRY_VALID)
							++assumevalid;

						if (e->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE)
							++skipworktree;

						if (e->mode & 0111)
							++executable;

						if ((e->mode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
							++symlink;
					}
					else
					{
						// do not show checkboxes for unversioned files
						ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
						ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
						ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
						ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
						break;
					}
				}
			} while (0);

			if (assumevalid != 0 && assumevalid != filenames.size())
			{
				SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
				SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, BST_INDETERMINATE, 0);
			}
			else
				SendMessage(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), BM_SETCHECK, (assumevalid == 0) ? BST_UNCHECKED : BST_CHECKED, 0);

			if (skipworktree != 0 && skipworktree != filenames.size())
			{
				SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
				SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, BST_INDETERMINATE, 0);
			}
			else
				SendMessage(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), BM_SETCHECK, (skipworktree == 0) ? BST_UNCHECKED : BST_CHECKED, 0);

			if (executable != 0 && executable != filenames.size())
			{
				SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_EXECUTABLE), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
				SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, BST_INDETERMINATE, 0);
				EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), TRUE);
			}
			else
			{
				SendMessage(GetDlgItem(m_hwnd, IDC_EXECUTABLE), BM_SETCHECK, (executable == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
				EnableWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), (executable == 0) ? TRUE : FALSE);
			}

			if (symlink != 0 && symlink != filenames.size())
			{
				SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETSTYLE, (DWORD)GetWindowLong(GetDlgItem(m_hwnd, IDC_SYMLINK), GWL_STYLE) & ~BS_AUTOCHECKBOX | BS_AUTO3STATE, 0);
				SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, BST_INDETERMINATE, 0);
				EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), TRUE);
			}
			else
			{
				SendMessage(GetDlgItem(m_hwnd, IDC_SYMLINK), BM_SETCHECK, (symlink == 0) ? BST_UNCHECKED : BST_CHECKED, 0);
				EnableWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), (symlink == 0) ? TRUE : FALSE);
			}
		}
		else
		{
			ShowWindow(GetDlgItem(m_hwnd, IDC_ASSUMEVALID), SW_HIDE);
			ShowWindow(GetDlgItem(m_hwnd, IDC_SKIPWORKTREE), SW_HIDE);
			ShowWindow(GetDlgItem(m_hwnd, IDC_EXECUTABLE), SW_HIDE);
			ShowWindow(GetDlgItem(m_hwnd, IDC_SYMLINK), SW_HIDE);
		}
	}

	if (filenames.size() == 1 && !PathIsDirectory(filenames[0].c_str()))
	{
		SetDlgItemText(m_hwnd, IDC_LAST_SUBJECT, CString(MAKEINTRESOURCE(IDS_LOADING)));
		_beginthread(LogThreadEntry, 0, this);
	}
	else
		ShowWindow(GetDlgItem(m_hwnd, IDC_STATIC_LASTMODIFIED), SW_HIDE);
}
static git_commit* FindFileRecentCommit(git_repository* repository, const CString& path)
{
	CAutoRevwalk walk;
	if (git_revwalk_new(walk.GetPointer(), repository))
		return nullptr;

	CStringA pathA = CUnicodeUtils::GetUTF8(path);
	if (pathA.GetLength() >= MAX_PATH)
		return nullptr;
	const char *pathC = pathA;
	char folder[MAX_PATH] = {0}, file[MAX_PATH] = {0};
	const char *slash = strrchr(pathC, '/');
	if (slash)
	{
		strncpy(folder, pathC, slash - pathC + 1);
		folder[slash - pathC + 1] = '\0';
		strcpy(file, slash + 1);
	}
	else
	{
		folder[0] = '\0';
		strcpy(file, pathC);
	}

	TreewalkStruct treewalkstruct = { folder, file };

	if (git_revwalk_push_head(walk))
		return nullptr;

	git_oid oid;
	CAutoCommit commit;
	while (!git_revwalk_next(&oid, walk))
	{
		if (git_commit_lookup(commit.GetPointer(), repository, &oid))
			return nullptr;

		CAutoTree tree;
		if (git_commit_tree(tree.GetPointer(), commit))
			return nullptr;

		memset(&treewalkstruct.oid.id, 0, sizeof(treewalkstruct.oid.id));
		int ret = git_tree_walk(tree, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct);

		if (ret < 0 && ret != GIT_EUSER)
			return nullptr;

		// check if file not found
		if (git_oid_iszero(&treewalkstruct.oid))
			return nullptr;

		bool diff = true;
		// for merge point, check if it is different to all parents, if yes then there are real change in the merge point.
		// if no parent then of course it is different
		for (unsigned int i = 0; i < git_commit_parentcount(commit); ++i)
		{
			CAutoCommit commit2;
			if (git_commit_parent(commit2.GetPointer(), commit, i))
				return nullptr;

			CAutoTree tree2;
			if (git_commit_tree(tree2.GetPointer(), commit2))
				return nullptr;

			TreewalkStruct treewalkstruct2 = { folder, file };
			memset(&treewalkstruct2.oid.id, 0, sizeof(treewalkstruct2.oid.id));
			int ret = git_tree_walk(tree2, GIT_TREEWALK_PRE, TreewalkCB_FindFileRecentCommit, &treewalkstruct2);

			if (ret < 0 && ret != GIT_EUSER)
				return nullptr;

			if (!git_oid_cmp(&treewalkstruct.oid, &treewalkstruct2.oid))
				diff = false;
			else if (git_revwalk_hide(walk, git_commit_parent_id(commit, i)))
				return nullptr;
		}

		if (diff)
			break;
	}

	return commit.Detach();
}