Skip to content

Commit f4e5de7

Browse files
Improve branching mode (#11)
1 parent a9a844a commit f4e5de7

File tree

5 files changed

+100
-49
lines changed

5 files changed

+100
-49
lines changed

p4-fusion/branch_set.cc

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,15 @@ Branch::Branch(const std::string& branch, const std::string& alias)
5050
}
5151
}
5252

53-
std::array<std::string, 2> Branch::SplitBranchPath(const std::string& relativeDepotPath) const
53+
bool Branch::IsInBranch(const std::string& relativeDepotPath) const
5454
{
55-
if (
55+
return
5656
// The relative depot branch, to match this branch path, must start with the
5757
// branch path + "/". The "StartsWith" is put at the end of the 'and' checks,
5858
// because it takes the longest.
5959
relativeDepotPath.size() > depotBranchPath.size()
6060
&& relativeDepotPath[depotBranchPath.size()] == '/'
61-
&& STDHelpers::StartsWith(relativeDepotPath, depotBranchPath))
62-
{
63-
return { gitAlias, relativeDepotPath.substr(depotBranchPath.size() + 1) };
64-
}
65-
return { "", "" };
61+
&& STDHelpers::StartsWith(relativeDepotPath, depotBranchPath);
6662
}
6763

6864
Branch createBranchFromPath(const std::string& depotBranchPath)
@@ -140,23 +136,22 @@ BranchSet::BranchSet(GitAPI& gitAPI,
140136
}
141137
}
142138

143-
std::array<std::string, 2> BranchSet::splitBranchPath(const std::string& relativeDepotPath) const
139+
const Branch* BranchSet::getBranchFor(const std::string& relativeDepotPath) const
144140
{
145141
// Check if the relative depot path starts with any of the branches.
146142
// This checks the branches in their stored order, which can mean that having a branch
147143
// order like "//a/b/c" and "//a/b" will only work if the sub-branches are listed first.
148144
// To do this properly, the stored branches should be scanned based on their length - longest
149145
// first, but that's extra processing and code for a use case that is rare and has a manual
150146
// work around (list branches in a specific order).
151-
for (auto& branch : m_branches)
147+
for (const Branch& branch : m_branches)
152148
{
153-
auto split = branch.SplitBranchPath(relativeDepotPath);
154-
if (!split[0].empty() && !split[1].empty())
149+
if (branch.IsInBranch(relativeDepotPath))
155150
{
156-
return split;
151+
return &branch;
157152
}
158153
}
159-
return { "", "" };
154+
return nullptr;
160155
}
161156

162157
bool BranchSet::matchesExcludes(const std::string& depotPath) const
@@ -187,19 +182,19 @@ struct branchIntegrationMap
187182
std::unordered_map<std::string, int> branchIndicies;
188183
int fileCount = 0;
189184

190-
void addMerge(const std::string& sourceBranch, const std::string& targetBranch, const FileData& rev);
191-
void addTarget(const std::string& targetBranch, const FileData& rev);
185+
void addMerge(const std::string& sourceBranch, const std::string& targetBranch, const std::string& depotBranchPath, const FileData& rev);
186+
void addTarget(const std::string& targetBranch, const std::string& depotBranchPath, const FileData& rev);
192187

193188
// note: not const, because it cleans out the branchGroups.
194189
std::unique_ptr<ChangedFileGroups> createChangedFileGroups() { return std::unique_ptr<ChangedFileGroups>(new ChangedFileGroups(branchGroups, fileCount)); };
195190
};
196191

197-
void branchIntegrationMap::addTarget(const std::string& targetBranch, const FileData& fileData)
192+
void branchIntegrationMap::addTarget(const std::string& targetBranch, const std::string& depotBranchPath, const FileData& fileData)
198193
{
199-
addMerge(EMPTY_STRING, targetBranch, fileData);
194+
addMerge(EMPTY_STRING, targetBranch, depotBranchPath, fileData);
200195
}
201196

202-
void branchIntegrationMap::addMerge(const std::string& sourceBranch, const std::string& targetBranch, const FileData& fileData)
197+
void branchIntegrationMap::addMerge(const std::string& sourceBranch, const std::string& targetBranch, const std::string& depotBranchPath, const FileData& fileData)
203198
{
204199
// Need to store this in the integration map, using "src/tgt" as the
205200
// key. Because stream names can't have a '/' in them, this creates a unique key.
@@ -214,6 +209,7 @@ void branchIntegrationMap::addMerge(const std::string& sourceBranch, const std::
214209
BranchedFileGroup& bfg = branchGroups[index];
215210
bfg.sourceBranch = sourceBranch;
216211
bfg.targetBranch = targetBranch;
212+
bfg.depotBranchPath = depotBranchPath;
217213
bfg.hasSource = !sourceBranch.empty();
218214
bfg.files.push_back(fileData);
219215
}
@@ -327,50 +323,46 @@ std::unique_ptr<ChangedFileGroups> BranchSet::ParseAffectedFiles(const std::vect
327323
if (HasMergeableBranch())
328324
{
329325
// [0] == branch name, [1] == relative path in the branch.
330-
std::array<std::string, 2> branchPath = splitBranchPath(relativeDepotPath);
331-
if (
332-
branchPath[0].empty()
333-
|| branchPath[1].empty())
326+
const Branch* branch = getBranchFor(relativeDepotPath);
327+
if (branch == nullptr)
334328
{
335329
// not a valid branch file. skip it.
336330
continue;
337331
}
338332

339333
// It's a valid destination to a branch.
340334
// Make sure the relative path is set.
341-
fileData.SetRelativeDepotPath(branchPath[1]);
335+
const std::string branchFilePath = relativeDepotPath.substr(branch->depotBranchPath.size() + 1);
336+
fileData.SetRelativeDepotPath(branchFilePath);
342337

343338
bool needsHandling = true;
344339
if (fileData.IsIntegrated())
345340
{
346341
// Only add the integration if the source is from a branch we care about.
347342
// [0] == branch name, [1] == relative path in the branch.
348-
std::array<std::string, 2> fromBranchPath = splitBranchPath(stripBasePath(fileData.GetFromDepotFile()));
349-
if (
350-
!fromBranchPath[0].empty()
351-
&& !fromBranchPath[1].empty()
352-
343+
const Branch* fromBranch = getBranchFor(stripBasePath(fileData.GetFromDepotFile()));
344+
if (fromBranch != nullptr
353345
// Can't have source and target be pointing to the same branch; that's not
354346
// a branch operation in the Git sense.
355-
&& fromBranchPath[0] != branchPath[0])
347+
&& fromBranch->gitAlias != branch->gitAlias)
356348
{
357349
// This is a valid integrate from a known source to a known target branch.
358-
branchMap.addMerge(fromBranchPath[0], branchPath[0], fileData);
350+
branchMap.addMerge(fromBranch->gitAlias, branch->gitAlias, branch->depotBranchPath, fileData);
359351
needsHandling = false;
360352
}
361353
}
362354
if (needsHandling)
363355
{
364356
// Either not a valid integrate, or a normal operation.
365-
branchMap.addTarget(branchPath[0], fileData);
357+
branchMap.addTarget(branch->gitAlias, branch->depotBranchPath, fileData);
366358
}
367359
}
368360
else
369361
{
370362
// It's a non-branching setup.
371363
// Make sure the relative path is set.
372364
fileData.SetRelativeDepotPath(relativeDepotPath);
373-
branchMap.addTarget(EMPTY_STRING, fileData);
365+
branchMap.addTarget(EMPTY_STRING, EMPTY_STRING, fileData);
374366
}
375367
}
376368
return branchMap.createChangedFileGroups();

p4-fusion/branch_set.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ struct BranchedFileGroup
2828
// These branch names will be the Git branch names.
2929
std::string sourceBranch;
3030
std::string targetBranch;
31+
std::string depotBranchPath;
3132
bool hasSource;
3233
std::vector<FileData> files;
3334
};
@@ -58,9 +59,7 @@ struct Branch
5859

5960
Branch(const std::string& branch, const std::string& alias);
6061

61-
// splitBranchPath If the relativeDepotPath matches, returns {branch alias, branch file path}.
62-
// Otherwise, returns {"", ""}
63-
std::array<std::string, 2> SplitBranchPath(const std::string& relativeDepotPath) const;
62+
bool IsInBranch(const std::string& relativeDepotPath) const;
6463
};
6564

6665
// A singular view on the branches and a base view (acts as a filter to trim down affected files).
@@ -84,9 +83,7 @@ struct BranchSet
8483
// stripBasePath remove the base path from the depot path, or "" if not in the base path.
8584
std::string stripBasePath(const std::string& depotPath) const;
8685

87-
// splitBranchPath extract the branch name and path under the branch (no leading '/' on the path)
88-
// relativeDepotPath - already stripped from running stripBasePath.
89-
std::array<std::string, 2> splitBranchPath(const std::string& relativeDepotPath) const;
86+
const Branch* getBranchFor(const std::string& relativeDepotPath) const;
9087

9188
bool matchesExcludes(const std::string& depotPath) const;
9289

@@ -107,6 +104,8 @@ struct BranchSet
107104

108105
int Count() const { return m_branches.size(); };
109106

107+
const std::vector<Branch>& GetBranches() const { return m_branches; };
108+
110109
// ParseAffectedFiles create collections of merges and commits.
111110
// Breaks up the files into those that are within the view, with each item in the
112111
// list is its own target Git branch.

p4-fusion/git_api.cc

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ GitAPI::~GitAPI()
6262
git_libgit2_shutdown();
6363
}
6464

65-
bool GitAPI::IsRepositoryClonedFrom(const std::string& depotPath)
65+
std::string GitAPI::GetDepotPathFromLastCommit() const
6666
{
6767
git_oid oid;
6868
GIT2(git_reference_name_to_id(&oid, m_Repo, "HEAD"));
@@ -73,11 +73,11 @@ bool GitAPI::IsRepositoryClonedFrom(const std::string& depotPath)
7373
std::string message = git_commit_message(headCommit);
7474
size_t depotPathStart = message.find("depot-paths = \"") + 15;
7575
size_t depotPathEnd = message.find("\": change") - 1;
76-
std::string repoDepotPath = message.substr(depotPathStart, depotPathEnd - depotPathStart + 1) + "...";
76+
std::string repoDepotPath = message.substr(depotPathStart, depotPathEnd - depotPathStart + 1);
7777

7878
git_commit_free(headCommit);
7979

80-
return repoDepotPath == depotPath;
80+
return repoDepotPath;
8181
}
8282

8383
void GitAPI::OpenRepository(const std::string& repoPath)
@@ -363,8 +363,7 @@ std::string GitAPI::Commit(
363363
git_signature* author = nullptr;
364364
GIT2(git_signature_new(&author, user.c_str(), email.c_str(), timestamp, timezone));
365365

366-
// -3 to remove the trailing "..."
367-
std::string commitMsg = desc + "\n[p4-fusion: depot-paths = \"" + depotPath.substr(0, depotPath.size() - 3) + "\": change = " + cl + "]";
366+
std::string commitMsg = desc + "\n[p4-fusion: depot-paths = \"" + depotPath + "\": change = " + cl + "]";
368367

369368
// Find the parent commits.
370369
// Order is very important.

p4-fusion/git_api.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class GitAPI
3636
void OpenRepository(const std::string& repoPath);
3737

3838
bool IsHEADExists();
39-
bool IsRepositoryClonedFrom(const std::string& depotPath);
39+
std::string GetDepotPathFromLastCommit() const;
4040
std::string DetectLatestCL();
4141

4242
git_oid CreateBlob(const std::vector<char>& data);

p4-fusion/main.cc

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -350,10 +350,32 @@ int Main(int argc, char** argv)
350350
std::string resumeFromCL;
351351
if (git.IsHEADExists())
352352
{
353-
if (!git.IsRepositoryClonedFrom(depotPath))
353+
if (branchSet.HasMergeableBranch())
354354
{
355-
ERR("Git repository at " << srcPath << " was not initially cloned with depotPath = " << depotPath << ". Exiting.");
356-
return 1;
355+
bool foundMatchingBranch = false;
356+
for (const Branch& branch : branchSet.GetBranches())
357+
{
358+
const std::string branchDepotPath = depotPath.substr(0, depotPath.size() - 3) + branch.depotBranchPath;
359+
360+
if (git.GetDepotPathFromLastCommit() == branchDepotPath)
361+
{
362+
foundMatchingBranch = true;
363+
break;
364+
}
365+
}
366+
if (!foundMatchingBranch)
367+
{
368+
ERR("Git repository at " << srcPath << " was not initially cloned with any provided branches. Exiting.");
369+
return 1;
370+
}
371+
}
372+
else
373+
{
374+
if (git.GetDepotPathFromLastCommit() + "..." != depotPath)
375+
{
376+
ERR("Git repository at " << srcPath << " was not initially cloned with depotPath = \"" << depotPath << "\". Exiting.");
377+
return 1;
378+
}
357379
}
358380

359381
resumeFromCL = git.DetectLatestCL();
@@ -362,7 +384,25 @@ int Main(int argc, char** argv)
362384

363385
PRINT("Requesting changelists to convert from the Perforce server");
364386

365-
std::vector<ChangeList> changes = std::move(p4.Changes(depotPath, resumeFromCL, maxChanges).GetChanges());
387+
std::vector<ChangeList> changes;
388+
if (branchSet.HasMergeableBranch())
389+
{
390+
for (const Branch& branch : branchSet.GetBranches())
391+
{
392+
const std::string branchDepotPath = depotPath.substr(0, depotPath.size() - 3) + branch.depotBranchPath + "/...";
393+
394+
std::vector<ChangeList> branchChanges = std::move(p4.Changes(branchDepotPath, resumeFromCL, maxChanges).GetChanges());
395+
for (ChangeList& cl : branchChanges)
396+
{
397+
changes.push_back(std::move(cl));
398+
}
399+
}
400+
std::sort(changes.begin(), changes.end());
401+
}
402+
else
403+
{
404+
changes = std::move(p4.Changes(depotPath, resumeFromCL, maxChanges).GetChanges());
405+
}
366406

367407
if (streamMappings)
368408
{
@@ -382,6 +422,17 @@ int Main(int argc, char** argv)
382422
}
383423
}
384424

425+
// Check if all changelists appear only once
426+
std::set<std::string> uniqueChangeListNumbers;
427+
for (const ChangeList& cl : changes) {
428+
uniqueChangeListNumbers.insert(cl.number);
429+
}
430+
if (uniqueChangeListNumbers.size() != changes.size())
431+
{
432+
ERR("Changelists appear more than once. Exiting.");
433+
return 1;
434+
}
435+
385436
// Return early if we have no work to do
386437
if (changes.empty())
387438
{
@@ -494,7 +545,17 @@ int Main(int argc, char** argv)
494545
mergeFrom = branchGroup.sourceBranch;
495546
}
496547

497-
std::string commitSHA = git.Commit(depotPath,
548+
std::string depotPathString = branchSet.HasMergeableBranch() ? branchGroup.depotBranchPath : depotPath;
549+
if (STDHelpers::EndsWith(depotPathString, "/..."))
550+
{
551+
depotPathString = depotPathString.substr(0, depotPathString.size() - 3);
552+
}
553+
if (!STDHelpers::StartsWith(depotPathString, "//"))
554+
{
555+
depotPathString = "//" + depotPathString;
556+
}
557+
558+
std::string commitSHA = git.Commit(depotPathString,
498559
cl.number,
499560
fullName,
500561
email,

0 commit comments

Comments
 (0)